From 43e82c9a811ce485ee8597dfe3fa632e3f9ae1ff Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Thu, 27 Mar 2025 23:11:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=92=8C=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增订单管理模块,包括总订单和日订单的展示 - 优化样式,添加时间线和订单状态标签 - 实现图片上传功能,支持手套、鞋套、宠物照片等 - 添加打卡功能,支持前后对比照片上传 - 完善订单详情、宠物档案和服务档案的跳转逻辑 --- api/order/order.js | 17 + otherPages/myOrdersManage/clock/index.vue | 222 +++++++++-- otherPages/myOrdersManage/detail/index.vue | 2 +- otherPages/myOrdersManage/list/index.vue | 2 +- otherPages/myOrdersManage/service/index.vue | 17 +- otherPages/myOrdersManage/transaction/index.vue | 2 +- otherPages/myOrdersManage/withdrawal/index.vue | 2 +- otherPages/orderTakingManage/pet/index.vue | 13 +- .../myOrdersManage/components/orderListByData.vue | 415 +++++++++++++++++++++ pages/myOrdersManage/components/systemOrder.vue | 144 +++---- pages/myOrdersManage/index.vue | 168 ++++++--- utils/oss-upload/common/crypto/base64.js | 95 +++++ utils/oss-upload/common/crypto/crypto.js.js | 117 ++++++ utils/oss-upload/common/crypto/hmac.js | 29 ++ utils/oss-upload/common/crypto/sha1.js | 59 +++ utils/oss-upload/oss/OSSConfig.js | 51 +++ utils/oss-upload/oss/index.js | 132 +++++++ 17 files changed, 1317 insertions(+), 170 deletions(-) create mode 100644 api/order/order.js create mode 100644 pages/myOrdersManage/components/orderListByData.vue create mode 100644 utils/oss-upload/common/crypto/base64.js create mode 100644 utils/oss-upload/common/crypto/crypto.js.js create mode 100644 utils/oss-upload/common/crypto/hmac.js create mode 100644 utils/oss-upload/common/crypto/sha1.js create mode 100644 utils/oss-upload/oss/OSSConfig.js create mode 100644 utils/oss-upload/oss/index.js diff --git a/api/order/order.js b/api/order/order.js new file mode 100644 index 0000000..b9070e8 --- /dev/null +++ b/api/order/order.js @@ -0,0 +1,17 @@ +import request from '@/utils/request' + + +// 获取订单列表 +export function getAppOrderList() { + return request({ + headers: { + "isToken": true + }, + url: "/applet/order/getAppOrderList", + method: 'get' + }) +} + +export default { + getAppOrderList +} \ No newline at end of file diff --git a/otherPages/myOrdersManage/clock/index.vue b/otherPages/myOrdersManage/clock/index.vue index 4bf104c..ec47d9a 100644 --- a/otherPages/myOrdersManage/clock/index.vue +++ b/otherPages/myOrdersManage/clock/index.vue @@ -11,8 +11,16 @@ - - + + + + + + + + + @@ -21,8 +29,13 @@ - - + + + + + + @@ -30,24 +43,22 @@ 宠物状态记录 - - 小汪照片 - (至少2张) - - - - - + + + {{ pet.title }}照片 + (至少2张) - - - Billion照片 - (至少2张) - - - - - + + + + + + + + + @@ -58,39 +69,69 @@ 粮碗前后对比 - - + + + + + + - - + + + + + + 水碗前后对比 - - + + + + + + - - + + + + + + 猫砂盆、尿垫前后对比 - - + + + + + + - - + + + + + + @@ -105,8 +146,13 @@ - - + + + + + + @@ -115,8 +161,13 @@ - - + + + + + + @@ -133,7 +184,7 @@ class="pd20 box-size"> - + 确定提交 @@ -141,7 +192,100 @@ - \ No newline at end of file diff --git a/pages/myOrdersManage/components/systemOrder.vue b/pages/myOrdersManage/components/systemOrder.vue index 12dda76..e224933 100644 --- a/pages/myOrdersManage/components/systemOrder.vue +++ b/pages/myOrdersManage/components/systemOrder.vue @@ -1,73 +1,91 @@ \ No newline at end of file diff --git a/pages/myOrdersManage/index.vue b/pages/myOrdersManage/index.vue index 4bb46bd..a6fb991 100644 --- a/pages/myOrdersManage/index.vue +++ b/pages/myOrdersManage/index.vue @@ -1,67 +1,119 @@ + @import "index.scss"; + \ No newline at end of file diff --git a/utils/oss-upload/common/crypto/base64.js b/utils/oss-upload/common/crypto/base64.js new file mode 100644 index 0000000..f31bb3a --- /dev/null +++ b/utils/oss-upload/common/crypto/base64.js @@ -0,0 +1,95 @@ +export const Base64 = { + _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + encode: function(input) { + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + input = Base64._utf8_encode(input); + while (i < input.length) { + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + } + return output; + }, + + decode: function(input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + while (i < input.length) { + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + output = output + String.fromCharCode(chr1); + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + } + output = Base64._utf8_decode(output); + return output; + }, + + _utf8_encode: function(string) { + string = string.replace(/\r\n/g, "\n"); + var utftext = ""; + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } else if ((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + } + return utftext; + }, + + _utf8_decode: function(utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + while (i < utftext.length) { + c = utftext.charCodeAt(i); + if (c < 128) { + string += String.fromCharCode(c); + i++; + } else if ((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i + 1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = utftext.charCodeAt(i + 1); + c3 = utftext.charCodeAt(i + 2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + return string; + } +} \ No newline at end of file diff --git a/utils/oss-upload/common/crypto/crypto.js.js b/utils/oss-upload/common/crypto/crypto.js.js new file mode 100644 index 0000000..8ee30dc --- /dev/null +++ b/utils/oss-upload/common/crypto/crypto.js.js @@ -0,0 +1,117 @@ +var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +let Crypto = {}; +var util = Crypto.util = { + rotl: function(n, b) { + return (n << b) | (n >>> (32 - b)); + }, + rotr: function(n, b) { + return (n << (32 - b)) | (n >>> b); + }, + endian: function(n) { + if (n.constructor == Number) { + return util.rotl(n, 8) & 0x00FF00FF | + util.rotl(n, 24) & 0xFF00FF00; + } + for (var i = 0; i < n.length; i++) + n[i] = util.endian(n[i]); + return n; + }, + randomBytes: function(n) { + for (var bytes = []; n > 0; n--) + bytes.push(Math.floor(Math.random() * 256)); + return bytes; + }, + stringToBytes: function(str) { + var bytes = []; + for (var i = 0; i < str.length; i++) + bytes.push(str.charCodeAt(i)); + return bytes; + }, + bytesToString: function(bytes) { + var str = []; + for (var i = 0; i < bytes.length; i++) + str.push(String.fromCharCode(bytes[i])); + return str.join(""); + }, + stringToWords: function(str) { + var words = []; + for (var c = 0, b = 0; c < str.length; c++, b += 8) + words[b >>> 5] |= str.charCodeAt(c) << (24 - b % 32); + return words; + }, + bytesToWords: function(bytes) { + var words = []; + for (var i = 0, b = 0; i < bytes.length; i++, b += 8) + words[b >>> 5] |= bytes[i] << (24 - b % 32); + return words; + }, + wordsToBytes: function(words) { + var bytes = []; + for (var b = 0; b < words.length * 32; b += 8) + bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); + return bytes; + }, + bytesToHex: function(bytes) { + var hex = []; + for (var i = 0; i < bytes.length; i++) { + hex.push((bytes[i] >>> 4).toString(16)); + hex.push((bytes[i] & 0xF).toString(16)); + } + return hex.join(""); + }, + hexToBytes: function(hex) { + var bytes = []; + for (var c = 0; c < hex.length; c += 2) + bytes.push(parseInt(hex.substr(c, 2), 16)); + return bytes; + }, + bytesToBase64: function(bytes) { + if (typeof btoa == "function") return btoa(util.bytesToString(bytes)); + var base64 = [], + overflow; + for (var i = 0; i < bytes.length; i++) { + switch (i % 3) { + case 0: + base64.push(base64map.charAt(bytes[i] >>> 2)); + overflow = (bytes[i] & 0x3) << 4; + break; + case 1: + base64.push(base64map.charAt(overflow | (bytes[i] >>> 4))); + overflow = (bytes[i] & 0xF) << 2; + break; + case 2: + base64.push(base64map.charAt(overflow | (bytes[i] >>> 6))); + base64.push(base64map.charAt(bytes[i] & 0x3F)); + overflow = -1; + } + } + if (overflow != undefined && overflow != -1) + base64.push(base64map.charAt(overflow)); + while (base64.length % 4 != 0) base64.push("="); + return base64.join(""); + }, + base64ToBytes: function(base64) { + if (typeof atob == "function") return util.stringToBytes(atob(base64)); + base64 = base64.replace(/[^A-Z0-9+\/]/ig, ""); + var bytes = []; + for (var i = 0; i < base64.length; i++) { + switch (i % 4) { + case 1: + bytes.push((base64map.indexOf(base64.charAt(i - 1)) << 2) | + (base64map.indexOf(base64.charAt(i)) >>> 4)); + break; + case 2: + bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0xF) << 4) | + (base64map.indexOf(base64.charAt(i)) >>> 2)); + break; + case 3: + bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0x3) << 6) | + (base64map.indexOf(base64.charAt(i)))); + break; + } + } + return bytes; + } +}; +Crypto.mode = {}; +export default Crypto; \ No newline at end of file diff --git a/utils/oss-upload/common/crypto/hmac.js b/utils/oss-upload/common/crypto/hmac.js new file mode 100644 index 0000000..457bec3 --- /dev/null +++ b/utils/oss-upload/common/crypto/hmac.js @@ -0,0 +1,29 @@ +import Crypto from '@/utils/oss-upload/common/crypto/crypto.js.js'; +(function() { + // Shortcut + var util = Crypto.util; + Crypto.HMAC = function(hasher, message, key, options) { + // Allow arbitrary length keys + key = key.length > hasher._blocksize * 4 ? + hasher(key, { + asBytes: true + }) : + util.stringToBytes(key); + // XOR keys with pad constants + var okey = key, + ikey = key.slice(0); + for (var i = 0; i < hasher._blocksize * 4; i++) { + okey[i] ^= 0x5C; + ikey[i] ^= 0x36; + } + var hmacbytes = hasher(util.bytesToString(okey) + + hasher(util.bytesToString(ikey) + message, { + asString: true + }), { + asBytes: true + }); + return options && options.asBytes ? hmacbytes : + options && options.asString ? util.bytesToString(hmacbytes) : + util.bytesToHex(hmacbytes); + }; +})(); \ No newline at end of file diff --git a/utils/oss-upload/common/crypto/sha1.js b/utils/oss-upload/common/crypto/sha1.js new file mode 100644 index 0000000..f03675c --- /dev/null +++ b/utils/oss-upload/common/crypto/sha1.js @@ -0,0 +1,59 @@ +import Crypto from '@/utils/oss-upload/common/crypto/crypto.js.js'; +(function() { + // Shortcut + var util = Crypto.util; + // Public API + var SHA1 = Crypto.SHA1 = function(message, options) { + var digestbytes = util.wordsToBytes(SHA1._sha1(message)); + return options && options.asBytes ? digestbytes : + options && options.asString ? util.bytesToString(digestbytes) : + util.bytesToHex(digestbytes); + }; + // The core + SHA1._sha1 = function(message) { + var m = util.stringToWords(message), + l = message.length * 8, + w = [], + H0 = 1732584193, + H1 = -271733879, + H2 = -1732584194, + H3 = 271733878, + H4 = -1009589776; + // Padding + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >>> 9) << 4) + 15] = l; + for (var i = 0; i < m.length; i += 16) { + var a = H0, + b = H1, + c = H2, + d = H3, + e = H4; + for (var j = 0; j < 80; j++) { + if (j < 16) w[j] = m[i + j]; + else { + var n = w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16]; + w[j] = (n << 1) | (n >>> 31); + } + var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + ( + j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 : + j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 : + j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 : + (H1 ^ H2 ^ H3) - 899497514); + H4 = H3; + H3 = H2; + H2 = (H1 << 30) | (H1 >>> 2); + H1 = H0; + H0 = t; + } + H0 += a; + H1 += b; + H2 += c; + H3 += d; + H4 += e; + } + return [H0, H1, H2, H3, H4]; + }; + + // Package private blocksize + SHA1._blocksize = 16; +})(); \ No newline at end of file diff --git a/utils/oss-upload/oss/OSSConfig.js b/utils/oss-upload/oss/OSSConfig.js new file mode 100644 index 0000000..f3f7dfe --- /dev/null +++ b/utils/oss-upload/oss/OSSConfig.js @@ -0,0 +1,51 @@ +import Crypto from '@/utils/oss-upload/common/crypto/crypto.js.js'; +import '@/utils/oss-upload/common/crypto/hmac.js'; +import '@/utils/oss-upload/common/crypto/sha1.js'; +import { Base64 } from '@/utils/oss-upload/common/crypto/base64.js'; + +// 阿里云配置 +const ossConfig = { + url: 'https://image.hhlm1688.com/', + config: { + //桶的地址 + region: 'oss-cn-guangzhou', + //id + accessKeyId: 'LTAI5tQSs47izVy8DLVdwUU9', + //密钥 + accessKeySecret: 'qHI7C3PaXYZySr84HTToviC71AYlFq', + //桶的名字 + bucket: 'hanhaiimage', + endpoint: 'oss-cn-shenzhen.aliyuncs.com', + } +} + +let date = new Date() +date = date.setHours(date.getHours() + 1) +let extime = "" + new Date(date).toISOString() +let policyText = { + "expiration": extime, + "conditions": [ + ["content-length-range", 0, 1024 * 1024 * 100] // 设置上传文件的大小限制 + ] +}; +let config = { + accessid: ossConfig.config.accessKeyId, + accesskey: ossConfig.config.accessKeySecret, + osshost: ossConfig.url, + policyBase64: Base64.encode(JSON.stringify(policyText)) +} + +let message = config.policyBase64; +let bytes = Crypto.HMAC(Crypto.SHA1, message, config.accesskey, { + asBytes: true +}); +let signature = Crypto.util.bytesToBase64(bytes); +let timetamp = new Date().getTime(); +let OSSConfig = { + name: 'aliyun', + host: config.osshost, + accessid: config.accessid, + signature: signature, + policyBase64: config.policyBase64, +} +export default OSSConfig; \ No newline at end of file diff --git a/utils/oss-upload/oss/index.js b/utils/oss-upload/oss/index.js new file mode 100644 index 0000000..cf449dc --- /dev/null +++ b/utils/oss-upload/oss/index.js @@ -0,0 +1,132 @@ +/** + * 阿里云OSS工具类 + */ +import OSSConfig from "@/utils/oss-upload/oss/OSSConfig.js" + + +/** + * 生成一个随机的Key + */ +function storeKey() { + let s = []; + let hexDigits = "0123456789abcdef"; + for (let i = 0; i < 36; i++) { + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); + } + s[14] = "4"; + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); + s[8] = s[13] = s[18] = s[23] = "-"; + return s.join(""); +} + +/** + * 根据当天日期在OSS端生成文件夹 + */ +function storeFolder() { + const date = new Date(); + const formatNumber = n => { + n = n.toString() + return n[1] ? n : '0' + n + } + return [date.getFullYear(), date.getMonth() + 1, date.getDate()].map(formatNumber).join('-') +} + +/** + * 阿里云OSS上传文件, 所有具体功能的工具函数均基于此 + * 注意, resolve时一定为上传成功, 返回OSS上的Key + * @param filePath 待上传文件的URI + * @param key 存储桶中的目标文件名 + * @param folder 存储桶中的目标文件夹 + */ +export function ossUpload(filePath, key = storeKey(), folder = storeFolder()) { + return new Promise((resolve, reject) => { + if (folder && folder?.length > 0) { + if (folder[0] == "/") folder = folder.slice(1, folder.length) + if (folder[folder.length - 1] != "/") folder += "/" + key = folder + key + } + const filePrefixArr = filePath.split(".") + key += `.${filePrefixArr[filePrefixArr.length - 1]}` + let config = { + url: OSSConfig.host, + name: 'file', + filePath, + formData: { + key, + policy: OSSConfig.policyBase64, + OSSAccessKeyId: OSSConfig.accessid, + success_action_status: '200', + signature: OSSConfig.signature, + }, + success(res) { + if (res.errMsg.includes("uploadFile:ok")) { + resolve(OSSConfig.host + key) + } else { + reject(res) + } + }, + fail(err) { + reject(err) + } + } + uni.uploadFile(config) + }) +} + +/** + * 阿里云OSS上传图片 + * @param {compressed, key, folder, success, fail} compressed: 是否压缩 key: 存储桶中的目标文件名 folder: 存储桶中的目标文件夹 + */ +export function ossUploadImage({ + key, + folder, + compressed = true, //是否压缩 +}) { + const sizeType = [compressed ? 'compressed' : 'original'] + + return new Promise((success, fail) => { + uni.chooseImage({ + count: 1, + sizeType, + success(res) { + ossUpload(res.tempFilePaths[0], key, folder).then(success).catch(fail) + }, + fail + }) + }) +} + +/** + * 阿里云OSS上传视频 + * @param { key, folder, sourceType, compressed, maxDuration, camera, success, fail} + * key: 存储桶中的目标文件名 folder: 存储桶中的目标文件夹 其它参数同uni.chooseVideo(mpWeixin) + */ +export function ossUploadVideo({ + key, + folder, + sourceType = ['album', 'camera'], //album 从相册选视频, camera 使用相机拍摄 + compressed = true, //是否压缩所选的视频源文件 + maxDuration = 60, //拍摄视频最长拍摄时间, 单位秒。最长支持 60 秒 + camera = 'back', //调用相机方向, 'front'、'back', 默认'back' +}) { + + return new Promise((success, fail) => { + uni.chooseVideo({ + sourceType, + compressed, + maxDuration, + camera, + success(res) { + ossUpload(res.tempFilePath, key, folder).then(success).catch(fail) + }, + fail + }) + }) + +} +const OSS = { + ossUploadVideo, + ossUploadImage, + ossUpload +} +export default OSS; \ No newline at end of file