完成京东购物模块

This commit is contained in:
tangxinyue 2026-04-10 14:08:42 +08:00
parent 107f39efef
commit c7a434a24d
2 changed files with 443 additions and 23 deletions

View File

@ -1,6 +1,8 @@
<template>
<view class="top-fixed">
<nav-bar title="京东" bgColor="#F0F4F9" :buttonGroup="buttonGroup" @button-click="util.clickTitlePopupButton">
<nav-bar v-if="!selectedImage" title="京东" bgColor="#F0F4F9" :buttonGroup="buttonGroup"
@button-click="util.clickTitlePopupButton" tipLayerType="jingdong-shopping-list" isTipLayer
tipLayerText="添加订单信息">
<template v-slot:center>
<view class="search-box">
<image class="search-icon" src="/static/image/shopping/jingdong/search-icon.png" mode="aspectFit">
@ -17,6 +19,9 @@
</view>
</template>
</nav-bar>
<nav-bar v-else title="拼图" bgColor="#EFEFEF" noBack @back="closeImage" isRightButton
@right-click="confirmImage">
</nav-bar>
<view class="tab-list">
<view class="tab-item" v-for="(item, index) in tabList" :key="index"
:class="{ active: currentTab === index }" @click="switchTab(index)">
@ -55,13 +60,30 @@
<image class="like-icon" src="/static/image/shopping/jingdong/weinituijian.png" mode="aspectFit"></image>
<text>为你推荐</text>
</view>
<view class="upload-screenshot-box">
<view class="upload-screenshot-content">
<view v-if="!selectedImage" class="upload-screenshot-box" @touchstart="handleTouchStart"
@touchend="handleTouchEnd">
<view v-if="!screenshotImage" class="upload-screenshot-content">
<image class="upload-screenshot" src="/static/image/common/upload-screenshot.png" mode="aspectFit">
</image>
<view class="upload-screenshot-text">长按替换截图</view>
</view>
<view v-else class="upload-screenshot-content">
<image class="screenshot-preview" :src="screenshotImage" mode="widthFix"></image>
</view>
<!-- <image v-else class="w100 h100" :src="screenshotImage" mode="widthFix"></image> -->
</view>
<view v-else class="upload-screenshot-box scroll-image-box">
<scroll-view class="image-box" style="width: 100%;height: 100%;" scroll-y :show-scrollbar="false"
@scroll="onImageScroll">
<image class="crop-image-target" style="width:100%;" :src="selectedImage" mode="widthFix"></image>
</scroll-view>
<view class="dashed-line-box">
<view class="dashed-line-text">我是分割线</view>
</view>
</view>
<canvas canvas-id="crop-canvas"
style="position: fixed; left: -9999px; width: 750rpx; height: 100vh; pointer-events: none;"></canvas>
</view>
<!-- 长按操作气泡菜单 -->
@ -74,6 +96,14 @@
<view class="bubble-arrow"></view>
</view>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
</template>
<script setup>
@ -85,6 +115,12 @@ import { util } from '@/utils/common.js';
const showActionMenu = ref(false);
const actionMenuState = ref({ x: 0, y: 0, item: null });
//
const screenshotImage = ref('');
const selectedImage = ref('');
const scrollTop = ref(0);
let longPressTimer = null;
//
const buttonGroup = [
{
@ -235,9 +271,243 @@ onShow(() => {
mockOrderList.value = cachedData;
}
//
const cachedImage = uni.getStorageSync('jingdong_screenshot');
if (cachedImage) {
screenshotImage.value = cachedImage;
}
console.log('screenshotImage.value', screenshotImage.value)
console.log("mockOrderList", mockOrderList.value);
});
// -
const chooseImage = () => {
if (selectedImage.value) return;
uni.chooseImage({
count: 1,
sourceType: ['album'],
success: (res) => {
selectedImage.value = res.tempFilePaths[0];
}
});
};
//
const handleTouchStart = (e) => {
const systemInfo = uni.getSystemInfoSync();
if (systemInfo.platform === 'ios' && systemInfo.safeAreaInsets?.bottom) {
const clientY = e.touches[0].clientY;
const windowHeight = systemInfo.windowHeight;
if (clientY > windowHeight - systemInfo.safeAreaInsets.bottom) {
return;
}
}
longPressTimer = setTimeout(() => {
uni.vibrateShort();
chooseImage();
}, 1200);
};
const handleTouchEnd = () => {
if (longPressTimer) {
clearTimeout(longPressTimer);
longPressTimer = null;
}
};
//
const onImageScroll = (e) => {
scrollTop.value = e.detail.scrollTop;
};
//
//const confirmImage = () => {
// uni.showLoading({ title: '...' });
// const query = uni.createSelectorQuery().in(instance.proxy);
// query.select('.image-box').boundingClientRect();
// query.select('.crop-image-target').boundingClientRect();
// query.exec(res => {
// if (!res[0] || !res[1]) {
// uni.hideLoading();
// return;
// }
// const container = res[0];
// const image = res[1];
//
// uni.getImageInfo({
// src: selectedImage.value,
// success: (imgInfo) => {
// const scale = imgInfo.width / image.width;
// const sTop = scrollTop.value * scale;
// const sHeight = container.height * scale;
// const sWidth = imgInfo.width;
// const canvasW = container.width;
// const canvasH = container.height;
//
// const ctx = uni.createCanvasContext('crop-canvas', instance.proxy);
// ctx.clearRect(0, 0, canvasW, canvasH);
// ctx.drawImage(
// imgInfo.path,
// 0, sTop, sWidth, sHeight,
// 0, 0, canvasW, canvasH
// );
// ctx.draw(false, () => {
// uni.canvasToTempFilePath({
// canvasId: 'crop-canvas',
// width: canvasW,
// height: canvasH,
// destWidth: sWidth,
// destHeight: sHeight,
// success: (res) => {
// uni.saveFile({
// tempFilePath: res.tempFilePath,
// success: (saveRes) => {
// screenshotImage.value = saveRes.savedFilePath;
// selectedImage.value = '';
// uni.setStorageSync('jingdong_screenshot', saveRes.savedFilePath);
// uni.hideLoading();
// uni.showToast({ title: '', icon: 'success' });
// },
// fail: () => {
// uni.hideLoading();
// uni.showToast({ title: '', icon: 'none' });
// }
// });
// },
// fail: () => {
// uni.hideLoading();
// uni.showToast({ title: '', icon: 'none' });
// }
// }, instance.proxy);
// });
// },
// fail: () => {
// uni.hideLoading();
// uni.showToast({ title: '', icon: 'none' });
// }
// });
// });
//};
//
const confirmImage = () => {
uni.showLoading({
title: '处理中...'
})
const query = uni.createSelectorQuery().in(instance)
//
query.select('.image-box').boundingClientRect()
query.select('.crop-image-target').boundingClientRect()
query.exec(res => {
if (!res[0] || !res[1]) {
uni.hideLoading()
return
}
console.log('rects', res)
const container = res[0] //
const image = res[1] //
// ( / /?)
// canvas
// canvas drawImage : img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight
//
uni.getImageInfo({
src: selectedImage.value,
success: (imgInfo) => {
const scale = imgInfo.width / image.width // 图片 原始宽 / 渲染宽
const sTop = scrollTop.value * scale // Y
const sHeight = container.height * scale //
// widthFix
const sWidth = imgInfo.width
// (使)
// canvasContext使 pixelRatio
// uni-app canvas-id (px)
// canvas
const canvasW = container.width
const canvasH = container.height
const ctx = uni.createCanvasContext('crop-canvas', instance)
//
ctx.clearRect(0, 0, canvasW, canvasH)
//
// drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
ctx.drawImage(
imgInfo.path,
0, sTop, sWidth, sHeight, //
0, 0, canvasW, canvasH //
)
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'crop-canvas',
width: canvasW,
height: canvasH,
destWidth: sWidth, // 使,
destHeight: sHeight, // 使,
success: (res) => {
console.log('crop success (temp)', res
.tempFilePath)
//
uni.saveFile({
tempFilePath: res.tempFilePath,
success: (saveRes) => {
console.log('save success (saved)', saveRes.savedFilePath)
screenshotImage.value = saveRes.savedFilePath
selectedImage.value = '' //
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");
}, 200)
//
uni.setStorageSync('jingdong_screenshot', screenshotImage.value)
uni.hideLoading()
},
fail: (err) => {
console.error('saveFile fail', err)
uni.hideLoading()
uni.showToast({
title: '保存失败',
icon: 'none'
})
}
})
},
fail: (err) => {
console.error(err)
uni.hideLoading()
uni.showToast({
title: '裁剪失败',
icon: 'none'
})
}
}, instance)
})
},
fail: () => {
uni.hideLoading()
uni.showToast({
title: '图片加载失败',
icon: 'none'
})
}
})
})
}
//
const closeImage = () => {
selectedImage.value = '';
};
const currentTab = ref(0);
const tabList = ref([
{ name: '全部' },
@ -784,6 +1054,7 @@ const mockOrderList = ref([
display: flex;
align-items: center;
justify-content: center;
position: relative;
.upload-screenshot {
width: 92rpx;
@ -791,6 +1062,8 @@ const mockOrderList = ref([
}
.upload-screenshot-content {
width: 100%;
height: 100%;
display: flex;
align-items: center;
flex-direction: column;
@ -802,6 +1075,63 @@ const mockOrderList = ref([
line-height: 50rpx;
margin-top: 16rpx;
}
.screenshot-preview {
width: 100%;
height: 100%;
}
}
.scroll-image-box {
width: 100%;
min-height: 0; // flex
// overflow: hidden; // scroll-view
position: relative;
}
.dashed-line-box {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
border: 4rpx dashed #ffffff;
pointer-events: none;
.dashed-line-text {
height: 44rpx;
line-height: 44rpx;
width: 180rpx;
padding: 0 20rpx;
border-radius: 8rpx;
color: #1777FF;
font-size: 24rpx;
font-weight: 500;
background-color: #fff;
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
}
}
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.8);
z-index: 999;
.mask-icon {
position: absolute;
top: 50%;
right: 52rpx;
transform: translateY(-25%);
width: 360rpx;
height: 360rpx;
}
}
}

View File

@ -1,6 +1,7 @@
<template>
<view>
<nav-bar title="订单详情" bgColor="#F2F3F7">
<nav-bar title="订单详情" bgColor="#F2F3F7" :buttonGroup="buttonGroup" @button-click="util.clickTitlePopupButton"
tipLayerType="jingdong-shopping-order-detail-tip" isTipLayer tipLayerText="编辑订单信息">
<!-- <template v-slot:left>
<image class="back-icon" src="/static/image/common/back.png" mode="aspectFit"></image>
</template> -->
@ -27,7 +28,7 @@
<!-- 等待付款 -->
<template v-if="order.status == '等待付款'">
<view class="tip-box">
<text>还剩 <text style="color: #DD223F;font-weight: 500;">18 : 25 : 46</text> 订单自动取消</text>
<text>还剩 <text style="color: #DD223F;font-weight: 500;">{{ order.statusDesc }}</text> 订单自动取消</text>
</view>
<view class="address-info">
<view>
@ -410,6 +411,7 @@
<view class="text">购物再享折上95折</view>
</view>
<template v-if="screenshotImage">
<view class="w100 flex-center" style="margin-top: 40rpx;margin-bottom: 32rpx;">
<image style="width: 32rpx;height: 20rpx;" src="/static/image/shopping/jingdong/detail/like-left.png">
</image>
@ -421,18 +423,21 @@
<!-- 上传图片 -->
<view class="upload-screenshot-box">
<view class="upload-screenshot-content">
<image class="upload-screenshot" src="/static/image/common/upload-screenshot.png" mode="aspectFit">
<view class="upload-screenshot-content w100">
<!-- <image class="upload-screenshot" src="/static/image/common/upload-screenshot.png" mode="aspectFit">
</image>
<view class="upload-screenshot-text">长按替换截图</view>
<view class="upload-screenshot-text">长按替换截图</view> -->
<image class="w100" :src="screenshotImage" mode="widthFix"></image>
</view>
</view>
</template>
<view class="fixed-bottom flex-align-center flex-justify-between">
<text>{{ bottomText }}</text>
<view class="btn-box">
<view v-if="order.status == '等待付款'" class="big-btn ">立即支付<text class="wx-font-medium money">¥69</text>
</view>
<view v-else class="flex-align-center">
<view v-else class="flex-align-center 我00">
<view v-for="btn in bottomBtns" :key="btn" class="bottom-btn " :class="{ red: btn == '再次购买' }">
{{ btn }}
</view>
@ -440,18 +445,38 @@
</view>
</view>
<view class="placeholder"></view>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import { ref, computed, onUnmounted, watch } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { util } from '@/utils/common.js';
const buttonGroup = [
{
name: "修改订单",
click: () => {
uni.navigateTo({
url: '/pages/shopping/jingdong/add-order/add-order?id=' + order.value.id + '&type=' + order.value.shopType + '&isEdit=true'
});
}
},
]
/**
* 模拟订单列表
*/
const mockOrderList = ref([]);
const screenshotImage = ref('')
const bottomBtns = computed(() => {
if (order.value.status == '正在出库') {
@ -509,12 +534,77 @@ onLoad((options) => {
if (target) {
order.value = target;
console.log('成功匹配订单数据:', order.value);
//
if (order.value.status === '等待付款' && order.value.statusDesc) {
initCountdown();
}
} else {
console.error('未找到指定的订单数据, ID:', id);
}
});
let countdownTimer = null;
/**
* 初始化并启动倒计时
*/
const initCountdown = () => {
if (countdownTimer) clearInterval(countdownTimer);
// "HH : mm : ss" "HH:mm:ss"
const parseTimeToSeconds = (timeStr) => {
if (!timeStr) return 0;
//
const parts = timeStr.replace(/\s/g, '').split(':');
let seconds = 0;
if (parts.length === 3) {
seconds = parseInt(parts[0]) * 3600 + parseInt(parts[1]) * 60 + parseInt(parts[2]);
} else if (parts.length === 2) {
seconds = parseInt(parts[0]) * 60 + parseInt(parts[1]);
}
return isNaN(seconds) ? 0 : seconds;
};
// "HH : mm : ss"
const formatSecondsToTime = (totalSeconds) => {
if (totalSeconds <= 0) return "00 : 00 : 00";
const h = Math.floor(totalSeconds / 3600);
const m = Math.floor((totalSeconds % 3600) / 60);
const s = totalSeconds % 60;
const pad = (n) => String(n).padStart(2, '0');
return `${pad(h)} : ${pad(m)} : ${pad(s)}`;
};
let remainingSeconds = parseTimeToSeconds(order.value.statusDesc);
if (remainingSeconds <= 0) return;
countdownTimer = setInterval(() => {
remainingSeconds--;
if (remainingSeconds <= 0) {
order.value.statusDesc = "00 : 00 : 00";
clearInterval(countdownTimer);
//
// order.value.status = '';
} else {
order.value.statusDesc = formatSecondsToTime(remainingSeconds);
}
}, 1000);
};
onUnmounted(() => {
if (countdownTimer) {
clearInterval(countdownTimer);
}
});
onShow(() => {
//
const cachedImage = uni.getStorageSync('jingdong_screenshot');
if (cachedImage) {
screenshotImage.value = cachedImage;
}
// #ifdef APP-PLUS
util.setAndroidSystemBarColor('#FFFFFF')
setTimeout(() => {
@ -1284,7 +1374,7 @@ const isOrderInfoExpanded = ref(false);
.upload-screenshot-box {
width: 100%;
flex: 1;
height: 540rpx;
// height: 540rpx;
background-color: #FFFFFF;
display: flex;
align-items: center;