Merge branch 'feat/appointment-goods'

This commit is contained in:
pikachu1995@126.com
2026-09-30 16:51:04 +08:00
19 changed files with 2010 additions and 1844 deletions

View File

@@ -18,7 +18,6 @@
v-model="bindForm.cardSecret"
border="none"
maxlength="128"
password
placeholder="请输入兑换码(卡密)"
clearable
/>

View File

@@ -20,6 +20,9 @@
padding: 2rpx 20rpx;
}
.list {
padding-bottom: calc(140rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
.item:hover {
background: #ededed;
}
@@ -115,21 +118,28 @@
}
}
.btn-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
padding: 20rpx 30rpx calc(20rpx + constant(safe-area-inset-bottom));
padding: 20rpx 30rpx calc(20rpx + env(safe-area-inset-bottom));
background: $page-color-base;
box-sizing: border-box;
}
.btn {
background: $light-color;
position: fixed;
width: 690rpx;
bottom: calc(60rpx + constant(safe-area-inset-bottom));
bottom: calc(60rpx + env(safe-area-inset-bottom));
width: 100%;
height: 80rpx;
left: 30rpx;
font-size: 30rpx;
color: #fff;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 99;
overflow: hidden;
.btn-text {

View File

@@ -1,138 +1,135 @@
<template>
<view class="address">
<u-empty class="empty" v-if="addressList.length == 0" text="暂无收货地址" mode="address"></u-empty>
<view class="list" v-else>
<view class="item c-content" v-for="(item, index) in addressList" :key="index">
<view class="basic" @click="selectAddressData(item)">
<text>{{ item.name }}</text>
<text>{{ item.mobile }}</text>
<text class="default" v-show="item.isDefault">默认</text>
<view>
<div class="region">
<span v-if="item.consigneeAddressPath[0]">{{item.consigneeAddressPath[0]}}</span>
<span v-if="item.consigneeAddressPath[1]">{{item.consigneeAddressPath[1]}}</span>
<span v-if="item.consigneeAddressPath[2]">{{item.consigneeAddressPath[2]}}</span>
<span v-if="item.consigneeAddressPath[3]">{{item.consigneeAddressPath[3]}}</span>
<span>{{ item.detail }}</span>
</div>
</view>
</view>
<view class="edit">
<view class="relative" @click="setDefault(item)">
<view v-if="item.isDefault" class="alifont icon-xuanzhong"></view>
<text v-else class="unchecked"></text>
<text>{{ item.isDefault ? "默认地址" : "设为默认" }}</text>
</view>
<view class="relative">
<view class="alifont icon-bianji-copy"></view>
<text class="mr-40" @click="addAddress(item.id)">编辑</text>
<view class="alifont icon-lajitong"></view>
<text @click="removeAddress(item.id)">删除</text>
</view>
</view>
</view>
<view style="height: 100px"></view>
</view>
<view class="btn" @click="addAddress('')">
<u-icon name="plus-circle" color="#ffffff" size="18"></u-icon>
<text class="btn-text">添加新收货人</text>
</view>
<u-action-sheet :list="removeList" :tips="tips" v-model:show="showAction" @click="deleteAddressMessage"></u-action-sheet>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onShow, onPullDownRefresh } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Trade from '@/api/trade'
import * as API_Address from '@/api/address.js'
const store = useStore()
const addressList = ref<any[]>([])
const showAction = ref(false)
const removeList = [{ text: '确定' }]
const tips = { text: '确定要删除该收货人信息吗?' }
const removeId = ref('')
const routerVal = ref<Record<string, string>>({})
const params = { pageNumber: 1, pageSize: 1000 }
onPullDownRefresh(() => {
addressList.value = []
getAddressList()
})
onLoad((val) => {
routerVal.value = val || {}
})
onShow(() => {
addressList.value = []
getAddressList()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
async function selectAddressData(val: any) {
await API_Trade.setAddressId(val.id, routerVal.value.way)
uni.navigateBack({ delta: 1 })
}
function getAddressList() {
uni.showLoading()
API_Address.getAddressList(params.pageNumber, params.pageSize).then((res) => {
res.data.result.records.forEach((item: any) => {
item.consigneeAddressPath = item.consigneeAddressPath.split(',')
})
addressList.value = res.data.result.records
hideLoadingIfNeeded()
})
}
function removeAddress(id: string) {
removeId.value = id
showAction.value = true
}
function deleteAddressMessage() {
API_Address.deleteAddress(removeId.value).then((res) => {
if (res.statusCode == 200) {
uni.showToast({ icon: 'none', title: '删除成功' })
getAddressList()
} else {
uni.showToast({ icon: 'none', title: res.data.message, duration: 2000 })
}
})
}
function addAddress(id: string) {
if (id) {
uni.navigateTo({
url: `/pages/mine/address/add?id=${id}&way=${routerVal.value.way}&type=order`,
})
} else {
uni.navigateTo({
url: `/pages/mine/address/add?way=${routerVal.value.way}&type=order`,
})
}
}
function setDefault(item: any) {
delete item.updateBy
delete item.updateTime
delete item.deleteFlag
if (!item.isDefault) item.isDefault = true
API_Address.editAddress(item).then(() => {
uni.showToast({ title: '设置默认地址成功', icon: 'none' })
getAddressList()
})
}
</script>
<style lang="scss" scoped>
@import "./address.scss";
</style>
<template>
<view class="address">
<u-empty class="empty" v-if="addressList.length == 0" text="暂无收货地址" mode="address"></u-empty>
<view class="list" v-else>
<view class="item c-content" v-for="(item, index) in addressList" :key="index">
<view class="basic" @click="selectAddressData(item)">
<text>{{ item.name }}</text>
<text>{{ item.mobile }}</text>
<text class="default" v-show="item.isDefault">默认</text>
<view>
<div class="region">
<span v-if="item.consigneeAddressPath[0]">{{item.consigneeAddressPath[0]}}</span>
<span v-if="item.consigneeAddressPath[1]">{{item.consigneeAddressPath[1]}}</span>
<span v-if="item.consigneeAddressPath[2]">{{item.consigneeAddressPath[2]}}</span>
<span v-if="item.consigneeAddressPath[3]">{{item.consigneeAddressPath[3]}}</span>
<span>{{ item.detail }}</span>
</div>
</view>
</view>
<view class="edit">
<view class="relative" @click="setDefault(item)">
<view v-if="item.isDefault" class="alifont icon-xuanzhong"></view>
<text v-else class="unchecked"></text>
<text>{{ item.isDefault ? "默认地址" : "设为默认" }}</text>
</view>
<view class="relative">
<view class="alifont icon-bianji-copy"></view>
<text class="mr-40" @click="addAddress(item.id)">编辑</text>
<view class="alifont icon-lajitong" @click.stop="removeAddress(item.id)"></view>
<text @click.stop="removeAddress(item.id)">删除</text>
</view>
</view>
</view>
<view style="height: 100px"></view>
</view>
<view class="btn" @click="addAddress('')">
<u-icon name="plus-circle" color="#ffffff" size="18"></u-icon>
<text class="btn-text">添加新收货人</text>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onShow, onPullDownRefresh } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Trade from '@/api/trade'
import * as API_Address from '@/api/address.js'
const store = useStore()
const addressList = ref<any[]>([])
const routerVal = ref<Record<string, string>>({})
const params = { pageNumber: 1, pageSize: 1000 }
onPullDownRefresh(() => {
addressList.value = []
getAddressList()
})
onLoad((val) => {
routerVal.value = val || {}
})
onShow(() => {
addressList.value = []
getAddressList()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
async function selectAddressData(val: any) {
await API_Trade.setAddressId(val.id, routerVal.value.way)
uni.navigateBack({ delta: 1 })
}
function getAddressList() {
uni.showLoading()
API_Address.getAddressList(params.pageNumber, params.pageSize).then((res) => {
res.data.result.records.forEach((item: any) => {
item.consigneeAddressPath = item.consigneeAddressPath.split(',')
})
addressList.value = res.data.result.records
hideLoadingIfNeeded()
})
}
function removeAddress(id: string) {
uni.showModal({
title: '提示',
content: '确定要删除该收货人信息吗?',
success: (res) => {
if (!res.confirm) return
API_Address.deleteAddress(id).then((result) => {
if (result.statusCode == 200) {
uni.showToast({ icon: 'none', title: '删除成功' })
getAddressList()
} else {
uni.showToast({ icon: 'none', title: result.data.message, duration: 2000 })
}
})
},
})
}
function addAddress(id: string) {
if (id) {
uni.navigateTo({
url: `/pages/mine/address/add?id=${id}&way=${routerVal.value.way}&type=order`,
})
} else {
uni.navigateTo({
url: `/pages/mine/address/add?way=${routerVal.value.way}&type=order`,
})
}
}
function setDefault(item: any) {
delete item.updateBy
delete item.updateTime
delete item.deleteFlag
if (!item.isDefault) item.isDefault = true
API_Address.editAddress(item).then(() => {
uni.showToast({ title: '设置默认地址成功', icon: 'none' })
getAddressList()
})
}
</script>
<style lang="scss" scoped>
@import "./address.scss";
</style>

View File

@@ -26,20 +26,19 @@
<view class="relative">
<view class="alifont icon-bianji-copy"></view>
<text class="mr-40" @click="addAddress(item.id)">编辑</text>
<view class="alifont icon-lajitong"></view>
<text @click="removeAddress(item.id)">删除</text>
<view class="alifont icon-lajitong" @click.stop="removeAddress(item.id)"></view>
<text @click.stop="removeAddress(item.id)">删除</text>
</view>
</view>
</view>
<view style="height: 100px"></view>
</view>
<view class="btn" @click="addAddress('')">
<u-icon name="plus-circle" color="#ffffff" size="18"></u-icon>
<text class="btn-text">添加新收货人</text>
<view class="btn-bar">
<view class="btn" @click="addAddress('')">
<u-icon name="plus-circle" color="#ffffff" size="18"></u-icon>
<text class="btn-text">添加新收货人</text>
</view>
</view>
<u-action-sheet :list="removeList" :tips="tips" v-model:show="showAction" @click="deleteAddressMessage"></u-action-sheet>
</view>
</template>
@@ -53,10 +52,6 @@ import * as API_Address from '@/api/address.js'
const store = useStore()
const addressList = ref<any[]>([])
const showAction = ref(false)
const removeList = [{ text: '确定' }]
const tips = { text: '确定要删除该收货人信息吗?' }
const removeId = ref('')
const routerVal = ref<Record<string, string>>({})
const params = { pageNumber: 1, pageSize: 1000 }
@@ -96,18 +91,20 @@ function getAddressList() {
}
function removeAddress(id: string) {
removeId.value = id
showAction.value = true
}
function deleteAddressMessage() {
API_Address.deleteAddress(removeId.value).then((res) => {
if (res.statusCode == 200) {
uni.showToast({ icon: 'none', title: '删除成功' })
getAddressList()
} else {
uni.showToast({ icon: 'none', title: res.data.message, duration: 2000 })
}
uni.showModal({
title: '提示',
content: '确定要删除该收货人信息吗?',
success: (res) => {
if (!res.confirm) return
API_Address.deleteAddress(id).then((result) => {
if (result.statusCode == 200) {
uni.showToast({ icon: 'none', title: '删除成功' })
getAddressList()
} else {
uni.showToast({ icon: 'none', title: result.data.message, duration: 2000 })
}
})
},
})
}

View File

@@ -16,7 +16,13 @@
</view>
<view style="height: 100px"></view>
</view>
<u-action-sheet :list="removeList" :tips="tips" v-model:show="showAction" @click="deleteAddressMessage"></u-action-sheet>
<u-action-sheet
:actions="removeList"
:description="tips.text"
cancel-text="取消"
v-model:show="showAction"
@select="deleteAddressMessage"
></u-action-sheet>
</view>
</template>
@@ -31,7 +37,7 @@ const store = useStore()
const storeAddressList = ref<any[]>([])
const showAction = ref(false)
const removeList = [{ text: '确定' }]
const removeList = [{ name: '确定' }]
const tips = { text: '确定要删除该收货人信息吗?' }
const removeId = ref('')
const routerVal = ref<Record<string, string>>({})

View File

@@ -1,330 +1,325 @@
<template>
<view class="myTracks">
<u-navbar
title="我的足迹"
:fixed="true"
:placeholder="true"
:auto-back="true"
>
<template #right>
<view class="light-color edit" @click="isEdit = !isEdit">{{ !isEdit ? '编辑' : '完成'}}</view>
</template>
</u-navbar>
<view class="tracks-tip">
<u-icon name="volume" color="#f9ae3d" size="19"></u-icon>
<text class="tracks-tip-text">右划删除浏览记录</text>
</view>
<u-empty text="暂无历史记录" style="margin-top:200rpx;" mode="history" v-if="isEmpty"></u-empty>
<view v-else class="tracks-list">
<block v-for="(item, index) in trackList" :key="index">
<view
class="myTracks-title"
v-if="getStoreName(item)"
@click="navigateToStore(item)"
>{{ getStoreName(item) }}</view>
<u-swipe-action class="myTracks-swipe">
<u-swipe-action-item
:show="item.show"
:name="index"
@click="deleteTracks"
@open="openSwipe"
:options="swipeOptions"
>
<view class="myTracks-item">
<u-checkbox-group v-if="isEdit" class="store-line-check">
<u-checkbox
usedAlone
shape="circle"
:active-color="lightColor"
v-model:checked="item.checked"
@change="onTrackCheckChange(item)"
></u-checkbox>
</u-checkbox-group>
<view class="myTracks-item-img" @click.stop="navigateToDetail(item)">
<image class="track-thumb" :src="item.thumbnail"></image>
</view>
<view class="myTracks-item-content" @click.stop="navigateToDetail(item)">
<view class="myTracks-item-title">
{{ item.goodsName }}
</view>
<view class="myTracks-item-price">
¥{{ unitPrice(item.price) }}
</view>
</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
<view class="myTracks-divider"></view>
</block>
<view v-if="isEdit" class="submit" @click="deleteSelectedTracks">
删除所选
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onShow, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { myTrackList, deleteHistoryListId } from '@/api/members.js'
import { getStoreBaseInfo } from '@/api/store.js'
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const isEdit = ref(false)
const isEmpty = ref(false)
const params = ref({
pageNumber: 1,
pageSize: 10,
order: 'desc',
sort: 'updateTime',
})
const swipeOptions = [{ text: '删除', style: { backgroundColor: '#dd524d' } }]
const trackList = ref<any[]>([])
const storeNameMap = ref<Record<string, string>>({})
onReachBottom(() => {
params.value.pageNumber++
fetchTrackList()
})
onShow(() => {
params.value.pageNumber = 1
trackList.value = []
fetchTrackList()
})
onPullDownRefresh(() => {
params.value.pageNumber = 1
trackList.value = []
fetchTrackList()
})
function getStoreName(item: any) {
return item.storeName || storeNameMap.value[item.storeId] || ''
}
async function enrichStoreNames(records: any[]) {
const storeIds = [
...new Set(
records
.filter((item) => !item.storeName && item.storeId)
.map((item) => item.storeId)
),
].filter((storeId) => !storeNameMap.value[storeId])
await Promise.all(
storeIds.map(async (storeId) => {
try {
const res = await getStoreBaseInfo(storeId)
const name = res.data?.result?.storeName
if (name) {
storeNameMap.value[storeId] = name
}
} catch {
// ignore
}
})
)
}
function onTrackCheckChange(_val: any) {
// 勾选状态由 v-model:checked 维护
}
function deleteSelectedTracks() {
const ids = trackList.value.filter((item) => item.checked).map((item) => item.goodsId)
if (!ids.length) {
uni.showToast({ title: '请选择删除数据', icon: 'none' })
return
}
deleteTracks(0, ids)
}
function navigateToStore(val: any) {
uni.navigateTo({ url: `/pages/product/shopPage?id=${val.storeId}` })
}
function openSwipe(index: number) {
trackList.value[index].show = true
trackList.value.forEach((val, idx) => {
if (index !== idx) val.show = false
})
}
function navigateToDetail(item: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
})
}
function fetchTrackList() {
uni.showLoading({ title: '加载中' })
myTrackList(params.value).then(async (res) => {
uni.stopPullDownRefresh()
uni.hideLoading()
if (res.statusCode === 200) {
const records = res.data.result.records || []
records.forEach((item: any) => {
item.show = false
item.checked = false
})
if (!records.length) {
if (trackList.value.length === 0) {
isEmpty.value = true
}
} else {
isEmpty.value = false
await enrichStoreNames(records)
trackList.value.push(...records)
}
}
})
}
function deleteTracks(e: any, ids?: any) {
const index = typeof e === 'object' ? (e.name != null ? e.name : e.index) : e
const goodsId = ids || trackList.value[index]?.goodsId
if (!goodsId) return
deleteHistoryListId(goodsId).then((res) => {
if (res.data.code === 200) {
trackList.value = []
params.value.pageNumber = 1
fetchTrackList()
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}
</script>
<style lang="scss" scoped>
.submit {
position: fixed;
bottom: calc(20rpx + constant(safe-area-inset-bottom));
bottom: calc(20rpx + env(safe-area-inset-bottom));
left: 10%;
width: 80%;
height: 80rpx;
color: #fff;
border-radius: 100px;
display: flex;
align-items: center;
justify-content: center;
background: $light-color;
z-index: 99;
}
.myTracks {
width: 100%;
padding-top: 2rpx;
}
.tracks-tip {
display: flex;
align-items: center;
padding: 18rpx 24rpx;
background: #fdf6ec;
}
.tracks-tip-text {
margin-left: 10rpx;
font-size: 28rpx;
color: #f9ae3d;
line-height: 1.4;
}
.tracks-list {
width: 100%;
}
.myTracks-swipe,
:deep(.u-swipe-action),
:deep(.u-swipe-action-item),
:deep(.u-swipe-action-item__content) {
width: 100%;
}
:deep(.u-swipe-action-item__content) {
overflow: hidden;
}
.myTracks-title {
width: 100%;
height: 110rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #666;
font-weight: bold;
background-color: #fff;
display: flex;
align-items: center;
box-sizing: border-box;
}
.myTracks-item {
width: 100%;
min-height: 226rpx;
padding: 30rpx 20rpx;
background-color: #fff;
box-sizing: border-box;
display: flex;
align-items: center;
}
.store-line-check {
margin-right: 16rpx;
flex-shrink: 0;
}
.myTracks-item-img {
margin-right: 20rpx;
flex-shrink: 0;
.track-thumb {
width: 130rpx;
height: 130rpx;
border-radius: 8rpx;
display: block;
}
}
.myTracks-item-content {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
justify-content: center;
}
.myTracks-item-title {
font-size: 28rpx;
color: #333;
line-height: 1.4;
}
.myTracks-item-price {
font-size: 28rpx;
color: $light-color;
padding-top: 10rpx;
}
.myTracks-divider {
width: 100%;
height: 20rpx;
background: #f5f5f5;
}
.edit {
padding-right: 32rpx;
}
</style>
<template>
<view class="myTracks">
<u-navbar
title="我的足迹"
:fixed="true"
:placeholder="true"
:auto-back="true"
>
<template #right>
<view class="light-color edit" @click="isEdit = !isEdit">{{ !isEdit ? '编辑' : '完成'}}</view>
</template>
</u-navbar>
<u-empty text="暂无历史记录" style="margin-top:200rpx;" mode="history" v-if="isEmpty"></u-empty>
<view v-else class="tracks-list" :class="{ 'is-editing': isEdit }">
<u-swipe-action
v-for="(item, index) in trackList"
:key="item.id || index"
class="myTracks-swipe"
>
<u-swipe-action-item
:show="item.show"
:name="index"
@click="deleteTracks"
@open="openSwipe"
:options="swipeOptions"
>
<view class="myTracks-item">
<u-checkbox-group v-if="isEdit" class="store-line-check">
<u-checkbox
usedAlone
shape="circle"
:active-color="lightColor"
v-model:checked="item.checked"
@change="onTrackCheckChange(item)"
></u-checkbox>
</u-checkbox-group>
<view class="myTracks-item-img" @click.stop="navigateToDetail(item)">
<image v-if="item.thumbnail" class="track-thumb-img" mode="aspectFill" :src="item.thumbnail"></image>
</view>
<view class="myTracks-item-content" @click.stop="navigateToDetail(item)">
<view class="myTracks-item-title">{{ item.goodsName }}</view>
<view
v-if="getStoreName(item)"
class="myTracks-item-store"
@click.stop="navigateToStore(item)"
>{{ getStoreName(item) }}</view>
<view class="myTracks-item-price">¥{{ unitPrice(item.price) }}</view>
</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
<view v-if="isEdit" class="submit" @click="deleteSelectedTracks">删除所选</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onShow, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { myTrackList, deleteHistoryListId } from '@/api/members.js'
import { getStoreBaseInfo } from '@/api/store.js'
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const isEdit = ref(false)
const isEmpty = ref(false)
const params = ref({
pageNumber: 1,
pageSize: 10,
order: 'desc',
sort: 'updateTime',
})
const swipeOptions = [{ text: '删除', style: { backgroundColor: '#dd524d' } }]
const trackList = ref<any[]>([])
const storeNameMap = ref<Record<string, string>>({})
onReachBottom(() => {
params.value.pageNumber++
fetchTrackList()
})
onShow(() => {
params.value.pageNumber = 1
trackList.value = []
fetchTrackList()
})
onPullDownRefresh(() => {
params.value.pageNumber = 1
trackList.value = []
fetchTrackList()
})
function getStoreName(item: any) {
return item.storeName || storeNameMap.value[item.storeId] || ''
}
async function enrichStoreNames(records: any[]) {
const storeIds = [
...new Set(
records
.filter((item) => !item.storeName && item.storeId)
.map((item) => item.storeId)
),
].filter((storeId) => !storeNameMap.value[storeId])
await Promise.all(
storeIds.map(async (storeId) => {
try {
const res = await getStoreBaseInfo(storeId)
const name = res.data?.result?.storeName
if (name) {
storeNameMap.value[storeId] = name
}
} catch {
// ignore
}
})
)
}
function onTrackCheckChange(_val: any) {
// 勾选状态由 v-model:checked 维护
}
function deleteSelectedTracks() {
const ids = trackList.value.filter((item) => item.checked).map((item) => item.goodsId)
if (!ids.length) {
uni.showToast({ title: '请选择删除数据', icon: 'none' })
return
}
deleteTracks(0, ids)
}
function navigateToStore(val: any) {
uni.navigateTo({ url: `/pages/product/shopPage?id=${val.storeId}` })
}
function openSwipe(index: number) {
trackList.value[index].show = true
trackList.value.forEach((val, idx) => {
if (index !== idx) val.show = false
})
}
function navigateToDetail(item: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
})
}
function fetchTrackList() {
uni.showLoading({ title: '加载中' })
myTrackList(params.value).then(async (res) => {
uni.stopPullDownRefresh()
uni.hideLoading()
if (res.statusCode === 200) {
const records = res.data.result.records || []
records.forEach((item: any) => {
item.show = false
item.checked = false
})
if (!records.length) {
if (trackList.value.length === 0) {
isEmpty.value = true
}
} else {
isEmpty.value = false
await enrichStoreNames(records)
trackList.value.push(...records)
}
}
})
}
function deleteTracks(e: any, ids?: any) {
const index = typeof e === 'object' ? (e.name != null ? e.name : e.index) : e
const goodsId = ids || trackList.value[index]?.goodsId
if (!goodsId) return
deleteHistoryListId(goodsId).then((res) => {
if (res.data.code === 200) {
trackList.value = []
params.value.pageNumber = 1
fetchTrackList()
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}
</script>
<style lang="scss" scoped>
.submit {
position: fixed;
bottom: calc(20rpx + constant(safe-area-inset-bottom));
bottom: calc(20rpx + env(safe-area-inset-bottom));
left: 10%;
width: 80%;
height: 80rpx;
color: #fff;
border-radius: 100px;
display: flex;
align-items: center;
justify-content: center;
background: $light-color;
z-index: 99;
}
.myTracks {
width: 100%;
min-height: 100vh;
background: #f5f5f5;
padding-top: 2rpx;
box-sizing: border-box;
}
.tracks-list {
padding: 20rpx 24rpx 40rpx;
box-sizing: border-box;
&.is-editing {
padding-bottom: calc(140rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
}
}
.myTracks-swipe {
display: block;
margin-bottom: 20rpx;
border-radius: 16rpx;
overflow: hidden;
background: #fff;
}
:deep(.u-swipe-action),
:deep(.u-swipe-action-item),
:deep(.u-swipe-action-item__content) {
width: 100%;
}
:deep(.u-swipe-action-item__content) {
overflow: hidden;
background: #fff;
}
.myTracks-item {
width: 100%;
padding: 24rpx;
background-color: #fff;
box-sizing: border-box;
display: flex;
align-items: center;
}
.store-line-check {
margin-right: 16rpx;
flex-shrink: 0;
}
.myTracks-item-img {
width: 160rpx;
height: 160rpx;
margin-right: 20rpx;
flex-shrink: 0;
border-radius: 12rpx;
background: #f3f4f6;
overflow: hidden;
.track-thumb-img {
width: 100%;
height: 100%;
display: block;
}
}
.myTracks-item-content {
flex: 1;
min-width: 0;
min-height: 160rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.myTracks-item-title {
font-size: 28rpx;
color: #333;
line-height: 1.4;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.myTracks-item-store {
margin-top: 8rpx;
font-size: 24rpx;
color: #999;
line-height: 1.4;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.myTracks-item-price {
margin-top: 12rpx;
font-size: 32rpx;
font-weight: 600;
color: $light-color;
line-height: 1.2;
}
.edit {
padding-right: 32rpx;
}
</style>

View File

@@ -56,7 +56,6 @@
<view class="bottom">
<view class="submit" @click="submit">保存</view>
<view class="submit light" @click="quiteLoginOut">退出登录</view>
</view>
<u-datetime-picker
@@ -84,7 +83,6 @@
import { ref, reactive, computed } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { quiteLoginOut } from '@/utils/filters.js'
import { saveUserInfo } from '@/api/members.js'
import { upload } from '@/api/common.js'
import storage from '@/utils/storage.js'
@@ -211,31 +209,40 @@ onShow(() => {
})
</script>
<style lang="scss" scoped>
<style lang="scss">
page {
background: #fff;
background: #f5f5f5;
}
</style>
<style lang="scss" scoped>
.person-msg {
min-height: 100vh;
background: #fff;
padding-bottom: 180rpx;
background: #f5f5f5;
padding-bottom: calc(160rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.head {
padding: 40rpx 0 20rpx;
color: $font-color-light;
font-size: $font-sm;
margin-bottom: 20rpx;
padding: 48rpx 0 32rpx;
background: #fff;
color: #999;
font-size: 24rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
line-height: 2em;
image {
width: 144rpx;
height: 144rpx;
border-radius: 50%;
background: #f3f4f6;
}
view {
margin-top: 16rpx;
line-height: 1.4;
}
}
@@ -244,13 +251,13 @@ page {
}
:deep(.u-form-item) {
padding: 0 30rpx;
min-height: 100rpx;
padding: 0 32rpx;
}
:deep(.u-form-item__body) {
min-height: 100rpx;
min-height: 108rpx;
align-items: center;
padding: 16rpx 0 !important;
}
:deep(.u-form-item__body__left__content__label) {
@@ -258,8 +265,13 @@ page {
color: #333;
}
:deep(.u-form-item__body__right) {
min-width: 0;
}
:deep(.u-form-item__body__right__content__slot) {
flex: 1;
min-width: 0;
display: flex;
justify-content: flex-end;
align-items: center;
@@ -271,7 +283,8 @@ page {
:deep(.u-input__content__field-wrapper__field) {
font-size: 28rpx !important;
color: #666 !important;
color: #333 !important;
text-align: right;
}
.sex-row {
@@ -288,16 +301,20 @@ page {
.form-value {
width: 100%;
min-width: 0;
text-align: right;
font-size: 28rpx;
color: #666;
color: #333;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.bind-mobile {
width: 100%;
text-align: right;
font-size: 28rpx;
color: $main-color;
color: $light-color;
}
.bottom {
@@ -307,10 +324,10 @@ page {
bottom: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 30rpx calc(24rpx + env(safe-area-inset-bottom));
padding: 16rpx 32rpx calc(16rpx + env(safe-area-inset-bottom));
background: #fff;
box-sizing: border-box;
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.04);
}
.submit {
@@ -318,18 +335,9 @@ page {
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 100px;
border-radius: 40rpx;
font-size: 30rpx;
background: $light-color;
color: #fff;
}
.submit + .submit {
margin-left: 24rpx;
}
.light {
background: rgba($color: $light-color, $alpha: 0.2) !important;
color: $light-color !important;
}
</style>

View File

@@ -159,7 +159,7 @@
title="申请原因"
@confirm="reasonSelectConfirm"
></u-picker>
<u-toast ref="uToast" />
<u-toast ref="toastRef" />
</view>
</template>
@@ -186,7 +186,7 @@ const sku = ref<any>({})
const reasonSelectShow = ref(false)
const reasonList = ref<any[]>([])
const applyInfo = ref<any>({})
const uToast = ref<any>(null)
const toastRef = ref<any>(null)
const isAppointmentAfterSale = ref(false)
const form = reactive({
@@ -335,8 +335,8 @@ function onUploadAfterRead(event: any) {
function showToast(message: string, type = 'error') {
const text = message || (type === 'success' ? '操作成功' : '操作失败')
if (uToast.value) {
uToast.value.show({ message: text, type })
if (toastRef.value) {
toastRef.value.show({ message: text, type })
return
}
uni.showToast({

View File

@@ -69,7 +69,7 @@
@confirm="onTimeConfirm"
@close="timeshow = false"
/>
<u-toast ref="uToast"/>
<u-toast ref="toastRef"/>
</view>
</template>
@@ -96,7 +96,7 @@ const form = reactive({
})
const serviceDetail = reactive<{ sn: string }>({ sn: '' })
const sku = ref<any>({})
const uToast = ref<any>(null)
const toastRef = ref<any>(null)
onLoad((options) => {
sku.value = storage.getAfterSaleData()
@@ -136,15 +136,15 @@ function onSubmit() {
delete submitForm.courierCompany
if (form.logisticsId == '') {
uToast.value?.show({ title: '请选择快递公司', type: 'error' })
toastRef.value?.show({ title: '请选择快递公司', type: 'error' })
return
}
if (form.logisticsNo == '') {
uToast.value?.show({ title: '请填写快递单号', type: 'error' })
toastRef.value?.show({ title: '请填写快递单号', type: 'error' })
return
}
if (form.mDeliverTime == '') {
uToast.value?.show({ title: '请选择发货时间', type: 'error' })
toastRef.value?.show({ title: '请选择发货时间', type: 'error' })
return
}
@@ -152,7 +152,7 @@ function onSubmit() {
fillShipInfo(serviceDetail.sn, submitForm).then((res) => {
hideLoadingIfNeeded()
if (res.statusCode === 200) {
uToast.value?.show({
toastRef.value?.show({
title: '提交成功',
type: 'success',
back: true,

View File

@@ -417,7 +417,11 @@ function openOrder(order: any) {
}
function changeTab(e: any) {
tabCurrentIndex.value = e.target.current
// H5 的 swiper change 把下标放在 detail.current。
// 读 target.current 会得到 undefined,选中态被清掉,列表就停在「全部」。
const current = Number(e?.detail?.current ?? e?.target?.current)
if (!Number.isInteger(current) || current < 0 || current >= navList.length) return
tabCurrentIndex.value = current
}
function tabClick(index: number) {

View File

@@ -34,7 +34,7 @@
<verification-codes :items="verificationCodeItems" />
</view>
<view v-else-if="isECouponOrder" class="card-key-entry">
<view v-if="ecouponCardKeyDelivered" class="card-key-entry__btn" @click="goCardKeyDetail">
<view v-if="ecouponCardKeyDelivered" class="btn" @click="goCardKeyDetail">
查看卡密
</view>
<view v-else-if="order.orderStatus === 'CANCELLED'" class="card-key-entry__tip">
@@ -1055,17 +1055,14 @@ page,
.card-key-entry {
width: 100%;
text-align: center;
}
.card-key-entry__btn {
display: inline-block;
padding: 20rpx 80rpx;
border: 2rpx solid $main-color;
border-radius: 12rpx;
color: $main-color;
font-size: 28rpx;
font-weight: 600;
.btn {
width: 100%;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
font-size: 30rpx;
}
}
.card-key-entry__tip {

View File

@@ -11,15 +11,21 @@
<div class="step-list">
<div
class="step-item"
:class="{ active: current == index }"
v-for="(item, index) in entrySteps"
:key="index"
:class="{ active: current == index, done: current > index }"
v-for="(item, index) in flowSteps"
:key="item.value"
>
{{ item.title }}
<div class="step-mark">{{ index + 1 }}</div>
<div class="step-title">{{ item.title }}</div>
</div>
</div>
<div class="submit" @click="keepOn()">开始填写</div>
<div class="notice" @click="getEntryNotice">查看店铺入驻协议</div>
<div v-if="statusTip" class="status-tip" :class="statusTip.value">
{{ statusTip.title }}
</div>
<div class="actions">
<div v-if="showFillButton" class="submit" @click="keepOn()">开始填写</div>
<div class="notice" @click="getEntryNotice">查看店铺入驻协议</div>
</div>
</div>
</div>
</template>
@@ -36,8 +42,15 @@ const store = useStore()
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const current = ref(999)
const current = ref(-1)
const companyData = ref<any>('')
const showFillButton = computed(() => companyData.value?.storeDisable !== 'OPEN')
const flowSteps = computed(() =>
entrySteps.value.filter((item) => item.value === 'APPLY' || item.value === 'APPLYING')
)
const statusTip = computed(() =>
entrySteps.value.find((item) => item.value !== 'APPLY' && item.value !== 'APPLYING')
)
const entrySteps = ref([
{
@@ -137,54 +150,103 @@ page {
}
.entry-content {
padding: 32rpx 80rpx calc(40rpx + env(safe-area-inset-bottom));
padding: 48rpx 48rpx calc(40rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.title {
line-height: 1.2;
font-weight: 500;
font-size: 56rpx;
color: #333;
font-weight: 600;
font-size: 48rpx;
color: #222;
}
.step-list {
margin: 80rpx 0;
margin-top: 64rpx;
}
.step-item {
padding: 30rpx 20rpx;
font-size: 40rpx;
font-weight: bold;
color: #666;
display: flex;
align-items: center;
padding: 18rpx 0;
color: #999;
}
.step-item.active {
.step-mark {
width: 48rpx;
height: 48rpx;
margin-right: 20rpx;
border-radius: 50%;
background: #f2f2f2;
color: #999;
font-size: 26rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.step-title {
font-size: 32rpx;
font-weight: 500;
line-height: 1.4;
}
.step-item.done .step-mark,
.step-item.active .step-mark {
background: var(--theme-light, #ff6b35);
color: #fff;
}
.step-item.done .step-title {
color: #333;
}
.step-item.active .step-title {
color: var(--theme-light, #ff6b35);
}
.status-tip {
margin-top: 24rpx;
padding: 24rpx 28rpx;
border-radius: 16rpx;
font-size: 28rpx;
line-height: 1.5;
color: var(--theme-light, #ff6b35);
background: var(--theme-light-10, rgba(255, 107, 53, 0.1));
border-radius: 20rpx;
}
.status-tip.REFUSED {
color: var(--theme-primary, #ff3c2a);
background: #fff1f0;
}
.status-tip.CLOSED {
color: #666;
background: #f5f5f5;
}
.actions {
margin-top: 72rpx;
}
.submit,
.notice {
font-weight: bold;
font-size: 28rpx;
height: 92rpx;
height: 88rpx;
line-height: 88rpx;
text-align: center;
letter-spacing: 4rpx;
line-height: 92rpx;
border-radius: 20rpx;
font-size: 30rpx;
border-radius: 44rpx;
}
.submit {
color: #fff;
margin-top: 120rpx;
background: var(--theme-light, #ff6b35);
margin-bottom: 24rpx;
}
.notice {
margin-top: 40rpx;
color: #333;
background: $bg-color;
background: #f5f5f5;
}
</style>

View File

@@ -1,25 +1,22 @@
<template>
<view class="box">
<view v-if="bargainLog.length != 0">
<view v-for="(item,index) in bargainLog" class="flex" :key="index">
<view v-if="bargainLog.length != 0" class="list">
<view v-for="(item,index) in bargainLog" class="card" :key="index">
<view class="goods-img">
<u-image radius="20rpx" width="230rpx" height="230rpx" :src="item.thumbnail"></u-image>
<u-image radius="12rpx" width="160rpx" height="160rpx" :src="item.thumbnail"></u-image>
</view>
<view class="goods">
<view class="wes-2">
{{item.goodsName}}
<view class="head">
<view class="goods-name wes-2">{{ item.goodsName }}</view>
<text class="tips" :class="item.status">{{ statusWay[item.status] }}</text>
</view>
<view class="surplus">
还剩<text class="surplusPrice">{{unitPrice(item.surplusPrice)}}元</text>
</view>
<view @click="navigateToBargainDetail(item)" v-if="item.status == 'START'" class="buy">
继续免费领
</view>
</view>
<view class="tips-box">
<view class="tips" :class="[item.status]">
{{statusWay[item.status]}}
<view class="foot">
<view class="surplus">
还剩<text class="surplusPrice">{{ unitPrice(item.surplusPrice) }}</text>元
</view>
<view v-if="item.status == 'START'" class="buy" @click="navigateToBargainDetail(item)">
继续免费领
</view>
</view>
</view>
</view>
@@ -76,67 +73,90 @@ function navigateToBargainDetail(val: any) {
</script>
<style>
page {
background: #fff;
background: #f5f5f5;
}
</style>
<style scoped lang="scss">
.box {
padding: 0 32rpx;
min-height: 100vh;
padding: 20rpx 24rpx 40rpx;
background: #f5f5f5;
box-sizing: border-box;
}
.card {
display: flex;
align-items: center;
margin-bottom: 20rpx;
padding: 24rpx;
background: #fff;
border-radius: 16rpx;
}
.goods-img {
width: 230rpx;
height: 230rpx;
flex: 0 0 230rpx;
}
.buy {
background: $light-color;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
padding: 10rpx 0;
border-radius: 100rpx;
width: 200rpx;
font-size: 24rpx;
margin-top: 20rpx;
}
.tips-box {
flex: 1;
justify-content: center;
display: flex;
align-items: center;
}
.tips {
color: #999;
margin-top: 20rpx;
}
.surplusPrice {
font-size: 40rpx;
margin-left: 10rpx;
font-weight: bold;
color: $light-color;
}
.surplus {
color: #333;
font-size: 26rpx;
margin-top: 12rpx;
width: 160rpx;
height: 160rpx;
flex: 0 0 160rpx;
border-radius: 12rpx;
overflow: hidden;
background: #f5f5f5;
}
.goods {
margin: 0 20rpx;
flex: 1;
min-width: 0;
margin-left: 20rpx;
display: flex;
flex: 2;
flex-direction: column;
justify-content: center;
justify-content: space-between;
min-height: 160rpx;
}
.flex {
border-bottom: 1rpx solid #f7f7f7;
.head {
display: flex;
align-items: flex-start;
}
.goods-name {
flex: 1;
min-width: 0;
font-size: 28rpx;
color: #333;
line-height: 1.4;
}
.tips {
flex-shrink: 0;
margin-left: 16rpx;
font-size: 24rpx;
line-height: 1.4;
color: #999;
}
.foot {
display: flex;
align-items: center;
padding: 20rpx 0;
margin: 10rpx 0;
justify-content: space-between;
margin-top: 16rpx;
}
.surplus {
flex: 1;
min-width: 0;
color: #666;
font-size: 24rpx;
white-space: nowrap;
}
.surplusPrice {
margin: 0 6rpx;
font-size: 36rpx;
font-weight: 600;
color: $light-color;
}
.buy {
flex-shrink: 0;
margin-left: 16rpx;
height: 56rpx;
padding: 0 28rpx;
border-radius: 100rpx;
background: $light-color;
color: #fff;
font-size: 24rpx;
display: flex;
align-items: center;
justify-content: center;
}
.SUCCESS {
color: $light-color;

File diff suppressed because it is too large Load Diff