fix:修复bug
This commit is contained in:
Vendored
+1
-1
@@ -5,6 +5,6 @@ VITE_DELETE_CONSOLE=false
|
|||||||
|
|
||||||
#本地环境
|
#本地环境
|
||||||
VITE_SERVER_BASEURL=https://howhowfresh.com/prod-api
|
VITE_SERVER_BASEURL=https://howhowfresh.com/prod-api
|
||||||
#VITE_SERVER_BASEURL=http://192.168.5.64:8080
|
#VITE_SERVER_BASEURL=http://192.168.5.36:8080
|
||||||
#VITE_SERVER_BASEURL=http://192.168.0.148:8888
|
#VITE_SERVER_BASEURL=http://192.168.0.148:8888
|
||||||
#VITE_SERVER_BASEURL=http://liuyao.nat100.top/meiguowaimai
|
#VITE_SERVER_BASEURL=http://liuyao.nat100.top/meiguowaimai
|
||||||
@@ -10,6 +10,12 @@ const props = withDefaults(defineProps<{
|
|||||||
});
|
});
|
||||||
|
|
||||||
function handleClickLeft() {
|
function handleClickLeft() {
|
||||||
|
// 页面栈不足时,navigateBack 会失败;兜底回首页
|
||||||
|
const pages = getCurrentPages?.() || []
|
||||||
|
if (pages.length <= 1) {
|
||||||
|
uni.switchTab({ url: '/pages/home/index' })
|
||||||
|
return
|
||||||
|
}
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ const show = ref(false);
|
|||||||
const pickerValue = ref(0)
|
const pickerValue = ref(0)
|
||||||
const emits = defineEmits(['confirm'])
|
const emits = defineEmits(['confirm'])
|
||||||
|
|
||||||
function onOpen(value: number) {
|
function onOpen(value?: number) {
|
||||||
if (value) {
|
if (value !== undefined && value !== null) {
|
||||||
pickerValue.value = value;
|
pickerValue.value = value;
|
||||||
}
|
}
|
||||||
show.value = true;
|
show.value = true;
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
"loading": "Loading",
|
"loading": "Loading",
|
||||||
"mile": "mile",
|
"mile": "mile",
|
||||||
"minutes": "minutes",
|
"minutes": "minutes",
|
||||||
|
"day": "day",
|
||||||
|
"days": "days",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"obtain": "get",
|
"obtain": "get",
|
||||||
"operation-success": "Operation successful",
|
"operation-success": "Operation successful",
|
||||||
@@ -439,6 +441,7 @@
|
|||||||
"claimCoupon": "Claim coupon",
|
"claimCoupon": "Claim coupon",
|
||||||
"couponOff": "Off",
|
"couponOff": "Off",
|
||||||
"validDays": "Valid Days",
|
"validDays": "Valid Days",
|
||||||
|
"day": "day",
|
||||||
"days": "days",
|
"days": "days",
|
||||||
"get": "Get",
|
"get": "Get",
|
||||||
"congratulations": "Congratulations",
|
"congratulations": "Congratulations",
|
||||||
@@ -493,6 +496,7 @@
|
|||||||
"tips3": "membership to enjoy a ",
|
"tips3": "membership to enjoy a ",
|
||||||
"tips4": "Enjoy delivery service over",
|
"tips4": "Enjoy delivery service over",
|
||||||
"tips5": "Delivery fee",
|
"tips5": "Delivery fee",
|
||||||
|
"start": " and up",
|
||||||
"title": "The minimum order amount for this store is",
|
"title": "The minimum order amount for this store is",
|
||||||
"toast": {
|
"toast": {
|
||||||
"deliveryService": "This merchant does not provide delivery service",
|
"deliveryService": "This merchant does not provide delivery service",
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
"loading": "加载中",
|
"loading": "加载中",
|
||||||
"mile": "英里",
|
"mile": "英里",
|
||||||
"minutes": "分钟",
|
"minutes": "分钟",
|
||||||
|
"day": "天",
|
||||||
|
"days": "天",
|
||||||
"no": "否",
|
"no": "否",
|
||||||
"obtain": "获取",
|
"obtain": "获取",
|
||||||
"operation-success": "操作成功",
|
"operation-success": "操作成功",
|
||||||
@@ -439,6 +441,7 @@
|
|||||||
"claimCoupon": "领取优惠券",
|
"claimCoupon": "领取优惠券",
|
||||||
"couponOff": "优惠",
|
"couponOff": "优惠",
|
||||||
"validDays": "有效天数",
|
"validDays": "有效天数",
|
||||||
|
"day": "天",
|
||||||
"days": "天",
|
"days": "天",
|
||||||
"get": "获取",
|
"get": "获取",
|
||||||
"congratulations": "恭喜!",
|
"congratulations": "恭喜!",
|
||||||
@@ -493,6 +496,7 @@
|
|||||||
"tips3": "会员可享受",
|
"tips3": "会员可享受",
|
||||||
"tips4": "最低起送金额",
|
"tips4": "最低起送金额",
|
||||||
"tips5": "配送费",
|
"tips5": "配送费",
|
||||||
|
"start": "起",
|
||||||
"title": "这家店的起送金额是",
|
"title": "这家店的起送金额是",
|
||||||
"toast": {
|
"toast": {
|
||||||
"deliveryService": "该商家未开通配送服务",
|
"deliveryService": "该商家未开通配送服务",
|
||||||
|
|||||||
+2
-2
@@ -2,8 +2,8 @@
|
|||||||
"name" : "CHEFLINK delivery",
|
"name" : "CHEFLINK delivery",
|
||||||
"appid" : "__UNI__06509BE",
|
"appid" : "__UNI__06509BE",
|
||||||
"description" : "",
|
"description" : "",
|
||||||
"versionName" : "1.0.21",
|
"versionName" : "1.0.26",
|
||||||
"versionCode" : 121,
|
"versionCode" : 126,
|
||||||
"transformPx" : false,
|
"transformPx" : false,
|
||||||
/* 5+App特有相关 */
|
/* 5+App特有相关 */
|
||||||
"app-plus" : {
|
"app-plus" : {
|
||||||
|
|||||||
@@ -30,8 +30,9 @@ const userStore = useUserStore();
|
|||||||
|
|
||||||
// 送达偏好
|
// 送达偏好
|
||||||
const visitMethodRef = ref();
|
const visitMethodRef = ref();
|
||||||
|
// value 与 visit-method 组件保持一致:0-亲自送达 1-放门口
|
||||||
const visitMethod = ref({
|
const visitMethod = ref({
|
||||||
value: 2,
|
value: 1,
|
||||||
label: t("components.visit.putItAtTheDoor"),
|
label: t("components.visit.putItAtTheDoor"),
|
||||||
});
|
});
|
||||||
function chooseVisitMethod() {
|
function chooseVisitMethod() {
|
||||||
@@ -633,15 +634,14 @@ function getAddressList() {
|
|||||||
|
|
||||||
let data = addressesList.value[0]
|
let data = addressesList.value[0]
|
||||||
|
|
||||||
// 回显用户地址中的送达偏好
|
// 回显送达偏好(deliveryType: 1-亲自送达 2-放门口)
|
||||||
// 回显送达偏好
|
if(+data.deliveryType === 1) {
|
||||||
if(+data.deliveryRemark === 1) {
|
|
||||||
visitMethod.value.label = t('components.visit.leaveItToMePersonally')
|
visitMethod.value.label = t('components.visit.leaveItToMePersonally')
|
||||||
visitMethod.value.value = 1
|
visitMethod.value.value = 0
|
||||||
}
|
}
|
||||||
if(+data.deliveryRemark === 2) {
|
if(+data.deliveryType === 2) {
|
||||||
visitMethod.value.label = t('components.visit.putItAtTheDoor')
|
visitMethod.value.label = t('components.visit.putItAtTheDoor')
|
||||||
visitMethod.value.value = 2
|
visitMethod.value.value = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是批量模式,地址加载完成后重新计算价格
|
// 如果是批量模式,地址加载完成后重新计算价格
|
||||||
@@ -664,12 +664,14 @@ function chooseAddress() {
|
|||||||
// 将新选择的地址id存储到selectedAddressId中
|
// 将新选择的地址id存储到selectedAddressId中
|
||||||
selectedAddressId.value = data.data
|
selectedAddressId.value = data.data
|
||||||
|
|
||||||
// 回显送达偏好
|
// 回显送达偏好(deliveryType: 1-亲自送达 2-放门口)
|
||||||
if(+data.deliveryType === 1) {
|
if(+data.deliveryType === 1) {
|
||||||
visitMethod.value.label = t('components.visit.leaveItToMePersonally')
|
visitMethod.value.label = t('components.visit.leaveItToMePersonally')
|
||||||
|
visitMethod.value.value = 0
|
||||||
}
|
}
|
||||||
if(+data.deliveryType === 2) {
|
if(+data.deliveryType === 2) {
|
||||||
visitMethod.value.label = t('components.visit.putItAtTheDoor')
|
visitMethod.value.label = t('components.visit.putItAtTheDoor')
|
||||||
|
visitMethod.value.value = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新计算价格
|
// 重新计算价格
|
||||||
@@ -895,9 +897,16 @@ function appMerchantOrderPayOrder() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
setTimeout(()=> {
|
setTimeout(()=> {
|
||||||
uni.navigateBack({
|
// 支付成功后跳转到当前订单详情页
|
||||||
delta: 2,
|
if (resOrderId.value) {
|
||||||
})
|
uni.reLaunch({
|
||||||
|
url: `/pages-store/pages/order/index?id=${resOrderId.value}`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.navigateBack({
|
||||||
|
delta: 2,
|
||||||
|
})
|
||||||
|
}
|
||||||
}, 500)
|
}, 500)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -920,9 +929,17 @@ function appMerchantOrderPayOrderBatch() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
setTimeout(()=> {
|
setTimeout(()=> {
|
||||||
uni.navigateBack({
|
// 批量支付后,优先跳转到第一笔订单详情;若无订单ID则返回
|
||||||
delta: 2,
|
const firstOrderId = resOrderIds.value && resOrderIds.value.length > 0 ? resOrderIds.value[0] : ''
|
||||||
})
|
if (firstOrderId) {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: `/pages-store/pages/order/index?id=${firstOrderId}`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.navigateBack({
|
||||||
|
delta: 2,
|
||||||
|
})
|
||||||
|
}
|
||||||
}, 500)
|
}, 500)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('批量支付失败', err)
|
console.error('批量支付失败', err)
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import {
|
|||||||
getCurrentInstance
|
getCurrentInstance
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import Search from "@/pages/home/components/tabbar-home/components/search.vue";
|
import Search from "@/pages/home/components/tabbar-home/components/search.vue";
|
||||||
import useEventEmit from "@/hooks/useEventEmit";
|
|
||||||
import {EventEnum} from "@/constant/enums";
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
import {useAddressStore} from "@/pages/address/store/address";
|
import {useAddressStore} from "@/pages/address/store/address";
|
||||||
import {appUserAddressListPost} from "@/service";
|
import {appUserAddressListPost} from "@/service";
|
||||||
@@ -15,6 +13,8 @@ const addressStore = useAddressStore()
|
|||||||
|
|
||||||
const addressesList = ref([])
|
const addressesList = ref([])
|
||||||
const currentAddressId = ref('')
|
const currentAddressId = ref('')
|
||||||
|
// 从 checkout 等页面通过 URL 传入的地址 id,用于进入页面时保持正确选中项
|
||||||
|
const initialAddressIdFromQuery = ref('')
|
||||||
function getAddressList() {
|
function getAddressList() {
|
||||||
appUserAddressListPost({
|
appUserAddressListPost({
|
||||||
params: {
|
params: {
|
||||||
@@ -25,37 +25,61 @@ function getAddressList() {
|
|||||||
console.log('获取用户地址列表', res)
|
console.log('获取用户地址列表', res)
|
||||||
addressesList.value = res.rows
|
addressesList.value = res.rows
|
||||||
if(res.rows.length > 0) {
|
if(res.rows.length > 0) {
|
||||||
currentAddressId.value = res.rows[0].id
|
// 若有从上一页传入的 id 且该 id 在列表中,则保持选中该地址;否则选中第一个
|
||||||
|
const fromQuery = initialAddressIdFromQuery.value
|
||||||
|
const existsInList = res.rows.some((row: any) => String(row.id) === String(fromQuery))
|
||||||
|
if (fromQuery && existsInList) {
|
||||||
|
currentAddressId.value = fromQuery
|
||||||
|
} else {
|
||||||
|
currentAddressId.value = res.rows[0].id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function handleClickSearch() {
|
function handleClickSearch() {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages-user/pages/search-address/index',
|
url: '/pages-user/pages/search-address/index',
|
||||||
|
events: {
|
||||||
|
chooseAddress: (data: any) => {
|
||||||
|
console.log('搜索的地址信息', data)
|
||||||
|
if (!data) return
|
||||||
|
|
||||||
|
// 先判断是否已有相同地址
|
||||||
|
const exist = addressesList.value.find((item: any) => {
|
||||||
|
return (
|
||||||
|
item.formattedAddress === data.formattedAddress &&
|
||||||
|
(item.displayName || '') === (data.displayName || '')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (exist) {
|
||||||
|
// 已存在:直接选择这个地址,走原有 chooseAddress 流程
|
||||||
|
chooseAddress(exist)
|
||||||
|
} else {
|
||||||
|
// 不存在:走新增地址流程
|
||||||
|
addressStore.setAddressLocation({
|
||||||
|
displayName: data.displayName,
|
||||||
|
formattedAddress: data.formattedAddress,
|
||||||
|
longitude: data.location.lng,
|
||||||
|
latitude: data.location.lat,
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/address/choose-type'
|
||||||
|
})
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEventEmit(EventEnum.CHOOSE_ADDRESS, (data) => {
|
|
||||||
console.log('搜索的地址信息', data)
|
|
||||||
if(data) {
|
|
||||||
addressStore.setAddressLocation({
|
|
||||||
displayName: data.displayName,
|
|
||||||
formattedAddress: data.formattedAddress,
|
|
||||||
longitude: data.location.lng,
|
|
||||||
latitude: data.location.lat,
|
|
||||||
})
|
|
||||||
setTimeout(()=> {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/address/choose-type'
|
|
||||||
})
|
|
||||||
}, 300)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
onShow(()=> {
|
onShow(()=> {
|
||||||
getAddressList()
|
getAddressList()
|
||||||
})
|
})
|
||||||
onLoad((options: any)=> {
|
onLoad((options: any)=> {
|
||||||
if(options.id) {
|
if(options.id) {
|
||||||
|
initialAddressIdFromQuery.value = options.id
|
||||||
currentAddressId.value = options.id
|
currentAddressId.value = options.id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -163,6 +163,8 @@ const payMethodOptions = ref({
|
|||||||
payMethod: 1, // 支付方式 1信用卡 2余额
|
payMethod: 1, // 支付方式 1信用卡 2余额
|
||||||
payPassword: '',
|
payPassword: '',
|
||||||
})
|
})
|
||||||
|
// 支付密码输入框 ref
|
||||||
|
const passwordInputRef = ref()
|
||||||
useEventEmit(EventEnum.CHOOSE_PAYMENT_METHOD, (data) => {
|
useEventEmit(EventEnum.CHOOSE_PAYMENT_METHOD, (data) => {
|
||||||
if(data) {
|
if(data) {
|
||||||
if(data.payMethod === 1) {
|
if(data.payMethod === 1) {
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import {useConfigStore} from "@/store";
|
|||||||
import {receiveCouponApi} from "@/pages-user/service";
|
import {receiveCouponApi} from "@/pages-user/service";
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
|
|
||||||
const {t} = useI18n()
|
const { t, locale } = useI18n()
|
||||||
const emit = defineEmits(['confirm'])
|
const emit = defineEmits(['confirm'])
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
couponList: {
|
couponList: {
|
||||||
type: Array,
|
type: Array as unknown as PropType<any[]>,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -37,6 +37,57 @@ function confirmCoupon(item: any) {
|
|||||||
defineExpose({
|
defineExpose({
|
||||||
init
|
init
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function daySuffix(value: unknown) {
|
||||||
|
const n = Number(value)
|
||||||
|
const isEn = String(locale.value || '').toLowerCase().startsWith('en')
|
||||||
|
if (isEn) {
|
||||||
|
return n === 1 ? ` ${t('pages-store.store.day')}` : ` ${t('pages-store.store.days')}`
|
||||||
|
}
|
||||||
|
return t('pages-store.store.days')
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEnLocale() {
|
||||||
|
return String(locale.value || '').toLowerCase().startsWith('en')
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMoney(value: unknown) {
|
||||||
|
const n = Number(value)
|
||||||
|
if (!Number.isFinite(n)) return '0.00'
|
||||||
|
return n.toFixed(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function couponTitleText(item: any) {
|
||||||
|
const type = Number(item?.couponType)
|
||||||
|
const discountValue = Number(item?.discountValue)
|
||||||
|
|
||||||
|
// 标题保持原样:折扣券显示百分比,满减券只显示减免金额(不展示门槛)
|
||||||
|
if (type === 1) {
|
||||||
|
const pct = Number.isFinite(discountValue) ? Number(discountValue * 100).toFixed(0) : '0'
|
||||||
|
return `${pct}% ${t('pages-store.store.couponOff')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `$${formatMoney(discountValue)} ${t('pages-store.store.couponOff')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function couponBenefitText(item: any) {
|
||||||
|
const type = Number(item?.couponType)
|
||||||
|
const discountValue = Number(item?.discountValue)
|
||||||
|
const minAmount = Number(item?.minAmount)
|
||||||
|
|
||||||
|
if (type === 2) {
|
||||||
|
const discountText = `$${formatMoney(discountValue)}`
|
||||||
|
const hasMin = Number.isFinite(minAmount) && minAmount > 0
|
||||||
|
if (hasMin) {
|
||||||
|
const minText = `$${formatMoney(minAmount)}`
|
||||||
|
return isEnLocale() ? `Min ${minText} · Off ${discountText}` : `满${minText}减${discountText} 优惠`
|
||||||
|
}
|
||||||
|
return isEnLocale() ? `Off ${discountText}` : `立减${discountText}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const pct = Number.isFinite(discountValue) ? Number(discountValue * 100).toFixed(0) : '0'
|
||||||
|
return `${pct}%`
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -44,20 +95,15 @@ defineExpose({
|
|||||||
<view class="bg-#F5F5F5 px-32rpx pt-30rpx">
|
<view class="bg-#F5F5F5 px-32rpx pt-30rpx">
|
||||||
<view class="text-40rpx lh-40rpx text-#333 font-bold text-center mb-60rpx">{{ t('pages-store.store.claimCoupon') }}</view>
|
<view class="text-40rpx lh-40rpx text-#333 font-bold text-center mb-60rpx">{{ t('pages-store.store.claimCoupon') }}</view>
|
||||||
<scroll-view scroll-y class="h-1000rpx">
|
<scroll-view scroll-y class="h-1000rpx">
|
||||||
<template v-for="item in couponList">
|
<template v-for="item in couponList" :key="item.id">
|
||||||
<view class="coupon-item h-328rpx flex flex-col mb-30rpx last:mb-0">
|
<view class="coupon-item h-328rpx flex flex-col mb-30rpx last:mb-0">
|
||||||
<view class="flex-1 pt-40rpx px-58rpx">
|
<view class="flex-1 pt-40rpx px-58rpx">
|
||||||
<view class="line-clamp-1 text-34rpx lh-34rpx text-#333 font-bold">
|
<view class="line-clamp-1 text-34rpx lh-34rpx text-#333 font-bold">
|
||||||
<!-- couponType 1-折扣券, 2-满减券-->
|
<!-- couponType 1-折扣券, 2-满减券-->
|
||||||
<template v-if="item.couponType === 1">
|
{{ couponTitleText(item) }}
|
||||||
{{ Number(item.discountValue * 100).toFixed(0) }}% {{ t('pages-store.store.couponOff') }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
${{ item.discountValue }} {{ t('pages-store.store.couponOff') }}
|
|
||||||
</template>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="text-24rpx lh-32rpx text-#999 my-18rpx">
|
<view class="text-24rpx lh-32rpx text-#999 my-18rpx">
|
||||||
{{ t('pages-store.store.validDays') }}: {{ item.validDays }} {{ t('pages-store.store.days') }}
|
{{ t('pages-store.store.validDays') }}: {{ item.validDays }}{{ daySuffix(item.validDays) }}
|
||||||
</view>
|
</view>
|
||||||
<view class="text-24rpx lh-32rpx text-#999 my-18rpx">
|
<view class="text-24rpx lh-32rpx text-#999 my-18rpx">
|
||||||
{{ item.nameZh }}
|
{{ item.nameZh }}
|
||||||
@@ -67,12 +113,8 @@ defineExpose({
|
|||||||
class="w-36rpx h-36rpx shrink-0 mr-10rpx"
|
class="w-36rpx h-36rpx shrink-0 mr-10rpx"
|
||||||
src="@img/chef/106.png"
|
src="@img/chef/106.png"
|
||||||
></image>
|
></image>
|
||||||
{{ t('pages-store.store.get') }} <template v-if="item.couponType === 1">
|
{{ t('pages-store.store.get') }} {{ couponBenefitText(item) }}
|
||||||
{{ Number(item.discountValue * 100).toFixed(0) }}%
|
<template v-if="Number(item?.couponType) === 1"> {{ t('pages-store.store.couponOff') }}</template>
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
${{ item.discountValue }}
|
|
||||||
</template> {{ t('pages-store.store.couponOff') }}
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<template v-if="item.userCouponVo">
|
<template v-if="item.userCouponVo">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { debounce } from 'throttle-debounce'
|
import { debounce } from 'throttle-debounce'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import Config from '@/config/index'
|
import Config from '@/config/index'
|
||||||
const { t } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
import StoreSkeleton from './components/store-skeleton.vue';
|
import StoreSkeleton from './components/store-skeleton.vue';
|
||||||
import { useScrollThreshold } from '@/hooks/useScrollThreshold'
|
import { useScrollThreshold } from '@/hooks/useScrollThreshold'
|
||||||
import {
|
import {
|
||||||
@@ -271,20 +271,6 @@ const deliveryMethod = ref(0);
|
|||||||
const deliveryMethodOptions = [t('pages-store.store.delivery'), t('pages-store.store.pickup')];
|
const deliveryMethodOptions = [t('pages-store.store.delivery'), t('pages-store.store.pickup')];
|
||||||
const showDeliverySwitch = ref(true); // 是否显示配送方式切换组件
|
const showDeliverySwitch = ref(true); // 是否显示配送方式切换组件
|
||||||
|
|
||||||
// 配送时间显示:后端返回分钟;>= 1 天时换算成“天”,否则保持“分钟”
|
|
||||||
function formatMinutesToDaysText(minutes?: number | string) {
|
|
||||||
const m = Number(minutes)
|
|
||||||
if (!Number.isFinite(m) || m <= 0) return '--'
|
|
||||||
const MINUTES_PER_DAY = 60 * 24
|
|
||||||
if (m < MINUTES_PER_DAY) return `${m} ${t('common.minutes')}`
|
|
||||||
const days = m / MINUTES_PER_DAY
|
|
||||||
// 保留 1 位小数并去掉末尾 0(例如 2.0 -> 2)
|
|
||||||
let daysText = days.toFixed(1).replace(/\.0$/, '')
|
|
||||||
daysText = '3';
|
|
||||||
return `${daysText}${t('pages-store.store.days')}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const deliveryTimeText = computed(() => formatMinutesToDaysText(storeDetail.value?.deliveryTime as any))
|
|
||||||
function handleClickSegmented(index: number) {
|
function handleClickSegmented(index: number) {
|
||||||
console.log("切换配送方式:", index);
|
console.log("切换配送方式:", index);
|
||||||
if(+storeDetail.value.deliveryService !== 1 && index === 0) {
|
if(+storeDetail.value.deliveryService !== 1 && index === 0) {
|
||||||
@@ -307,6 +293,15 @@ function handleClickSegmented(index: number) {
|
|||||||
const activeTab = ref(0);
|
const activeTab = ref(0);
|
||||||
const tabs = ref<any[]>([]);
|
const tabs = ref<any[]>([]);
|
||||||
|
|
||||||
|
function daySuffix(value: unknown) {
|
||||||
|
const n = Number(value)
|
||||||
|
const isEn = String(locale.value || '').toLowerCase().startsWith('en')
|
||||||
|
if (isEn) {
|
||||||
|
return n === 1 ? ` ${t('pages-store.store.day')}` : ` ${t('pages-store.store.days')}`
|
||||||
|
}
|
||||||
|
return t('pages-store.store.days')
|
||||||
|
}
|
||||||
|
|
||||||
// 分页相关状态
|
// 分页相关状态
|
||||||
const pageNum = ref(1);
|
const pageNum = ref(1);
|
||||||
const pageSize = ref(10);
|
const pageSize = ref(10);
|
||||||
@@ -563,7 +558,9 @@ function handleShare() {
|
|||||||
></image>
|
></image>
|
||||||
CHEFLINK
|
CHEFLINK
|
||||||
</view>
|
</view>
|
||||||
<text v-if="+storeDetail?.deliveryService === 1" class="text-#7D7D7D"> • {{ deliveryTimeText }}</text>
|
<text v-if="+storeDetail?.deliveryService === 1" class="text-#7D7D7D">
|
||||||
|
• {{ storeDetail?.deliveryTime }}{{ daySuffix(storeDetail?.deliveryTime) }}
|
||||||
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="text-24rpx lh-24rpx text-#7D7D7D text-center mt-16rpx">
|
<view class="text-24rpx lh-24rpx text-#7D7D7D text-center mt-16rpx">
|
||||||
{{ t('pages-store.store.title') }} US${{ storeDetail?.minOrderPrice }}
|
{{ t('pages-store.store.title') }} US${{ storeDetail?.minOrderPrice }}
|
||||||
@@ -627,11 +624,13 @@ function handleShare() {
|
|||||||
<template v-if="+storeDetail?.deliveryService === 1 && deliveryMethod === 0">
|
<template v-if="+storeDetail?.deliveryService === 1 && deliveryMethod === 0">
|
||||||
<view class="w-full h-full flex-1 text-center text-#CE7138 pr-44rpx">
|
<view class="w-full h-full flex-1 text-center text-#CE7138 pr-44rpx">
|
||||||
<view>{{ t('pages-store.store.tips4') }} ${{ storeDetail?.minOrderPrice }}</view>
|
<view>{{ t('pages-store.store.tips4') }} ${{ storeDetail?.minOrderPrice }}</view>
|
||||||
<view>{{ t('pages-store.store.tips5') }} ${{ storeDetail?.deliveryFee }}</view>
|
<view>{{ t('pages-store.store.tips5') }} ${{ storeDetail?.deliveryFee }}{{ t('pages-store.store.start') }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="h-128rpx w-1rpx rotate-0 bg-#D8D8D8"></view>
|
<view class="h-128rpx w-1rpx rotate-0 bg-#D8D8D8"></view>
|
||||||
<view class="w-full flex-1 center flex-col pl-44rpx">
|
<view class="w-full flex-1 center flex-col pl-44rpx">
|
||||||
<view class="text-#333 mb-8rpx">{{ deliveryTimeText }}</view>
|
<view class="text-#333 mb-8rpx">
|
||||||
|
{{ storeDetail?.deliveryTime }}{{ daySuffix(storeDetail?.deliveryTime) }}
|
||||||
|
</view>
|
||||||
<view class="text-#7D7D7D">{{ t('pages-store.store.earTime') }}</view>
|
<view class="text-#7D7D7D">{{ t('pages-store.store.earTime') }}</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -661,7 +660,7 @@ function handleShare() {
|
|||||||
<view v-if="tabs.length > 0" class="px-30rpx pb-120rpx">
|
<view v-if="tabs.length > 0" class="px-30rpx pb-120rpx">
|
||||||
<view v-if="currentDishList.length > 0" class="text-40rpx lh-40rpx font-500 my-36rpx">{{ t('pages-store.store.recommend') }}</view>
|
<view v-if="currentDishList.length > 0" class="text-40rpx lh-40rpx font-500 my-36rpx">{{ t('pages-store.store.recommend') }}</view>
|
||||||
<view v-if="currentDishList.length > 0" class="grid grid-cols-2 gap-30rpx">
|
<view v-if="currentDishList.length > 0" class="grid grid-cols-2 gap-30rpx">
|
||||||
<template v-for="item in currentDishList">
|
<template v-for="item in currentDishList" :key="item.id">
|
||||||
<view @click="navigateToDishes(item)" class="w-100% mb-10rpx">
|
<view @click="navigateToDishes(item)" class="w-100% mb-10rpx">
|
||||||
<view class="relative h-248rpx rounded-24rpx mb-28rpx">
|
<view class="relative h-248rpx rounded-24rpx mb-28rpx">
|
||||||
<view @click.stop="handleDishCollectionClick(item)" class="w-68rpx h-68rpx absolute z-2 top-0 right-0">
|
<view @click.stop="handleDishCollectionClick(item)" class="w-68rpx h-68rpx absolute z-2 top-0 right-0">
|
||||||
@@ -704,7 +703,7 @@ function handleShare() {
|
|||||||
<view class="flex-center-sb mt-12rpx">
|
<view class="flex-center-sb mt-12rpx">
|
||||||
<view class="text-28rpx text-#999">
|
<view class="text-28rpx text-#999">
|
||||||
<view class="line-through">US${{ item.originalPrice }}</view>
|
<view class="line-through">US${{ item.originalPrice }}</view>
|
||||||
<!-- <view>{{ t('pages-store.store.sales') }}:{{ item.salesCount }}</view> -->
|
<view>{{ t('pages-store.store.sales') }}:{{ item.salesCount }}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="center w-64rpx h-64rpx rounded-50% bg-white shadow-lg">
|
<view class="center w-64rpx h-64rpx rounded-50% bg-white shadow-lg">
|
||||||
|
|||||||
@@ -1,117 +1,100 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import * as R from "ramda";
|
import * as R from "ramda";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { debounce } from "throttle-debounce";
|
import {debounce} from "throttle-debounce";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import Config from "@/config";
|
import Config from "@/config";
|
||||||
|
|
||||||
const isSubmit = ref(false)
|
const isSubmit = ref(false)
|
||||||
|
|
||||||
function submit() {
|
function submit() {
|
||||||
isSubmit.value = true
|
isSubmit.value = true
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isSubmit.value = false
|
isSubmit.value = false
|
||||||
}, 3000)
|
}, 3000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 提交
|
// 提交
|
||||||
const handleSubmit = debounce(Config.debounceLongTime, submit, {
|
const handleSubmit = debounce(Config.debounceLongTime, submit, {
|
||||||
atBegin: true
|
atBegin: true
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { debounce } from "throttle-debounce";
|
import {debounce} from "throttle-debounce";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import Config from "@/config";
|
import Config from "@/config";
|
||||||
import { appUserCardSavePost } from "@/service";
|
import {appUserCardSavePost} from "@/service";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isSubmit: false
|
isSubmit: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
console.log('onLoad')
|
console.log('onLoad')
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.handleSuccess = debounce(Config.debounceLongTime, this.handleSuccess, {
|
this.handleSuccess = debounce(Config.debounceLongTime, this.handleSuccess, {
|
||||||
atBegin: true
|
atBegin: true
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleError() {
|
|
||||||
uni.showToast({
|
|
||||||
icon: 'none',
|
|
||||||
title: 'Add credit card failed'
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async handleSuccess(data) {
|
methods: {
|
||||||
try {
|
handleError() {
|
||||||
const params = {
|
uni.showToast({
|
||||||
cardNumber: '************' + data.card.last4,
|
icon: 'none',
|
||||||
cardId: data.id,
|
title: 'Add credit card failed'
|
||||||
}
|
|
||||||
const res = await appUserCardSavePost({
|
|
||||||
body: params
|
|
||||||
})
|
})
|
||||||
console.log('handleSuccess', res)
|
},
|
||||||
if (uni.getStorageSync('UNI_LOCALE') == 'zh-Hans') {
|
async handleSuccess(data) {
|
||||||
await uni.showToast({
|
try {
|
||||||
icon: 'none',
|
const params = {
|
||||||
title: '信用卡添加成功'
|
cardNumber: '************' + data.card.last4,
|
||||||
|
cardId: data.id,
|
||||||
|
}
|
||||||
|
const res = await appUserCardSavePost({
|
||||||
|
body: params
|
||||||
})
|
})
|
||||||
} else {
|
console.log('handleSuccess', res)
|
||||||
|
|
||||||
await uni.showToast({
|
await uni.showToast({
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
title: 'The credit card was added successfully.'
|
title: 'The credit card was added successfully.'
|
||||||
})
|
})
|
||||||
|
// const eventChannel = this.getOpenerEventChannel();
|
||||||
|
// const id = res.data
|
||||||
|
// eventChannel.emit('acceptDataFromOpenedPage', {...params, id});
|
||||||
|
setTimeout(uni.navigateBack, 1000)
|
||||||
|
} catch (e) {
|
||||||
}
|
}
|
||||||
// const eventChannel = this.getOpenerEventChannel();
|
},
|
||||||
// const id = res.data
|
|
||||||
// eventChannel.emit('acceptDataFromOpenedPage', {...params, id});
|
}
|
||||||
setTimeout(uni.navigateBack, 1000)
|
}
|
||||||
} catch (e) {
|
</script>
|
||||||
uni.showToast({
|
<script module="addRenderjs" lang="renderjs">
|
||||||
icon: 'none',
|
import {loadStripe} from '@stripe/stripe-js/pure';
|
||||||
title: e?.message || e?.data?.msg || 'Add credit card failed'
|
import Config from '@/config/index'
|
||||||
})
|
|
||||||
|
// @ts-ignore
|
||||||
|
export default {
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
isCompleted: false,
|
||||||
|
stripe:null,
|
||||||
|
elements:null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
mounted() {
|
||||||
}
|
console.log('mounted')
|
||||||
</script>
|
this.init()
|
||||||
<script module="addRenderjs" lang="renderjs">
|
},
|
||||||
import {loadStripe} from '@stripe/stripe-js/pure';
|
methods: {
|
||||||
import Config from '@/config/index'
|
async init(){
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
export default {
|
|
||||||
data(){
|
|
||||||
return {
|
|
||||||
isCompleted: false,
|
|
||||||
stripe:null,
|
|
||||||
elements:null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
console.log('mounted')
|
|
||||||
const locale = uni.getStorageSync('UNI_LOCALE') || 'en'
|
|
||||||
const loadingText = locale === 'zh-Hans' ? '加载中...' : 'Loading...'
|
|
||||||
uni.showLoading({
|
|
||||||
title: loadingText,
|
|
||||||
mask: true
|
|
||||||
})
|
|
||||||
this.init()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async init(){
|
|
||||||
try {
|
|
||||||
console.log('本次初始化使用的key', Config.stripeKey)
|
console.log('本次初始化使用的key', Config.stripeKey)
|
||||||
loadStripe.setLoadParameters({advancedFraudSignals: false});
|
loadStripe.setLoadParameters({advancedFraudSignals: false});
|
||||||
const stripe = await loadStripe(Config.stripeKey);
|
const stripe = await loadStripe(Config.stripeKey);
|
||||||
@@ -131,59 +114,55 @@ export default {
|
|||||||
// enable payment button
|
// enable payment button
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
uni.hideLoading()
|
},
|
||||||
} catch (error) {
|
async handleSubmit(isSubmit){
|
||||||
console.error('初始化失败', error)
|
console.log('handleSubmit',isSubmit,this.isCompleted)
|
||||||
uni.hideLoading()
|
console.log('handleSubmit',this.stripe)
|
||||||
const locale = uni.getStorageSync('UNI_LOCALE') || 'en'
|
|
||||||
const errorText = locale === 'zh-Hans' ? '加载失败,请重试' : 'Loading failed, please try again'
|
if(!isSubmit||!this.isCompleted||!this.stripe){
|
||||||
uni.showToast({
|
return
|
||||||
icon: 'none',
|
}
|
||||||
title: errorText
|
const {error, paymentMethod} = await this.stripe.createPaymentMethod({
|
||||||
})
|
elements:this.elements,
|
||||||
}
|
});
|
||||||
},
|
console.log(error)
|
||||||
async handleSubmit(isSubmit){
|
console.log(paymentMethod)
|
||||||
console.log('handleSubmit',isSubmit,this.isCompleted)
|
if (error) {
|
||||||
console.log('handleSubmit',this.stripe)
|
// Handle error
|
||||||
|
this.$ownerInstance.callMethod('handleError')
|
||||||
if(!isSubmit||!this.isCompleted||!this.stripe){
|
} else {
|
||||||
return
|
// Send paymentMethod.id to your server
|
||||||
}
|
this.$ownerInstance.callMethod('handleSuccess',paymentMethod)
|
||||||
const {error, paymentMethod} = await this.stripe.createPaymentMethod({
|
}
|
||||||
elements:this.elements,
|
|
||||||
});
|
|
||||||
console.log(error)
|
|
||||||
console.log(paymentMethod)
|
|
||||||
if (error) {
|
|
||||||
// Handle error
|
|
||||||
this.$ownerInstance.callMethod('handleError')
|
|
||||||
} else {
|
|
||||||
// Send paymentMethod.id to your server
|
|
||||||
this.$ownerInstance.callMethod('handleSuccess',paymentMethod)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
</script>
|
||||||
</script>
|
<template>
|
||||||
<template>
|
<view class="">
|
||||||
<view class="">
|
<navbar customClass="!bg-transparent" />
|
||||||
<navbar customClass="!bg-transparent" />
|
<view class="text-center px-30rpx mt-98rpx">
|
||||||
<view class="text-center px-30rpx mt-98rpx">
|
<view class="text-46rpx lh-46rpx text-#333 font-bold">{{ Config.appName }}</view>
|
||||||
<view class="text-46rpx lh-46rpx text-#333 font-bold">{{ Config.appName }}</view>
|
<view class="text-32rpx text-#333 font-500 mt-70rpx mb-12rpx">{{ t('pages-user.card.title') }}</view>
|
||||||
<view class="text-32rpx text-#333 font-500 mt-70rpx mb-12rpx">{{ t('pages-user.card.title') }}</view>
|
<view class="text-28rpx lh-28rpx text-#999">{{ t('pages-user.card.desc') }}</view>
|
||||||
<view class="text-28rpx lh-28rpx text-#999">{{ t('pages-user.card.desc') }}</view>
|
</view>
|
||||||
|
<view class="mt-188rpx px-30rpx py-40rpx bg-#fff" id="payment-form"
|
||||||
|
:prop="isSubmit"
|
||||||
|
:change:prop="addRenderjs.handleSubmit"
|
||||||
|
></view>
|
||||||
|
|
||||||
|
<!-- 底部确认按钮 -->
|
||||||
|
<fixed-bottom-large-btn
|
||||||
|
class="z-100"
|
||||||
|
fixed
|
||||||
|
:text="t('common.save')"
|
||||||
|
@click="handleSubmit"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="mt-188rpx px-30rpx py-40rpx bg-#fff" id="payment-form" :prop="isSubmit"
|
</template>
|
||||||
:change:prop="addRenderjs.handleSubmit"></view>
|
|
||||||
|
<style scoped lang="scss">
|
||||||
<!-- 底部确认按钮 -->
|
page {
|
||||||
<fixed-bottom-large-btn class="z-100" fixed :text="t('common.save')" @click="handleSubmit" />
|
background-color: #F5F5F5;
|
||||||
</view>
|
}
|
||||||
</template>
|
</style>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
page {
|
|
||||||
background-color: #F5F5F5;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,25 +1,57 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {appCouponExchangeCouponPost, appCouponUserCouponListPost} from "@/service";
|
import {appCouponExchangeCouponPost, appCouponUserCouponListPost} from "@/service";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
import { throttle } from "throttle-debounce";
|
import { throttle } from "throttle-debounce";
|
||||||
import Config from "@/config";
|
import Config from "@/config";
|
||||||
import {dayjs} from "@/plugin";
|
import {dayjs} from "@/plugin";
|
||||||
|
|
||||||
function getList(pageNum: number, pageSize: number) {
|
function isEnLocale() {
|
||||||
return new Promise(resolve => {
|
return String(locale.value || '').toLowerCase().startsWith('en')
|
||||||
appCouponUserCouponListPost({
|
|
||||||
params: {
|
|
||||||
pageNum,
|
|
||||||
pageSize,
|
|
||||||
}
|
|
||||||
}).then(res => {
|
|
||||||
resolve(res)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const {paging, loading, firstLoaded, dataList, queryList} = usePage(getList)
|
function formatMoney(value: unknown) {
|
||||||
|
const n = Number(value)
|
||||||
|
if (!Number.isFinite(n)) return '0.00'
|
||||||
|
return n.toFixed(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCouponDetail(item: any) {
|
||||||
|
const type = Number(item?.snapshotType)
|
||||||
|
const discount = Number(item?.snapshotDiscount)
|
||||||
|
const minAmount = Number(item?.snapshotMinAmount)
|
||||||
|
|
||||||
|
// 1-折扣券(percentage), 2-满减券(amount off with minimum spend)
|
||||||
|
if (type === 2) {
|
||||||
|
const hasMin = Number.isFinite(minAmount) && minAmount > 0
|
||||||
|
const discountText = `$${formatMoney(discount)}`
|
||||||
|
if (hasMin) {
|
||||||
|
const minText = `$${formatMoney(minAmount)}`
|
||||||
|
return isEnLocale()
|
||||||
|
? `Min ${minText} · Off ${discountText}`
|
||||||
|
: `满${minText}减${discountText}`
|
||||||
|
}
|
||||||
|
return isEnLocale() ? `Off ${discountText}` : `立减${discountText}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 1) {
|
||||||
|
const pct = Number.isFinite(discount) ? Number(discount * 100).toFixed(0) : '0'
|
||||||
|
return `${pct}% ${t('pages-store.store.discount')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return t('pages-store.store.discount')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getList(pageNum: number, pageSize: number) {
|
||||||
|
return appCouponUserCouponListPost({
|
||||||
|
params: {
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
}
|
||||||
|
}) as any
|
||||||
|
}
|
||||||
|
|
||||||
|
const {paging, loading, firstLoaded, dataList, queryList} = usePage<any>(getList)
|
||||||
|
|
||||||
|
|
||||||
const keyword = ref<string>("");
|
const keyword = ref<string>("");
|
||||||
@@ -120,23 +152,14 @@ function handleSubmit() {
|
|||||||
{{ item.snapshotMerchantId ? t('pages-user.coupon.merchant-specific') : t('pages-user.coupon.all-merchants') }}
|
{{ item.snapshotMerchantId ? t('pages-user.coupon.merchant-specific') : t('pages-user.coupon.all-merchants') }}
|
||||||
</view>
|
</view>
|
||||||
<view class="text-24rpx lh-32rpx text-#999 my-18rpx">
|
<view class="text-24rpx lh-32rpx text-#999 my-18rpx">
|
||||||
{{ t('pages-user.coupon.expiry-date') }}{{ dayjs(Number(item.snapshotValidEnd))
|
{{ dayjs(Number(item.snapshotValidEnd)).format('YYYY-MM-DD HH:mm') }}{{ isEnLocale() ? ' expires' : '到期' }}
|
||||||
.format('MM:DD HH:mm:ss') }}
|
|
||||||
</view>
|
</view>
|
||||||
<view class="text-28rpx lh-28rpx text-#333 flex items-center">
|
<view class="text-28rpx lh-28rpx text-#333 flex items-center">
|
||||||
<image
|
<image
|
||||||
class="w-36rpx h-36rpx shrink-0 mr-10rpx"
|
class="w-36rpx h-36rpx shrink-0 mr-10rpx"
|
||||||
src="@img/chef/106.png"
|
src="@img/chef/106.png"
|
||||||
></image>
|
></image>
|
||||||
<template v-if="item.snapshotType === 2">
|
{{ formatCouponDetail(item) }}
|
||||||
<!-- 满减-->
|
|
||||||
{{ item.snapshotDiscount }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ Number(item.snapshotDiscount * 100).toFixed(0) }}%
|
|
||||||
</template>
|
|
||||||
|
|
||||||
{{ t('pages-store.store.discount') }}
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="h-84rpx lh-84rpx text-center text-28rpx text-#fff">
|
<view class="h-84rpx lh-84rpx text-center text-28rpx text-#fff">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
appMerchantOrderCanUseCouponListMerchantIdGet
|
appMerchantOrderCanUseCouponListMerchantIdGet
|
||||||
} from "@/service";
|
} from "@/service";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
import { throttle } from "throttle-debounce";
|
import { throttle } from "throttle-debounce";
|
||||||
import Config from "@/config";
|
import Config from "@/config";
|
||||||
import {dayjs} from "@/plugin";
|
import {dayjs} from "@/plugin";
|
||||||
@@ -23,15 +23,51 @@ function getList(pageNum: number, pageSize: number) {
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
appMerchantOrderCanUseCouponListMerchantIdGet({
|
appMerchantOrderCanUseCouponListMerchantIdGet({
|
||||||
params: {
|
params: {
|
||||||
merchantId: props.id || ''
|
merchantId: props.id || '',
|
||||||
}
|
}
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
resolve({ rows: res.data })
|
resolve({ rows: res.data } as any)
|
||||||
})
|
})
|
||||||
})
|
}) as any
|
||||||
}
|
}
|
||||||
|
|
||||||
const {paging, loading, firstLoaded, dataList, queryList} = usePage(getList)
|
const {paging, loading, firstLoaded, dataList, queryList} = usePage<any>(getList)
|
||||||
|
|
||||||
|
function isEnLocale() {
|
||||||
|
return String(locale.value || '').toLowerCase().startsWith('en')
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMoney(value: unknown) {
|
||||||
|
const n = Number(value)
|
||||||
|
if (!Number.isFinite(n)) return '0.00'
|
||||||
|
return n.toFixed(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCouponDetail(item: any) {
|
||||||
|
const type = Number(item?.snapshotType)
|
||||||
|
const discount = Number(item?.snapshotDiscount)
|
||||||
|
const minAmount = Number(item?.snapshotMinAmount)
|
||||||
|
|
||||||
|
// 1-折扣券(percentage), 2-满减券(amount off with minimum spend)
|
||||||
|
if (type === 2) {
|
||||||
|
const hasMin = Number.isFinite(minAmount) && minAmount > 0
|
||||||
|
const discountText = `$${formatMoney(discount)}`
|
||||||
|
if (hasMin) {
|
||||||
|
const minText = `$${formatMoney(minAmount)}`
|
||||||
|
return isEnLocale()
|
||||||
|
? `Min ${minText} · Off ${discountText}`
|
||||||
|
: `满${minText}减${discountText}`
|
||||||
|
}
|
||||||
|
return isEnLocale() ? `Off ${discountText}` : `立减${discountText}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 1) {
|
||||||
|
const pct = Number.isFinite(discount) ? Number(discount * 100).toFixed(0) : '0'
|
||||||
|
return `${pct}% ${t('pages-store.store.discount')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return t('pages-store.store.discount')
|
||||||
|
}
|
||||||
|
|
||||||
function confirmCoupon(item: any) {
|
function confirmCoupon(item: any) {
|
||||||
eventChannel.emit('selectedCoupon', item);
|
eventChannel.emit('selectedCoupon', item);
|
||||||
@@ -72,13 +108,7 @@ function confirmCoupon(item: any) {
|
|||||||
class="w-36rpx h-36rpx shrink-0 mr-10rpx"
|
class="w-36rpx h-36rpx shrink-0 mr-10rpx"
|
||||||
src="@img/chef/106.png"
|
src="@img/chef/106.png"
|
||||||
></image>
|
></image>
|
||||||
<template v-if="item.snapshotType === 2">
|
{{ formatCouponDetail(item) }}
|
||||||
<!-- 满减-->
|
|
||||||
{{ item.snapshotDiscount }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ Number(item.snapshotDiscount * 100).toFixed(0) }}%
|
|
||||||
</template>{{ t('pages-store.store.discount') }}
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view @click="confirmCoupon(item)" class="h-84rpx lh-84rpx text-center text-28rpx text-#fff">
|
<view @click="confirmCoupon(item)" class="h-84rpx lh-84rpx text-center text-28rpx text-#fff">
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ function appMembershipRechargeItem() {
|
|||||||
<template v-if="!userStore.userInfo.userMembershipVo">
|
<template v-if="!userStore.userInfo.userMembershipVo">
|
||||||
<!-- <text class="ml-20rpx text-#CE7138">{{ membershipRechargeItem.trialWeeksDisplay || 0 }} {{ t('pages-user.member.weeks') }}</text>
|
<!-- <text class="ml-20rpx text-#CE7138">{{ membershipRechargeItem.trialWeeksDisplay || 0 }} {{ t('pages-user.member.weeks') }}</text>
|
||||||
<text class="ml-20rpx text-#CE7138">{{ t('pages-user.member.free') }}</text> -->
|
<text class="ml-20rpx text-#CE7138">{{ t('pages-user.member.free') }}</text> -->
|
||||||
<text class="ml-20rpx text-#CE7138">Free for 7 days</text>
|
<!-- <text class="ml-20rpx text-#CE7138">Free for 7 days</text> -->
|
||||||
<!-- <image :src="membershipRechargeItem.image"></image> -->
|
<!-- <image :src="membershipRechargeItem.image"></image> -->
|
||||||
</template>
|
</template>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -52,14 +52,22 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Config from "@/config";
|
import Config from "@/config";
|
||||||
import {EventEnum} from "@/constant/enums";
|
|
||||||
import {useLogicStore} from "@/pages-user/store/logic";
|
import {useLogicStore} from "@/pages-user/store/logic";
|
||||||
import {useUserStore} from "@/store";
|
import {useUserStore} from "@/store";
|
||||||
|
import { getCurrentInstance } from "vue";
|
||||||
|
|
||||||
const {t, locale} = useI18n();
|
const {t, locale} = useI18n();
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const logicStore = useLogicStore()
|
const logicStore = useLogicStore()
|
||||||
|
|
||||||
|
let openerEventChannel: any = null
|
||||||
|
onLoad(() => {
|
||||||
|
// 统一通过 getOpenerEventChannel 获取导航传入的 eventChannel
|
||||||
|
const pages = getCurrentPages() as any[]
|
||||||
|
const page = pages[pages.length - 1]
|
||||||
|
openerEventChannel = page?.getOpenerEventChannel?.() || null
|
||||||
|
})
|
||||||
|
|
||||||
// 生命周期:清空地址列表
|
// 生命周期:清空地址列表
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
logicStore.clearPlacesList()
|
logicStore.clearPlacesList()
|
||||||
@@ -99,7 +107,7 @@ const querySearch = computed(() => {
|
|||||||
|
|
||||||
function handleClickLocation(item: any) {
|
function handleClickLocation(item: any) {
|
||||||
console.log('item', item)
|
console.log('item', item)
|
||||||
uni.$emit(EventEnum.CHOOSE_ADDRESS, item)
|
openerEventChannel?.emit?.('chooseAddress', item)
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +138,7 @@ function handleUseLocation() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
uni.$emit(EventEnum.CHOOSE_ADDRESS, {
|
openerEventChannel?.emit?.('chooseAddress', {
|
||||||
displayName: userStore.location.location,
|
displayName: userStore.location.location,
|
||||||
formattedAddress: userStore.location.formattedAddress || '',
|
formattedAddress: userStore.location.formattedAddress || '',
|
||||||
location: {
|
location: {
|
||||||
@@ -192,8 +200,8 @@ function getCityName(latitude: number, longitude: number) {
|
|||||||
latitude
|
latitude
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发事件
|
// 回传上一页(eventChannel)
|
||||||
uni.$emit(EventEnum.CHOOSE_ADDRESS, {
|
openerEventChannel?.emit?.('chooseAddress', {
|
||||||
displayName: cityName,
|
displayName: cityName,
|
||||||
formattedAddress,
|
formattedAddress,
|
||||||
location: { lng: longitude, lat: latitude }
|
location: { lng: longitude, lat: latitude }
|
||||||
@@ -220,69 +228,65 @@ const mapDataComputed = computed(() => {
|
|||||||
lat: userStore.location.latitude || -36.8485,
|
lat: userStore.location.latitude || -36.8485,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
</script>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { useLogicStore } from "@/pages-user/store/logic";
|
||||||
|
|
||||||
// 监听来自 renderjs 的事件
|
export default defineComponent({
|
||||||
onMounted(() => {
|
methods: {
|
||||||
// 监听搜索结果
|
onMapSearchResult(places: any) {
|
||||||
uni.$on('MAP_SEARCH_RESULT', (places: any) => {
|
const logicStore = useLogicStore()
|
||||||
console.log(places, '接收到的搜索结果')
|
console.log(places, '接收到的搜索结果')
|
||||||
if (places && places.length > 0) {
|
|
||||||
// 解析实际返回的数据结构
|
|
||||||
const parsedPlaces = places.map((placeArray: any) => {
|
|
||||||
const placeData = placeArray[0]?.[0];
|
|
||||||
if (!placeData) return null;
|
|
||||||
|
|
||||||
const placeId = placeData[1];
|
if (places && places.length > 0) {
|
||||||
const formattedAddress = placeData[8] || '';
|
// 保持与原先一致的解析逻辑
|
||||||
const locationArray = placeData[11];
|
const parsedPlaces = places
|
||||||
const displayNameArray = placeData[27];
|
.map((placeArray: any) => {
|
||||||
|
const placeData = placeArray[0]?.[0]
|
||||||
|
if (!placeData) return null
|
||||||
|
|
||||||
return {
|
const placeId = placeData[1]
|
||||||
id: placeId,
|
const formattedAddress = placeData[8] || ''
|
||||||
displayName: displayNameArray?.[0] || formattedAddress,
|
const locationArray = placeData[11]
|
||||||
formattedAddress: formattedAddress,
|
const displayNameArray = placeData[27]
|
||||||
location: locationArray ? {
|
|
||||||
lat: locationArray[0],
|
|
||||||
lng: locationArray[1]
|
|
||||||
} : null
|
|
||||||
};
|
|
||||||
}).filter(Boolean);
|
|
||||||
|
|
||||||
console.log('解析后的地址列表', parsedPlaces);
|
return {
|
||||||
logicStore.setPlacesList(parsedPlaces);
|
id: placeId,
|
||||||
} else {
|
displayName: displayNameArray?.[0] || formattedAddress,
|
||||||
logicStore.setPlacesList([]);
|
formattedAddress: formattedAddress,
|
||||||
}
|
location: locationArray
|
||||||
})
|
? {
|
||||||
|
lat: locationArray[0],
|
||||||
|
lng: locationArray[1],
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
// 监听地图未加载提示
|
console.log('解析后的地址列表', parsedPlaces)
|
||||||
uni.$on('MAP_NOT_LOADED', () => {
|
logicStore.setPlacesList(parsedPlaces)
|
||||||
uni.showToast({
|
} else {
|
||||||
title: 'Map is not loaded yet, please wait',
|
logicStore.setPlacesList([])
|
||||||
icon: 'none'
|
}
|
||||||
});
|
},
|
||||||
})
|
onMapNotLoaded() {
|
||||||
|
uni.showToast({
|
||||||
// 监听搜索超时提示
|
title: 'Map is not loaded yet, please wait',
|
||||||
uni.$on('MAP_SEARCH_TIMEOUT', () => {
|
icon: 'none',
|
||||||
uni.showToast({
|
})
|
||||||
title: 'Search timeout, please try again',
|
},
|
||||||
icon: 'none'
|
onMapSearchTimeout() {
|
||||||
});
|
uni.showToast({
|
||||||
})
|
title: 'Search timeout, please try again',
|
||||||
|
icon: 'none',
|
||||||
// 监听搜索加载状态
|
})
|
||||||
uni.$on('MAP_SEARCH_LOADING', (isLoading: boolean) => {
|
},
|
||||||
// logicStore.searchLoading = isLoading;
|
onMapSearchLoading(_isLoading: boolean) {
|
||||||
})
|
// 如需联动 loading,可在此处驱动 store
|
||||||
})
|
},
|
||||||
|
},
|
||||||
onUnmounted(() => {
|
|
||||||
// 清理事件监听
|
|
||||||
uni.$off('MAP_SEARCH_RESULT')
|
|
||||||
uni.$off('MAP_NOT_LOADED')
|
|
||||||
uni.$off('MAP_SEARCH_TIMEOUT')
|
|
||||||
uni.$off('MAP_SEARCH_LOADING')
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<script lang="renderjs" module="mapRenderjs">
|
<script lang="renderjs" module="mapRenderjs">
|
||||||
@@ -377,24 +381,24 @@ export default {
|
|||||||
async searchPlace({keyword}) {
|
async searchPlace({keyword}) {
|
||||||
console.log('搜索关键词', keyword);
|
console.log('搜索关键词', keyword);
|
||||||
if (!keyword) {
|
if (!keyword) {
|
||||||
uni.$emit('MAP_SEARCH_RESULT', [])
|
this.$ownerInstance.callMethod('onMapSearchResult', [])
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!mapLoaded) {
|
if (!mapLoaded) {
|
||||||
console.log('地图未加载完成,无法搜索');
|
console.log('地图未加载完成,无法搜索');
|
||||||
uni.$emit('MAP_NOT_LOADED');
|
this.$ownerInstance.callMethod('onMapNotLoaded');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!map || !this.google) {
|
if (!map || !this.google) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
uni.$emit('MAP_SEARCH_LOADING', true);
|
this.$ownerInstance.callMethod('onMapSearchLoading', true);
|
||||||
let timeoutId;
|
let timeoutId;
|
||||||
try {
|
try {
|
||||||
// 设置搜索超时
|
// 设置搜索超时
|
||||||
const timeoutPromise = new Promise((_, reject) => {
|
const timeoutPromise = new Promise((_, reject) => {
|
||||||
timeoutId = setTimeout(() => {
|
timeoutId = setTimeout(() => {
|
||||||
uni.$emit('MAP_SEARCH_LOADING', false);
|
this.$ownerInstance.callMethod('onMapSearchLoading', false);
|
||||||
reject(new Error('Search timeout'));
|
reject(new Error('Search timeout'));
|
||||||
}, 10000); // 10 秒超时
|
}, 10000); // 10 秒超时
|
||||||
});
|
});
|
||||||
@@ -430,16 +434,16 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
console.log('序列化后的搜索结果', serializedPlaces);
|
console.log('序列化后的搜索结果', serializedPlaces);
|
||||||
uni.$emit('MAP_SEARCH_RESULT', serializedPlaces);
|
this.$ownerInstance.callMethod('onMapSearchResult', serializedPlaces);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('搜索错误原因', e);
|
console.log('搜索错误原因', e);
|
||||||
if (e.message === 'Search timeout') {
|
if (e.message === 'Search timeout') {
|
||||||
uni.$emit('MAP_SEARCH_TIMEOUT');
|
this.$ownerInstance.callMethod('onMapSearchTimeout');
|
||||||
}
|
}
|
||||||
uni.$emit('MAP_SEARCH_RESULT', []);
|
this.$ownerInstance.callMethod('onMapSearchResult', []);
|
||||||
} finally {
|
} finally {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
uni.$emit('MAP_SEARCH_LOADING', false);
|
this.$ownerInstance.callMethod('onMapSearchLoading', false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+18
-51
@@ -14,60 +14,27 @@ const message = useMessage();
|
|||||||
function handleClickSearch() {
|
function handleClickSearch() {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages-user/pages/search-address/index',
|
url: '/pages-user/pages/search-address/index',
|
||||||
|
events: {
|
||||||
|
chooseAddress: (data: any) => {
|
||||||
|
console.log('搜索的地址信息', data)
|
||||||
|
if (data) {
|
||||||
|
addressStore.setAddressLocation({
|
||||||
|
displayName: data.displayName,
|
||||||
|
formattedAddress: data.formattedAddress,
|
||||||
|
longitude: data.location.lng,
|
||||||
|
latitude: data.location.lat
|
||||||
|
})
|
||||||
|
setTimeout(()=> {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/address/choose-type'
|
||||||
|
})
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEventEmit(EventEnum.CHOOSE_ADDRESS, (data) => {
|
|
||||||
console.log('搜索的地址信息', data)
|
|
||||||
if(data) {
|
|
||||||
// 从addressComponents中提取州名/省名
|
|
||||||
// let stateName = '';
|
|
||||||
// if (data.addressComponents && Array.isArray(data.addressComponents)) {
|
|
||||||
// // 先判断是否为中国地址
|
|
||||||
// const countryComponent = data.addressComponents.find(component =>
|
|
||||||
// component.types && component.types.includes('country')
|
|
||||||
// );
|
|
||||||
// const isChina = countryComponent && (countryComponent.shortText === 'CN' || countryComponent.longText === '中国');
|
|
||||||
//
|
|
||||||
// if (isChina) {
|
|
||||||
// // 中国地址:优先取市级(locality),如果没有则取省级(administrative_area_level_1)
|
|
||||||
// const cityComponent = data.addressComponents.find(component =>
|
|
||||||
// component.types && component.types.includes('locality')
|
|
||||||
// );
|
|
||||||
// const provinceComponent = data.addressComponents.find(component =>
|
|
||||||
// component.types && component.types.includes('administrative_area_level_1')
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// if (cityComponent) {
|
|
||||||
// stateName = cityComponent.longText || cityComponent.shortText || '';
|
|
||||||
// } else if (provinceComponent) {
|
|
||||||
// stateName = provinceComponent.longText || provinceComponent.shortText || '';
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// // 美国等其他国家:取州级(administrative_area_level_1)
|
|
||||||
// const stateComponent = data.addressComponents.find(component =>
|
|
||||||
// component.types && component.types.includes('administrative_area_level_1')
|
|
||||||
// );
|
|
||||||
// if (stateComponent) {
|
|
||||||
// stateName = stateComponent.longText || stateComponent.shortText || '';
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
addressStore.setAddressLocation({
|
|
||||||
displayName: data.displayName,
|
|
||||||
formattedAddress: data.formattedAddress,
|
|
||||||
longitude: data.location.lng,
|
|
||||||
latitude: data.location.lat
|
|
||||||
})
|
|
||||||
setTimeout(()=> {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/address/choose-type'
|
|
||||||
})
|
|
||||||
}, 300)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function reservationTime() {
|
function reservationTime() {
|
||||||
uni.navigateTo({ url: '/pages/address/reservation-time' })
|
uni.navigateTo({ url: '/pages/address/reservation-time' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import dayjs from "dayjs";
|
|||||||
// MONDAY/TUESDAY/WEDNESDAY 09:00-16:00
|
// MONDAY/TUESDAY/WEDNESDAY 09:00-16:00
|
||||||
// MONDAY 09:00-18:00;TUESDAY 09:00-10:00;WEDNESDAY 09:00-18:00;THURSDAY 09:00-18:00;FRIDAY 08:00-09:00
|
// MONDAY 09:00-18:00;TUESDAY 09:00-10:00;WEDNESDAY 09:00-18:00;THURSDAY 09:00-18:00;FRIDAY 08:00-09:00
|
||||||
const storeBusinessHours = ref('');
|
const storeBusinessHours = ref('');
|
||||||
// 是否仅选择日期(当进入页面传递了 storeBusinessHours 时开启)
|
// 是否仅选择日期(当前需求:只选哪一天配送,不选具体时间段)
|
||||||
const onlySelectDay = ref(false);
|
const onlySelectDay = ref(true);
|
||||||
|
|
||||||
// 业务规则:只能预约周一 / 周四 / 周五
|
// 业务规则:只能预约周一 / 周四 / 周五
|
||||||
// JS 中:0-周日 1-周一 ... 4-周四 5-周五
|
// JS 中:0-周日 1-周一 ... 4-周四 5-周五
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ const addressStore = useAddressStore()
|
|||||||
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
||||||
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
||||||
function openVisitMethod() {
|
function openVisitMethod() {
|
||||||
visitMethodRef.value?.onOpen()
|
// deliveryType: 1-亲自送达(0) 2-放门口(1),打开时回显当前选中
|
||||||
|
visitMethodRef.value?.onOpen(+addressStore.addressInfo.deliveryType === 2 ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const visitMethod = ref(t('components.visit.leaveItToMePersonally')) // 默认选择亲自送达
|
const visitMethod = ref(t('components.visit.putItAtTheDoor')) // 默认选择放在门口
|
||||||
function visitMethodConfirm(data: any) {
|
function visitMethodConfirm(data: any) {
|
||||||
console.log('visitMethodConfirm', data)
|
console.log('visitMethodConfirm', data)
|
||||||
visitMethod.value = data.label;
|
visitMethod.value = data.label;
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ const addressStore = useAddressStore()
|
|||||||
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
||||||
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
||||||
function openVisitMethod() {
|
function openVisitMethod() {
|
||||||
visitMethodRef.value?.onOpen()
|
// deliveryType: 1-亲自送达(0) 2-放门口(1),打开时回显当前选中
|
||||||
|
visitMethodRef.value?.onOpen(+addressStore.addressInfo.deliveryType === 2 ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const visitMethod = ref(t('components.visit.leaveItToMePersonally')) // 默认选择亲自送达
|
const visitMethod = ref(t('components.visit.putItAtTheDoor')) // 默认选择放在门口
|
||||||
function visitMethodConfirm(data: any) {
|
function visitMethodConfirm(data: any) {
|
||||||
console.log('visitMethodConfirm', data)
|
console.log('visitMethodConfirm', data)
|
||||||
visitMethod.value = data.label;
|
visitMethod.value = data.label;
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ const addressStore = useAddressStore()
|
|||||||
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
||||||
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
||||||
function openVisitMethod() {
|
function openVisitMethod() {
|
||||||
visitMethodRef.value?.onOpen()
|
// deliveryType: 1-亲自送达(0) 2-放门口(1),打开时回显当前选中
|
||||||
|
visitMethodRef.value?.onOpen(+addressStore.addressInfo.deliveryType === 2 ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const visitMethod = ref(t('components.visit.leaveItToMePersonally')) // 默认选择亲自送达
|
const visitMethod = ref(t('components.visit.putItAtTheDoor')) // 默认选择放在门口
|
||||||
function visitMethodConfirm(data: any) {
|
function visitMethodConfirm(data: any) {
|
||||||
console.log('visitMethodConfirm', data)
|
console.log('visitMethodConfirm', data)
|
||||||
visitMethod.value = data.label;
|
visitMethod.value = data.label;
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ const addressStore = useAddressStore()
|
|||||||
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
||||||
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
||||||
function openVisitMethod() {
|
function openVisitMethod() {
|
||||||
visitMethodRef.value?.onOpen()
|
// deliveryType: 1-亲自送达(0) 2-放门口(1),打开时回显当前选中
|
||||||
|
visitMethodRef.value?.onOpen(+addressStore.addressInfo.deliveryType === 2 ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const visitMethod = ref(t('components.visit.leaveItToMePersonally')) // 默认选择亲自送达
|
const visitMethod = ref(t('components.visit.putItAtTheDoor')) // 默认选择放在门口
|
||||||
function visitMethodConfirm(data: any) {
|
function visitMethodConfirm(data: any) {
|
||||||
console.log('visitMethodConfirm', data)
|
console.log('visitMethodConfirm', data)
|
||||||
visitMethod.value = data.label;
|
visitMethod.value = data.label;
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ const addressStore = useAddressStore()
|
|||||||
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
const visitMethodRef = ref<InstanceType<typeof VisitMethod>>()
|
||||||
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
const buildingTypeRef = ref<InstanceType<typeof BuildingType>>()
|
||||||
function openVisitMethod() {
|
function openVisitMethod() {
|
||||||
visitMethodRef.value?.onOpen()
|
// deliveryType: 1-亲自送达(0) 2-放门口(1),打开时回显当前选中
|
||||||
|
visitMethodRef.value?.onOpen(+addressStore.addressInfo.deliveryType === 2 ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const visitMethod = ref(t('components.visit.leaveItToMePersonally')) // 默认选择亲自送达
|
const visitMethod = ref(t('components.visit.putItAtTheDoor')) // 默认选择放在门口
|
||||||
function visitMethodConfirm(data: any) {
|
function visitMethodConfirm(data: any) {
|
||||||
console.log('visitMethodConfirm', data)
|
console.log('visitMethodConfirm', data)
|
||||||
visitMethod.value = data.label;
|
visitMethod.value = data.label;
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import type { UserAddressBo } from '@/service/types';
|
|||||||
export const useAddressStore = defineStore('store-address', () => {
|
export const useAddressStore = defineStore('store-address', () => {
|
||||||
const addressInfo = ref<UserAddressBo>({
|
const addressInfo = ref<UserAddressBo>({
|
||||||
type: '',
|
type: '',
|
||||||
/** 配送类型 1 2 */
|
/** 配送类型 1-亲自送达 2-放门口,默认放门口 */
|
||||||
deliveryType: '1',
|
deliveryType: '2',
|
||||||
/** 配送说明 */
|
/** 配送说明 */
|
||||||
deliveryRemark: '',
|
deliveryRemark: '',
|
||||||
/** 配送图片 */
|
/** 配送图片 */
|
||||||
@@ -60,8 +60,8 @@ export const useAddressStore = defineStore('store-address', () => {
|
|||||||
addressInfo.value = {
|
addressInfo.value = {
|
||||||
id: '',
|
id: '',
|
||||||
type: '',
|
type: '',
|
||||||
/** 配送类型 1 2 */
|
/** 配送类型 1-亲自送达 2-放门口,默认放门口 */
|
||||||
deliveryType: '1',
|
deliveryType: '2',
|
||||||
/** 配送说明 */
|
/** 配送说明 */
|
||||||
deliveryRemark: '',
|
deliveryRemark: '',
|
||||||
/** 配送图片 */
|
/** 配送图片 */
|
||||||
|
|||||||
@@ -1,34 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="class-bullet-container">
|
<view class="class-bullet-container">
|
||||||
<!-- 第一行(可拖动 + 自动滚动) -->
|
<!-- 第一行:自动缓慢滚动 + 可手动滑动 -->
|
||||||
<view class="scroll-row first-row">
|
<view class="scroll-row first-row">
|
||||||
<scroll-view
|
<scroll-view
|
||||||
class="ab-scroll mb-22rpx"
|
class="ab-scroll mb-22rpx"
|
||||||
scroll-x
|
scroll-x
|
||||||
show-scrollbar="false"
|
show-scrollbar="false"
|
||||||
id="sv-1"
|
|
||||||
style="--ab-scroll-timeout: 50s"
|
|
||||||
:scroll-left="scrollLeft1"
|
:scroll-left="scrollLeft1"
|
||||||
@touchstart="onTouchStart1"
|
@touchstart="onTouchStart1"
|
||||||
@touchend="onTouchEnd1"
|
@touchend="onTouchEnd1"
|
||||||
@touchcancel="onTouchEnd1"
|
@touchcancel="onTouchEnd1"
|
||||||
@scroll="onScroll1"
|
@scroll="onScroll1"
|
||||||
>
|
>
|
||||||
<view id="sv-inner-1" :class="['sv-inner', { 'paused': isDragging1 }]">
|
<view class="sv-inner" id="sv1-inner">
|
||||||
<!-- 一份内容 -->
|
<!-- 一份内容 -->
|
||||||
<view
|
<view
|
||||||
v-for="(item, idx) in categories"
|
v-for="(item, idx) in categories"
|
||||||
:key="item.id + '-a-' + idx"
|
:key="item.id + '-row1-a-' + idx"
|
||||||
class="category-item"
|
class="category-item"
|
||||||
@click="handleItemClick(item)"
|
@click="handleItemClick(item)"
|
||||||
>
|
>
|
||||||
<image v-if="item.categoryImage || item.logoUrl" :src="item.categoryImage || item.logoUrl" class="category-icon" mode="aspectFit" />
|
<image v-if="item.categoryImage || item.logoUrl" :src="item.categoryImage || item.logoUrl" class="category-icon" mode="aspectFit" />
|
||||||
<text class="category-text">{{ item.categoryName || item.name }}</text>
|
<text class="category-text">{{ item.categoryName || item.name }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 第二份重复内容(用于无缝循环) -->
|
<!-- 第二份重复内容,用于无缝滚动 -->
|
||||||
<view
|
<view
|
||||||
v-for="(item, idx) in categories"
|
v-for="(item, idx) in categories"
|
||||||
:key="item.id + '-b-' + idx"
|
:key="item.id + '-row1-b-' + idx"
|
||||||
class="category-item"
|
class="category-item"
|
||||||
@click="handleItemClick(item)"
|
@click="handleItemClick(item)"
|
||||||
>
|
>
|
||||||
@@ -39,24 +37,22 @@
|
|||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 第二行(可拖动 + 自动滚动,速度不同) -->
|
<!-- 第二行:反向数据,自动滚动速度略不同 -->
|
||||||
<view class="scroll-row">
|
<view class="scroll-row">
|
||||||
<scroll-view
|
<scroll-view
|
||||||
class="ab-scroll"
|
class="ab-scroll"
|
||||||
scroll-x
|
scroll-x
|
||||||
show-scrollbar="false"
|
show-scrollbar="false"
|
||||||
id="sv-2"
|
|
||||||
style="--ab-scroll-timeout: 70s"
|
|
||||||
:scroll-left="scrollLeft2"
|
:scroll-left="scrollLeft2"
|
||||||
@touchstart="onTouchStart2"
|
@touchstart="onTouchStart2"
|
||||||
@touchend="onTouchEnd2"
|
@touchend="onTouchEnd2"
|
||||||
@touchcancel="onTouchEnd2"
|
@touchcancel="onTouchEnd2"
|
||||||
@scroll="onScroll2"
|
@scroll="onScroll2"
|
||||||
>
|
>
|
||||||
<view id="sv-inner-2" :class="['sv-inner', { 'paused': isDragging2 }]">
|
<view class="sv-inner" id="sv2-inner">
|
||||||
<view
|
<view
|
||||||
v-for="(item, idx) in categoriesReversed"
|
v-for="(item, idx) in categoriesReversed"
|
||||||
:key="item.id + '-c-' + idx"
|
:key="item.id + '-row2-a-' + idx"
|
||||||
class="category-item"
|
class="category-item"
|
||||||
@click="handleItemClick(item)"
|
@click="handleItemClick(item)"
|
||||||
>
|
>
|
||||||
@@ -65,7 +61,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
v-for="(item, idx) in categoriesReversed"
|
v-for="(item, idx) in categoriesReversed"
|
||||||
:key="item.id + '-d-' + idx"
|
:key="item.id + '-row2-b-' + idx"
|
||||||
class="category-item"
|
class="category-item"
|
||||||
@click="handleItemClick(item)"
|
@click="handleItemClick(item)"
|
||||||
>
|
>
|
||||||
@@ -80,7 +76,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, onMounted, nextTick, getCurrentInstance } from 'vue'
|
import { computed, ref, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
|
|
||||||
// 定义分类项接口(与模板字段一致)
|
// 定义分类项接口(与模板字段一致)
|
||||||
interface CategoryItem {
|
interface CategoryItem {
|
||||||
@@ -88,7 +84,7 @@ interface CategoryItem {
|
|||||||
categoryImage?: string
|
categoryImage?: string
|
||||||
logoUrl?: string
|
logoUrl?: string
|
||||||
name?: string
|
name?: string
|
||||||
categoryName: string
|
categoryName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义组件属性
|
// 定义组件属性
|
||||||
@@ -128,116 +124,121 @@ const categoriesReversed = computed(() => {
|
|||||||
return list.slice().reverse()
|
return list.slice().reverse()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 自动滚动(CSS 动画)+ 手动拖动时暂停
|
// ===== 自动滚动相关(电商标签推荐风格) =====
|
||||||
const isDragging1 = ref(false)
|
// 视口宽度,用于计算最大可滚动距离(scrollWidth - viewportWidth)
|
||||||
const isDragging2 = ref(false)
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
const RESUME_DELAY_MS = 1200
|
const viewportWidth = systemInfo.windowWidth || 0
|
||||||
let resumeTimer1: any
|
|
||||||
let resumeTimer2: any
|
|
||||||
|
|
||||||
// 中心定位与边界重置,避免滚动到底卡住
|
|
||||||
const scrollLeft1 = ref(0)
|
const scrollLeft1 = ref(0)
|
||||||
const scrollLeft2 = ref(0)
|
const scrollLeft2 = ref(0)
|
||||||
const copyWidth1Px = ref(0)
|
|
||||||
const copyWidth2Px = ref(0)
|
|
||||||
const viewportWidth1Px = ref(0)
|
|
||||||
const viewportWidth2Px = ref(0)
|
|
||||||
|
|
||||||
function measureAndInit() {
|
// 最大可滚动距离(由 scroll 事件提供,避免自己算)
|
||||||
const ins = getCurrentInstance()
|
const maxScroll1 = ref(0)
|
||||||
const q = uni.createSelectorQuery().in(ins?.proxy as any)
|
const maxScroll2 = ref(0)
|
||||||
q.select('#sv-1').boundingClientRect()
|
|
||||||
.select('#sv-inner-1').boundingClientRect()
|
const isTouching1 = ref(false)
|
||||||
.select('#sv-2').boundingClientRect()
|
const isTouching2 = ref(false)
|
||||||
.select('#sv-inner-2').boundingClientRect()
|
|
||||||
.exec((res) => {
|
let autoTimer1: any = null
|
||||||
const sv1 = res?.[0]
|
let autoTimer2: any = null
|
||||||
const inner1 = res?.[1]
|
let resumeTimer1: any = null
|
||||||
const sv2 = res?.[2]
|
let resumeTimer2: any = null
|
||||||
const inner2 = res?.[3]
|
|
||||||
if (sv1?.width) viewportWidth1Px.value = sv1.width
|
// 速度和间隔可以根据效果微调
|
||||||
if (inner1?.width) copyWidth1Px.value = inner1.width / 2
|
const AUTO_INTERVAL = 30
|
||||||
if (sv2?.width) viewportWidth2Px.value = sv2.width
|
const AUTO_STEP_1 = 0.6
|
||||||
if (inner2?.width) copyWidth2Px.value = inner2.width / 2
|
const AUTO_STEP_2 = 0.4
|
||||||
// 初始化到中间位置
|
const RESUME_DELAY = 800
|
||||||
if (copyWidth1Px.value) scrollLeft1.value = copyWidth1Px.value
|
const RESET_THRESHOLD = 2 // 在距离末尾一定范围内就重置
|
||||||
if (copyWidth2Px.value) scrollLeft2.value = copyWidth2Px.value
|
|
||||||
})
|
function startAuto1() {
|
||||||
|
if (autoTimer1) return
|
||||||
|
autoTimer1 = setInterval(() => {
|
||||||
|
if (isTouching1.value) return
|
||||||
|
let next = scrollLeft1.value + AUTO_STEP_1
|
||||||
|
const max = maxScroll1.value
|
||||||
|
if (max > 0 && next >= max - RESET_THRESHOLD) {
|
||||||
|
// 距离最右侧很近时,从头开始,实现循环
|
||||||
|
next = 0
|
||||||
|
}
|
||||||
|
scrollLeft1.value = next
|
||||||
|
}, AUTO_INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
function recenter1(currentLeft?: number) {
|
function startAuto2() {
|
||||||
const cw = copyWidth1Px.value
|
if (autoTimer2) return
|
||||||
const vw = viewportWidth1Px.value
|
autoTimer2 = setInterval(() => {
|
||||||
if (!cw || !vw) return
|
if (isTouching2.value) return
|
||||||
const left = currentLeft ?? scrollLeft1.value
|
let next = scrollLeft2.value + AUTO_STEP_2
|
||||||
const mod = left % cw
|
const max = maxScroll2.value
|
||||||
scrollLeft1.value = cw + mod
|
if (max > 0 && next >= max - RESET_THRESHOLD) {
|
||||||
|
next = 0
|
||||||
|
}
|
||||||
|
scrollLeft2.value = next
|
||||||
|
}, AUTO_INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
function recenter2(currentLeft?: number) {
|
function stopAuto1() {
|
||||||
const cw = copyWidth2Px.value
|
if (autoTimer1) {
|
||||||
const vw = viewportWidth2Px.value
|
clearInterval(autoTimer1)
|
||||||
if (!cw || !vw) return
|
autoTimer1 = null
|
||||||
const left = currentLeft ?? scrollLeft2.value
|
}
|
||||||
const mod = left % cw
|
}
|
||||||
scrollLeft2.value = cw + mod
|
|
||||||
|
function stopAuto2() {
|
||||||
|
if (autoTimer2) {
|
||||||
|
clearInterval(autoTimer2)
|
||||||
|
autoTimer2 = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTouchStart1() {
|
function onTouchStart1() {
|
||||||
isDragging1.value = true
|
isTouching1.value = true
|
||||||
if (resumeTimer1) clearTimeout(resumeTimer1)
|
if (resumeTimer1) clearTimeout(resumeTimer1)
|
||||||
|
stopAuto1()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTouchEnd1() {
|
function onTouchEnd1() {
|
||||||
// 手指松开后,若在边界附近,立即重置到中间位置,保持无缝
|
|
||||||
recenter1(scrollLeft1.value)
|
|
||||||
resumeTimer1 = setTimeout(() => {
|
resumeTimer1 = setTimeout(() => {
|
||||||
isDragging1.value = false
|
isTouching1.value = false
|
||||||
}, RESUME_DELAY_MS)
|
startAuto1()
|
||||||
|
}, RESUME_DELAY)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScroll1(e: any) {
|
function onScroll1(e: any) {
|
||||||
isDragging1.value = true
|
const detail = e?.detail
|
||||||
if (resumeTimer1) clearTimeout(resumeTimer1)
|
if (detail && typeof detail.scrollLeft === 'number') {
|
||||||
const left = e?.detail?.scrollLeft ?? 0
|
scrollLeft1.value = detail.scrollLeft
|
||||||
const cw = copyWidth1Px.value
|
// scrollWidth - viewportWidth 为最大可滚动距离
|
||||||
const vw = viewportWidth1Px.value
|
if (typeof detail.scrollWidth === 'number' && viewportWidth > 0) {
|
||||||
if (cw && vw) {
|
const max = detail.scrollWidth - viewportWidth
|
||||||
const total = cw * 2
|
maxScroll1.value = max > 0 ? max : 0
|
||||||
// 临界阈值,靠近起点或终点时重置
|
|
||||||
const threshold = 10
|
|
||||||
if (left <= threshold || left >= total - vw - threshold) {
|
|
||||||
recenter1(left)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resumeTimer1 = setTimeout(() => {
|
|
||||||
isDragging1.value = false
|
|
||||||
}, RESUME_DELAY_MS)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTouchStart2() {
|
function onTouchStart2() {
|
||||||
isDragging2.value = true
|
isTouching2.value = true
|
||||||
if (resumeTimer2) clearTimeout(resumeTimer2)
|
if (resumeTimer2) clearTimeout(resumeTimer2)
|
||||||
|
stopAuto2()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTouchEnd2() {
|
function onTouchEnd2() {
|
||||||
recenter2(scrollLeft2.value)
|
|
||||||
resumeTimer2 = setTimeout(() => {
|
resumeTimer2 = setTimeout(() => {
|
||||||
isDragging2.value = false
|
isTouching2.value = false
|
||||||
}, RESUME_DELAY_MS)
|
startAuto2()
|
||||||
|
}, RESUME_DELAY)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScroll2(e: any) {
|
function onScroll2(e: any) {
|
||||||
isDragging2.value = true
|
const detail = e?.detail
|
||||||
if (resumeTimer2) clearTimeout(resumeTimer2)
|
if (detail && typeof detail.scrollLeft === 'number') {
|
||||||
const left = e?.detail?.scrollLeft ?? 0
|
scrollLeft2.value = detail.scrollLeft
|
||||||
const cw = copyWidth2Px.value
|
if (typeof detail.scrollWidth === 'number' && viewportWidth > 0) {
|
||||||
const vw = viewportWidth2Px.value
|
const max = detail.scrollWidth - viewportWidth
|
||||||
if (cw && vw) {
|
maxScroll2.value = max > 0 ? max : 0
|
||||||
const total = cw * 2
|
|
||||||
const threshold = 10
|
|
||||||
if (left <= threshold || left >= total - vw - threshold) {
|
|
||||||
recenter2(left)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resumeTimer2 = setTimeout(() => {
|
|
||||||
isDragging2.value = false
|
|
||||||
}, RESUME_DELAY_MS)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 点击处理
|
// 点击处理
|
||||||
@@ -252,9 +253,20 @@ function navigateTo(url: string) {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
measureAndInit()
|
// 启动自动滚动,maxScroll 值会在第一次 scroll 事件时更新
|
||||||
|
setTimeout(() => {
|
||||||
|
startAuto1()
|
||||||
|
startAuto2()
|
||||||
|
}, 300)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopAuto1()
|
||||||
|
stopAuto2()
|
||||||
|
if (resumeTimer1) clearTimeout(resumeTimer1)
|
||||||
|
if (resumeTimer2) clearTimeout(resumeTimer2)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -280,8 +292,6 @@ onMounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 20rpx;
|
gap: 20rpx;
|
||||||
animation: ab-move-marquee var(--ab-scroll-timeout) linear infinite;
|
|
||||||
will-change: transform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-item {
|
.category-item {
|
||||||
@@ -316,17 +326,5 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GPU 动画,无缝循环(两份内容)
|
// 取消 CSS 动画,由 JS 控制的 scroll-left 实现无缝滚动,避免与手势滚动冲突导致的抖动
|
||||||
@keyframes ab-move-marquee {
|
|
||||||
0% {
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.paused {
|
|
||||||
animation-play-state: paused;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function handleClickFood(item: any) {
|
|||||||
<view class="text-24rpx lh-24rpx flex items-center mt-12rpx">
|
<view class="text-24rpx lh-24rpx flex items-center mt-12rpx">
|
||||||
<text class="text-#333 font-500">{{ item.rating }}</text>
|
<text class="text-#333 font-500">{{ item.rating }}</text>
|
||||||
<image src="@img/chef/124.png" class="w-24rpx h-24rpx mx-4rpx mt-2rpx"></image>
|
<image src="@img/chef/124.png" class="w-24rpx h-24rpx mx-4rpx mt-2rpx"></image>
|
||||||
<text class="text-#7D7D7D">({{ item.commentCount }}) • {{ item.deliveryTime }} {{ t('common.minutes') }}</text>
|
<text class="text-#7D7D7D">({{ item.commentCount }}) • {{ item.deliveryTime }} {{ Number(item.deliveryTime) === 1 ? t('common.day') : t('common.days') }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -7,12 +7,24 @@
|
|||||||
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||||
-->
|
-->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
// @ts-ignore - Vue SFC default export is provided by tooling
|
||||||
import Collection from "@/components/collection/index.vue";
|
import Collection from "@/components/collection/index.vue";
|
||||||
import {appCollectCollectPost} from "@/service";
|
import {appCollectCollectPost} from "@/service";
|
||||||
import {CollectionType} from "@/constant/enums";
|
import {CollectionType} from "@/constant/enums";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
type FoodBoxItem = {
|
||||||
|
id: number | string;
|
||||||
|
merchantId?: string;
|
||||||
|
isCollect?: boolean;
|
||||||
|
dishImage?: string;
|
||||||
|
logo?: string;
|
||||||
|
dishName?: string;
|
||||||
|
merchantName?: string;
|
||||||
|
originalPrice?: string | number;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
item: object;
|
item: FoodBoxItem;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function handleClickFood() {
|
function handleClickFood() {
|
||||||
@@ -34,10 +46,11 @@ if(props.item.merchantId){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleCollectionChange(value: boolean) {
|
function handleCollectionChange(value: boolean) {
|
||||||
|
const isDish = !!props.item?.merchantId;
|
||||||
appCollectCollectPost({
|
appCollectCollectPost({
|
||||||
body: {
|
body: {
|
||||||
targetId: props.item.id,
|
targetId: String(props.item.id),
|
||||||
targetType: CollectionType.STORE
|
targetType: isDish ? CollectionType.DISH : CollectionType.STORE
|
||||||
}
|
}
|
||||||
}).then(res=> {
|
}).then(res=> {
|
||||||
props.item.isCollect = value;
|
props.item.isCollect = value;
|
||||||
@@ -69,7 +82,7 @@ function handleCollectionChange(value: boolean) {
|
|||||||
<!-- <text class="text-#7D7D7D">({{ item.commentCount }}) • {{ item.deliveryTime }}{{ t('common.minutes') }}</text> -->
|
<!-- <text class="text-#7D7D7D">({{ item.commentCount }}) • {{ item.deliveryTime }}{{ t('common.minutes') }}</text> -->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<collection :is-collected="item.isCollect" @collectionChange="handleCollectionChange" />
|
<collection :is-collected="item.isCollect" @collection-change="handleCollectionChange" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ function handleClickFood(item: any) {
|
|||||||
<view class="w-156rpx text-center line-clamp-1 text-#333 text-28rpx lh-28rpx mt-12rpx font-500">
|
<view class="w-156rpx text-center line-clamp-1 text-#333 text-28rpx lh-28rpx mt-12rpx font-500">
|
||||||
{{ item.merchantName }}
|
{{ item.merchantName }}
|
||||||
</view>
|
</view>
|
||||||
<view v-if="+item.deliveryService === 1" class="mt-12rpx text-center text-24rpx lh-24rpx text-#7D7D7D">{{ item.deliveryTime }}{{ t('common.minutes') }}</view>
|
<view v-if="+item.deliveryService === 1" class="mt-12rpx text-center text-24rpx lh-24rpx text-#7D7D7D">{{ item.deliveryTime }}{{ Number(item.deliveryTime) === 1 ? t('common.day') : t('common.days') }}</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<view class="shrink-0 w-30rpx op-0">1</view>
|
<view class="shrink-0 w-30rpx op-0">1</view>
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: Co
|
|||||||
<view class="flex-center-sb mt-12rpx">
|
<view class="flex-center-sb mt-12rpx">
|
||||||
<view class="text-28rpx text-#999">
|
<view class="text-28rpx text-#999">
|
||||||
<view class="line-through">US${{ item?.originalPrice }}</view>
|
<view class="line-through">US${{ item?.originalPrice }}</view>
|
||||||
<!-- <view>{{ t('pages-store.store.sales') }}:{{ item?.salesCount }}</view> -->
|
<view>{{ t('pages-store.store.sales') }}:{{ item?.salesCount }}</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="center w-64rpx h-64rpx rounded-50% bg-white shadow-lg">
|
<view class="center w-64rpx h-64rpx rounded-50% bg-white shadow-lg">
|
||||||
|
|||||||
@@ -36,7 +36,16 @@ import {getCouponReceiveListApi, receiveAllCouponApi} from "@/pages-user/service
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const { t } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
|
|
||||||
|
function daySuffix(value: unknown) {
|
||||||
|
const n = Number(value)
|
||||||
|
const isEn = String(locale.value || '').toLowerCase().startsWith('en')
|
||||||
|
if (isEn) {
|
||||||
|
return n === 1 ? ` ${t('pages-store.store.day')}` : ` ${t('pages-store.store.days')}`
|
||||||
|
}
|
||||||
|
return t('pages-store.store.days')
|
||||||
|
}
|
||||||
const tabbarHomeRef = ref<InstanceType<typeof TabbarHome>>();
|
const tabbarHomeRef = ref<InstanceType<typeof TabbarHome>>();
|
||||||
const tabbarShopRef = ref<InstanceType<typeof TabbarShop>>();
|
const tabbarShopRef = ref<InstanceType<typeof TabbarShop>>();
|
||||||
const tabbarOrderRef = ref<InstanceType<typeof TabbarOrder>>();
|
const tabbarOrderRef = ref<InstanceType<typeof TabbarOrder>>();
|
||||||
@@ -614,7 +623,7 @@ function getInviteInfo() {
|
|||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="mt-4rpx text-24rpx text-#a0611d">
|
<view class="mt-4rpx text-24rpx text-#a0611d">
|
||||||
{{ t('pages-store.store.validDays') }}: {{ item.validDays }} {{ t('pages-store.store.days') }}</view>
|
{{ t('pages-store.store.validDays') }}: {{ item.validDays }}{{ daySuffix(item.validDays) }}</view>
|
||||||
<view class="mt-8rpx text-24rpx text-#a0611d">
|
<view class="mt-8rpx text-24rpx text-#a0611d">
|
||||||
{{ item?.merchantVo?.merchantName }}
|
{{ item?.merchantVo?.merchantName }}
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ function handleSubmitCollectRecipe(item: any) {
|
|||||||
const collectRecipe = debounce(1000, (item: any) => {
|
const collectRecipe = debounce(1000, (item: any) => {
|
||||||
appCollectCollectPost({
|
appCollectCollectPost({
|
||||||
body: {
|
body: {
|
||||||
targetId: item.id,
|
targetId: String(item.id),
|
||||||
targetType: CollectionType.RECIPE
|
targetType: CollectionType.RECIPE
|
||||||
}
|
}
|
||||||
}).then(res=> {
|
}).then(res=> {
|
||||||
|
|||||||
@@ -290,7 +290,7 @@
|
|||||||
|
|
||||||
export type AppMerchantOrderCanUseCouponListmerchantIdGetParams =
|
export type AppMerchantOrderCanUseCouponListmerchantIdGetParams =
|
||||||
{
|
{
|
||||||
'merchantId': number;
|
'merchantId': any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -5609,8 +5609,8 @@
|
|||||||
'userId'?: number;
|
'userId'?: number;
|
||||||
/** 用户类型 */
|
/** 用户类型 */
|
||||||
'userPort'?: number;
|
'userPort'?: number;
|
||||||
/** 收藏对象ID */
|
/** 收藏对象ID(建议传字符串避免大数四舍五入) */
|
||||||
'targetId'?: number;
|
'targetId'?: number | string;
|
||||||
/** 收藏对象类型(1-菜谱 2-菜品 3-配菜) */
|
/** 收藏对象类型(1-菜谱 2-菜品 3-配菜) */
|
||||||
'targetType'?: number;
|
'targetType'?: number;
|
||||||
/** 1-正常 2-删除 3-禁用 */
|
/** 1-正常 2-删除 3-禁用 */
|
||||||
|
|||||||
Reference in New Issue
Block a user