refactor: 重构多个组件以支持 Vue 3 语法和功能

- 将多个组件转换为 `<script setup>` 语法,提升可读性和性能
- 优化状态管理和事件处理逻辑,简化代码结构
- 更新样式和布局以适应新组件结构
- 添加新功能和修复已知问题,提升用户体验
This commit is contained in:
Yer11214
2026-07-08 18:37:11 +08:00
parent 4d0b0fb5e4
commit 349ffa4f34
189 changed files with 18278 additions and 20758 deletions

View File

@@ -1,302 +1,262 @@
<template>
<view class="coupon-center">
<div class="swiper-box">
<div class="swiper-item">
<div class="scroll-v" enableBackToTop="true" scroll-y>
<u-empty mode="coupon" style='margin-top: 20%;' text="没有优惠券了" v-if="whetherEmpty"></u-empty>
<view v-else class="coupon-item" v-for="(item, index) in couponList" :key="index">
<view class="left">
<view class="wave-line">
<view class="wave" v-for="(item, index) in 12" :key="index"></view>
</view>
<view class="message">
<view>
<!--判断当前优惠券类型 couponType PRICE || DISCOUNT -->
<span v-if="item.couponType == 'DISCOUNT'">{{ item.couponDiscount }}</span>
<span v-else>{{ item.price }}</span>
</view>
<view>{{unitPrice(item.consumeThreshold) }}元可用</view>
</view>
<view class="circle circle-top"></view>
<view class="circle circle-bottom"></view>
</view>
<view class="right">
<view>
<!-- 根据scopeType 判断是否是 平台品类或店铺 -->
<view class="coupon-title wes-3" v-if="item.scopeType">
<span v-if="item.scopeType == 'ALL' && item.storeId == '0'">全平台</span>
<span v-if="item.scopeType == 'PORTION_GOODS_CATEGORY'">仅限品类</span>
<view v-else>{{ item.storeName == 'platform' ? '全平台' :item.storeName+'店铺' }}使用
</view>
</view>
<view v-if="item.endTime">有效期至:{{ item.endTime.split(" ")[0] }}</view>
</view>
<view class="receive" @click="receive(item)">
<text>点击</text><br />
<text>领取</text>
</view>
<view class="bg-quan"> </view>
</view>
<view class="coupon-list">
<u-empty mode="coupon" style='margin-top: 20%;' text="没有优惠券了" v-if="whetherEmpty"></u-empty>
<view v-else class="coupon-card" v-for="(item, index) in couponList" :key="index">
<view class="coupon-card-left">
<text class="coupon-price-symbol">¥</text>
<text class="coupon-price-value">{{ item.couponType == 'DISCOUNT' ? item.couponDiscount + '折' : unitPrice(item.price) }}</text>
</view>
<view class="coupon-divider"></view>
<view class="coupon-card-right">
<view class="coupon-info">
<text class="coupon-card-name">{{ item.storeName == 'platform' ? '全平台' : item.storeName + '店铺' }}使用</text>
<text class="coupon-card-desc">{{unitPrice(item.consumeThreshold) }}元可用</text>
<text class="coupon-card-time" v-if="item.endTime">有效期至:{{ item.endTime.split(" ")[0] }}</text>
</view>
</div>
</div>
</div>
<view class="coupon-claim-btn" @click="receive(item)">
领取
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import {
receiveCoupons
} from "@/api/members.js";
import {
getAllCoupons
} from "@/api/promotions.js";
export default {
data() {
return {
loadStatus: "more", //下拉状态
whetherEmpty: false, //是否为空
couponList: [], // 优惠券列表
params: {
pageNumber: 1,
pageSize: 10,
},
storeId: "", //店铺 id,
couponData: ""
};
},
onLoad(option) {
this.storeId = option.storeId;
this.getCoupon();
},
onReachBottom() {
<script setup lang="ts">
import { receiveCoupons } from '@/api/members.js'
import { getAllCoupons } from '@/api/promotions.js'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import {
onLoad,
onNavigationBarButtonTap,
onPullDownRefresh,
onReachBottom,
} from '@dcloudio/uni-app'
import { getCurrentInstance, ref } from 'vue'
this.loadMore()
},
onPullDownRefresh() {
//下拉刷新
this.params.pageNumber = 1;
this.couponList = [];
this.getCoupon();
},
methods: {
/**
* 获取当前优惠券
*/
getCoupon() {
uni.showLoading({
title: "加载中",
});
let submitData = {
...this.params
};
// 判断当前是否有店铺
this.storeId ? (submitData = {
...this.params,
storeId: this.storeId
}) : "",
getAllCoupons(submitData)
.then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
uni.stopPullDownRefresh();
if (res.data.code == 200) {
// 如果请求成功,展示数据并进行展示
this.couponData = res.data.result
if (this.couponData.total == 0) {
// 当本次请求数据为空展示空信息
this.whetherEmpty = true;
} else {
this.couponList.push(...this.couponData.records);
this.loadStatus = "noMore";
}
}
})
.catch((err) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
/**
* 领取优惠券
*/
receive(val) {
this.$u.throttle(()=>{
this.fetchCoupon(val)
}, 1500)
},
const store = useStore()
const { proxy } = getCurrentInstance()!
fetchCoupon(val){
receiveCoupons(val.id).then((res) => {
if (res.data.code == 200) {
uni.showToast({
title: "领取成功",
icon: "none",
});
} else {
uni.showToast({
title: res.data.message,
icon: "none",
});
}
});
},
const loadStatus = ref('more')
const whetherEmpty = ref(false)
const couponList = ref<any[]>([])
const params = ref({
pageNumber: 1,
pageSize: 10,
})
const storeId = ref('')
const couponData = ref<any>(null)
/**
* 加载更多
*/
loadMore() {
if (this.couponData.total > this.params.pageNumber * this.params.pageSize) {
this.params.pageNumber++;
this.getCoupon();
}
},
},
onNavigationBarButtonTap(e) {
uni.navigateTo({
url: "/pages/cart/coupon/couponIntro",
});
},
};
onLoad((option) => {
storeId.value = option.storeId || ''
getCoupon()
})
onReachBottom(() => {
loadMore()
})
onPullDownRefresh(() => {
params.value.pageNumber = 1
couponList.value = []
getCoupon()
})
onNavigationBarButtonTap(() => {
uni.navigateTo({
url: '/pages/cart/coupon/couponIntro',
})
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function getCoupon() {
uni.showLoading({ title: '加载中' })
const submitData = storeId.value
? { ...params.value, storeId: storeId.value }
: { ...params.value }
getAllCoupons(submitData)
.then((res) => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
if (res.data.code == 200) {
couponData.value = res.data.result
if (couponData.value.total == 0) {
whetherEmpty.value = true
} else {
couponList.value.push(...couponData.value.records)
loadStatus.value = 'noMore'
}
}
})
.catch(() => {
hideLoadingIfNeeded()
})
}
function receive(val: any) {
proxy.$u.throttle(() => {
fetchCoupon(val)
}, 1500)
}
function fetchCoupon(val: any) {
receiveCoupons(val.id).then((res) => {
if (res.data.code == 200) {
uni.showToast({ title: '领取成功', icon: 'none' })
} else {
uni.showToast({ title: res.data.message, icon: 'none' })
}
})
}
function loadMore() {
if (couponData.value?.total > params.value.pageNumber * params.value.pageSize) {
params.value.pageNumber++
getCoupon()
}
}
</script>
<style>
page {
height: 100%;
}
</style>
<style lang="scss" scoped>
.coupon-center {
height: 100%;
.coupon-center {
min-height: 100vh;
background: #f7f8fa;
}
.swiper-box {
.coupon-item {
display: flex;
align-items: center;
height: 220rpx;
margin: 20rpx;
.coupon-list {
padding: 24rpx;
}
.left {
height: 100%;
width: 260rpx;
background-color: $light-color;
position: relative;
.coupon-card {
display: flex;
align-items: stretch;
background: #fff;
border-radius: 24rpx;
margin-bottom: 24rpx;
position: relative;
box-shadow: none;
border: 1rpx solid #f0f1f5;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
.message {
color: $font-color-white;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 40rpx;
&:active {
transform: scale(0.98);
}
view:nth-child(1) {
font-weight: bold;
font-size: 60rpx;
}
&::before,
&::after {
content: "";
position: absolute;
width: 32rpx;
height: 32rpx;
background: #f7f8fa;
border-radius: 50%;
left: 204rpx;
z-index: 2;
box-shadow: inset 0 0 0 1rpx #eceef2;
}
view:nth-child(2) {
font-size: $font-sm;
}
}
&::before {
top: -16rpx;
}
.wave-line {
height: 220rpx;
width: 8rpx;
position: absolute;
top: 0;
left: 0;
background-color: $light-color;
overflow: hidden;
&::after {
bottom: -16rpx;
}
}
.wave {
width: 8rpx;
height: 16rpx;
background-color: #ffffff;
border-radius: 0 16rpx 16rpx 0;
margin-top: 4rpx;
}
}
.coupon-card-left {
width: 220rpx;
min-height: 180rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: #ff3b30;
position: relative;
background: linear-gradient(135deg, #fff5f5 0%, #ffecec 100%);
}
.circle {
width: 40rpx;
height: 40rpx;
background-color: $bg-color;
position: absolute;
border-radius: 50%;
z-index: 111;
}
.coupon-price-symbol {
font-size: 32rpx;
font-weight: 700;
margin-right: 4rpx;
}
.circle-top {
top: -20rpx;
right: -20rpx;
}
.coupon-price-value {
font-size: 56rpx;
font-weight: 900;
line-height: 1;
letter-spacing: -1rpx;
}
.circle-bottom {
bottom: -20rpx;
right: -20rpx;
}
}
.coupon-divider {
position: absolute;
left: 220rpx;
top: 24rpx;
bottom: 24rpx;
width: 0;
border-left: 2rpx dashed #eceef2;
}
.right {
display: flex;
justify-content: space-between;
align-items: center;
width: 450rpx;
font-size: $font-sm;
height: 100%;
background-color: #ffffff;
overflow: hidden;
position: relative;
.coupon-card-right {
flex: 1;
padding: 32rpx 32rpx 32rpx 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
background: #fff;
}
>view:nth-child(1) {
color: #666666;
margin-left: 20rpx;
display: flex;
height: 100%;
flex-direction: column;
justify-content: space-around;
.coupon-info {
display: flex;
flex-direction: column;
gap: 12rpx;
flex: 1;
min-width: 0;
}
>view:nth-child(1) {
color: #ff6262;
font-size: 30rpx;
}
}
.coupon-card-name {
font-size: 30rpx;
color: #111;
font-weight: 700;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.receive {
color: #ffffff;
background-color: $main-color;
border-radius: 50%;
width: 86rpx;
height: 86rpx;
text-align: center;
margin-right: 30rpx;
vertical-align: middle;
padding-top: 8rpx;
position: relative;
z-index: 2;
}
.coupon-card-desc {
font-size: 24rpx;
color: #8a8f99;
font-weight: 500;
}
.bg-quan {
width: 244rpx;
height: 244rpx;
border: 6rpx solid $main-color;
border-radius: 50%;
opacity: 0.1;
color: $main-color;
text-align: center;
padding-top: 30rpx;
font-size: 130rpx;
position: absolute;
right: -54rpx;
bottom: -60rpx;
}
}
}
}
}
.coupon-card-time {
font-size: 22rpx;
color: #b0b3bf;
}
.coupon-title {
width: 260rpx;
.coupon-claim-btn {
flex-shrink: 0;
padding: 16rpx 32rpx;
border-radius: 40rpx;
font-size: 26rpx;
font-weight: 700;
color: #fff;
background: linear-gradient(135deg, #ff6b35, #ff4b2b);
box-shadow: 0 8rpx 16rpx rgba(255, 75, 43, 0.25);
}
&:active {
opacity: 0.9;
}
}
</style>

View File

@@ -2,52 +2,46 @@
<view class="content">
<view class="body">
<view class="top-view">
<view class="title">{{coupon.title}}</view>
<view class="price" v-if="coupon.couponType =='PRICE'"><text></text>{{unitPrice(coupon.price)}}</view>
<view class="price" v-if="coupon.couponType =='DISCOUNT'">{{coupon.discount}}</view>
<view class="text">{{coupon.consumeThreshold}}元可用</view>
<view class="bg-quan">
</view>
<view class="jiao-1" :class="{'used-color':coupon.used_status!=0}">
<text class="text-1">{{coupon.used_status == 0?'新到':coupon.used_status_text}}</text>
<view class="title">{{ coupon.title }}</view>
<view class="price" v-if="coupon.couponType == 'PRICE'"><text></text>{{ unitPrice(coupon.price) }}</view>
<view class="price" v-if="coupon.couponType == 'DISCOUNT'">{{ coupon.discount }}</view>
<view class="text">{{ coupon.consumeThreshold }}元可用</view>
<view class="bg-quan"></view>
<view class="jiao-1" :class="{ 'used-color': coupon.used_status != 0 }">
<text class="text-1">{{ coupon.used_status == 0 ? '新到' : coupon.used_status_text }}</text>
</view>
</view>
<view class="bottom-view">
<view class="text"> 使用范围{{
coupon.scopeType == 'ALL' && coupon.storeId == '0'
? "全平台"
: coupon.scopeType == "PORTION_GOODS"
? "部分商品"
: coupon.scopeType == "PORTION_GOODS_CATEGORY"
? "部分分类商品"
: coupon.storeName == 'platform' ? '全平台' :coupon.storeName+''
}}使用</view>
<view class="text"> 有效期至{{coupon.endTime}}</view>
coupon.scopeType == 'ALL' && coupon.storeId == '0'
? '全平台'
: coupon.scopeType == 'PORTION_GOODS'
? '部分商品'
: coupon.scopeType == 'PORTION_GOODS_CATEGORY'
? '部分分类商品'
: coupon.storeName == 'platform' ? '全平台' : coupon.storeName + ''
}}使用</view>
<view class="text"> 有效期至{{ coupon.endTime }}</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
coupon: {}, //优惠券数据
};
},
onLoad(option) {
this.coupon = JSON.parse(decodeURIComponent(option.item));
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { unitPrice } from '@/utils/filters.js'
const coupon = ref<Record<string, any>>({})
onLoad((option) => {
coupon.value = JSON.parse(decodeURIComponent(option.item))
})
</script>
<style lang="scss" scoped>
page,
.content {
// background: $main-color;
height: 100%;
}
.body {

View File

@@ -59,81 +59,59 @@
</view>
</div>
</template>
<script>
import { useCoupon } from "@/api/trade.js";
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { useCoupon } from '@/api/trade.js'
import { unitPrice } from '@/utils/filters.js'
export default {
data() {
return {
lightColor: this.$lightColor,
current: 0,
list: [
{
name: "可用优惠券",
},
{
name: "不可用优惠券",
},
],
couponsList: [], //优惠券集合
params: {
//传参
memberCouponStatus: "NEW", //优惠券状态
pageNumber: 1,
pageSize: 10,
scopeId: "", //商品skuid
storeId: "", //店铺id
totalPrice: "", //价格
},
routerVal: "", //上级传参
};
},
onLoad(options) {
this.routerVal = options;
},
watch: {
current(val) {
console.log(this.$store.state.cantUseCoupons);
val == 0
? (this.couponsList = this.$store.state.canUseCoupons)
: (this.couponsList = this.$store.state.cantUseCoupons);
},
},
const store = useStore()
mounted() {
this.init();
console.log(this.routerVal);
},
const lightColor = computed(() => store.getters.lightColor)
const current = ref(0)
const list = [
{ name: '可用优惠券' },
{ name: '不可用优惠券' },
]
const couponsList = ref<any[]>([])
const routerVal = ref<Record<string, any>>({})
methods: {
/**
* 从vuex中拿取优惠券信息
*/
init() {
this.couponsList = this.$store.state.canUseCoupons;
},
/**
* 领取优惠券
*/
clickWay(coupon) {
useCoupon({
memberCouponId: coupon.id,
used: !this.routerVal.selectedCoupon.includes(coupon.id),
way: this.routerVal.way,
}).then((res) => {
if (res.data.success) {
uni.navigateBack();
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
},
},
};
onLoad((options) => {
routerVal.value = options || {}
})
watch(current, (val) => {
couponsList.value = val == 0
? store.state.canUseCoupons
: store.state.cantUseCoupons
})
onMounted(() => {
init()
})
function init() {
couponsList.value = store.state.canUseCoupons
}
function clickWay(coupon: any) {
useCoupon({
memberCouponId: coupon.id,
used: !routerVal.value.selectedCoupon.includes(coupon.id),
way: routerVal.value.way,
}).then((res) => {
if (res.data.success) {
uni.navigateBack()
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}
</script>
<style scoped lang="scss">
.desc {

View File

@@ -1,471 +1,393 @@
<template>
<view class="b-content">
<view class="navbar">
<!-- 循环出头部tab栏 -->
<view
v-for="(item, index) in navList"
:key="index"
class="nav-item"
@click="handleTabClick(index)"
><text :class="{ current: tabCurrentIndex === index }">{{
item.text
}}</text></view
>
</view>
<swiper
:current="tabCurrentIndex"
class="swiper-box"
duration="300"
@change="changeTab"
>
<swiper-item
class="tab-content"
v-for="(navItem, navIndex) in navList"
:key="navIndex"
>
<scroll-view
class="list-scroll-content"
scroll-y
@scrolltolower="loadData"
>
<!-- 空白页 -->
<u-empty
mode="coupon"
text="暂无优惠券了"
v-if="navItem.whetherEmpty"
></u-empty>
<!-- 数据 -->
<view
class="coupon-item"
:class="{ 'coupon-used': navIndex != 0 }"
v-for="(coupon, index) in navItem.dataList"
:key="index"
>
<view class="left">
<view class="wave-line">
<view
class="wave"
v-for="(item, index) in 12"
:key="index"
></view>
</view>
<view class="message">
<view class="price" v-if="coupon.couponType == 'DISCOUNT'"
>{{ coupon.discount }}</view
>
<view class="price" v-else>{{ coupon.price }}</view>
<view class="sub-price"
>{{unitPrice(coupon.consumeThreshold) }}可用</view
>
</view>
<view class="circle circle-top"></view>
<view class="circle circle-bottom"></view>
</view>
<view class="right" v-if="coupon">
<view class="content">
<view class="title-1">{{ coupon.title }}</view>
<view class="title-2"
>使用范围{{
coupon.scopeType == "ALL" && coupon.storeId == "0"
? "全平台"
: coupon.scopeType == "PORTION_GOODS"
? "部分商品"
: coupon.scopeType == "PORTION_GOODS_CATEGORY"
? "部分分类商品"
: coupon.storeName == "platform"
? "全平台"
: coupon.storeName + ""
}}使用</view
>
<view v-if="coupon.endTime">{{ coupon.endTime }}</view>
<view @click="couponDetail(coupon)"
>详细说明
<u-icon
style="float: right; margin-top: 10rpx"
name="arrow-right"
></u-icon>
</view>
</view>
<view class="jiao-1" v-if="navIndex == 0">
<text class="text-1">新到</text>
<text class="text-2" v-if="coupon.used_status == 1"
>将过期</text
>
</view>
<image
class="no-icon"
v-if="navIndex == 1"
src="@/static/img/used.png"
></image>
<image
class="no-icon"
v-if="navIndex == 2"
src="@/static/img/overdue.png"
></image>
<view
class="receive"
v-if="navIndex == 0"
@click="useItNow(coupon)"
>
<text>立即</text><br />
<text>使用</text>
</view>
<view class="bg-quan"> </view>
</view>
</view>
<uni-load-more :status="navItem.loadStatus"></uni-load-more>
</scroll-view>
</swiper-item>
</swiper>
</view>
</template>
<script>
import { getMemberCoupons } from "@/api/members.js";
export default {
data() {
return {
tabCurrentIndex: 0, //tab栏下标默认为0 未使用
navList: [
//每个tab存储的信息
{
text: "未使用",
loadStatus: "more",
dataList: [],
params: {
memberCouponStatus: "NEW",
pageNumber: 1,
pageSize: 10,
status: 1,
},
whetherEmpty: false,
},
{
text: "已使用",
loadStatus: "more",
dataList: [],
params: {
memberCouponStatus: "USED",
pageNumber: 1,
pageSize: 10,
status: 2,
},
whetherEmpty: false,
},
{
text: "已过期",
loadStatus: "more",
dataList: [],
params: {
memberCouponStatus: "EXPIRE",
pageNumber: 1,
pageSize: 10,
status: 3,
},
whetherEmpty: false,
},
],
couponList: [], //优惠券列表
};
},
onShow() {
this.navList[this.tabCurrentIndex].params.pageNumber = 1;
this.navList[this.tabCurrentIndex].dataList = [];
this.getData();
},
watch: {
/**
* 监听切换顶部tab栏实现刷新数据
*/
tabCurrentIndex(val) {
if (this.navList[val].dataList.length == 0) this.getData();
},
},
methods: {
/**
* 顶部tab点击
*/
handleTabClick(index) {
this.tabCurrentIndex = index;
},
/**
* 读取优惠券
*/
getData() {
uni.showLoading({
title: "加载中",
});
let index = this.tabCurrentIndex;
getMemberCoupons(this.navList[index].params).then((res) => {
uni.stopPullDownRefresh();
if (res.data.success) {
let data = res.data.result.records;
if (data.length == 0) {
if (res.data.pageNumber == 1) {
this.navList[index].whetherEmpty = true;
} else {
this.navList[index].loadStatus = "noMore";
}
} else if (data.length < 10) {
this.navList[index].loadStatus = "noMore";
this.navList[index].dataList.push(...data);
} else {
this.navList[index].dataList.push(...data);
}
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
/**
* 切换tab
*/
changeTab(e) {
this.tabCurrentIndex = e.target.current;
},
/**
* 加载数据
*/
loadData() {
let index = this.tabCurrentIndex;
if (this.navList[index].loadStatus != "noMore") {
this.navList[index].params.pageNumber++;
this.getData();
}
},
/**
* 立即使用优惠券
*/
useItNow(item) {
uni.navigateTo({
url: `/pages/navigation/search/searchPage?promotionsId=${item.couponId}&promotionType=COUPON`,
});
},
/**
* 优惠券详情
*/
couponDetail(item) {
uni.navigateTo({
url:
"/pages/cart/coupon/couponDetail?item=" +
encodeURIComponent(JSON.stringify(item)),
});
},
},
};
</script>
<style lang="scss" scoped>
page {
height: 100%;
}
$item-color: #fff;
.b-content {
background: $page-color-base;
height: 100%;
}
.swiper-box {
height: calc(100vh - 40px);
}
.list-scroll-content {
height: 100%;
width: 100%;
.coupon-item {
display: flex;
align-items: center;
height: 220rpx;
margin: 20rpx;
.left {
height: 100%;
width: 260rpx;
background-color: $light-color;
position: relative;
.message {
color: $font-color-white;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 40rpx;
view:nth-child(1) {
font-weight: bold;
font-size: 60rpx;
}
view:nth-child(2) {
font-size: $font-sm;
}
}
.wave-line {
height: 220rpx;
width: 8rpx;
position: absolute;
top: 0;
left: 0;
background-color: $light-color;
overflow: hidden;
.wave {
width: 8rpx;
height: 16rpx;
background-color: #ffffff;
border-radius: 0 16rpx 16rpx 0;
margin-top: 4rpx;
}
}
.circle {
width: 40rpx;
height: 40rpx;
background-color: $bg-color;
position: absolute;
border-radius: 50%;
z-index: 111;
}
.circle-top {
top: -20rpx;
right: -20rpx;
}
.circle-bottom {
bottom: -20rpx;
right: -20rpx;
}
}
.right {
display: flex;
justify-content: space-between;
align-items: center;
width: 450rpx;
font-size: $font-sm;
height: 100%;
background-color: #ffffff;
overflow: hidden;
position: relative;
.content {
color: #666666;
margin-left: 20rpx;
line-height: 2em;
> view:nth-child(1) {
color: #ff6262;
font-size: 30rpx;
}
.title-1,
.title-2,
.title-3 {
font-size: 25rpx;
}
}
.receive {
color: #ffffff;
background-color: $main-color;
border-radius: 50%;
width: 86rpx;
height: 86rpx;
text-align: center;
margin-right: 48rpx;
vertical-align: middle;
padding-top: 8rpx;
position: relative;
z-index: 2;
}
.jiao-1 {
background-color: #ffc71c;
width: 400rpx;
transform: rotate(45deg);
text-align: center;
position: absolute;
color: #ffffff;
right: -130rpx;
top: 0;
.text-1 {
margin-left: 68rpx;
font-size: 28rpx;
}
.text-2 {
margin-left: 68rpx;
font-size: 28rpx;
}
}
.no-icon {
border-radius: 50%;
width: 86rpx;
height: 86rpx;
margin-right: 48rpx;
position: relative;
z-index: 2;
}
.bg-quan {
width: 244rpx;
height: 244rpx;
border: 6rpx solid $main-color;
border-radius: 50%;
opacity: 0.1;
color: $main-color;
text-align: center;
padding-top: 30rpx;
font-size: 130rpx;
position: absolute;
right: -54rpx;
bottom: -60rpx;
}
}
}
}
.navbar {
display: flex;
height: 80rpx;
padding: 0 5px;
background: #fff;
color: $light-color;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.06);
position: relative;
z-index: 10;
.nav-item {
flex: 1;
height: 100%;
font-size: 26rpx;
color: $light-color;
position: relative;
text-align: center;
text {
line-height: 80rpx;
}
.current {
font-weight: bold;
font-size: 28rpx;
&:after {
content: "";
position: absolute;
bottom: 10rpx;
left: 108rpx;
width: 30rpx;
border-bottom: 2px solid $light-color;
}
}
}
}
</style>
<template>
<view class="b-content">
<view class="coupon-tabs">
<u-tabs
:list="navList"
keyName="text"
:scrollable="false"
:inactiveStyle="{ color: '#333' }"
v-model:current="tabCurrentIndex"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
></u-tabs>
</view>
<swiper
:current="tabCurrentIndex"
class="swiper-box"
duration="300"
@change="changeTab"
>
<swiper-item
class="tab-content"
v-for="(navItem, navIndex) in navList"
:key="navIndex"
>
<scroll-view
class="list-scroll-content"
scroll-y
@scrolltolower="loadData"
>
<u-empty
mode="coupon"
text="暂无优惠券了"
v-if="navItem.whetherEmpty"
></u-empty>
<view
class="coupon-card"
:class="{ 'coupon-used': navIndex != 0, 'coupon-expired': navIndex == 2 }"
v-for="(coupon, index) in navItem.dataList"
:key="index"
>
<view class="coupon-card-left">
<text class="coupon-price-symbol" v-if="coupon.couponType != 'DISCOUNT'">¥</text>
<text class="coupon-price-value">{{ coupon.couponType == 'DISCOUNT' ? coupon.discount + '折' : unitPrice(coupon.price) }}</text>
</view>
<view class="coupon-divider"></view>
<view class="coupon-card-right">
<view class="coupon-info">
<text class="coupon-card-name">{{ coupon.title }}</text>
<text class="coupon-card-desc">使用范围{{
coupon.scopeType == "ALL" && coupon.storeId == "0"
? "全平台"
: coupon.scopeType == "PORTION_GOODS"
? "部分商品"
: coupon.scopeType == "PORTION_GOODS_CATEGORY"
? "部分分类商品"
: coupon.storeName == "platform"
? "全平台"
: coupon.storeName + ""
}}使用</text>
<text class="coupon-card-time" v-if="coupon.endTime">{{ coupon.endTime }}</text>
</view>
<view class="coupon-status-btn" v-if="navIndex == 0" @click="useItNow(coupon)">
立即使用
</view>
<view class="coupon-status-btn disabled" v-else-if="navIndex == 1">
已使用
</view>
<view class="coupon-status-btn disabled" v-else>
已过期
</view>
</view>
</view>
<uni-load-more :status="navItem.loadStatus"></uni-load-more>
</scroll-view>
</swiper-item>
</swiper>
</view>
</template>
<script setup lang="ts">
import { getMemberCoupons } from '@/api/members.js'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { onShow } from '@dcloudio/uni-app'
import { computed, ref, watch } from 'vue'
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const tabCurrentIndex = ref(0)
const navList = ref([
{
text: '未使用',
loadStatus: 'more',
dataList: [] as any[],
params: {
memberCouponStatus: 'NEW',
pageNumber: 1,
pageSize: 10,
status: 1,
},
whetherEmpty: false,
},
{
text: '已使用',
loadStatus: 'more',
dataList: [] as any[],
params: {
memberCouponStatus: 'USED',
pageNumber: 1,
pageSize: 10,
status: 2,
},
whetherEmpty: false,
},
{
text: '已过期',
loadStatus: 'more',
dataList: [] as any[],
params: {
memberCouponStatus: 'EXPIRE',
pageNumber: 1,
pageSize: 10,
status: 3,
},
whetherEmpty: false,
},
])
onShow(() => {
navList.value[tabCurrentIndex.value].params.pageNumber = 1
navList.value[tabCurrentIndex.value].dataList = []
getData()
})
watch(tabCurrentIndex, (val) => {
if (navList.value[val].dataList.length == 0) getData()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function handleTabClick(index: number) {
tabCurrentIndex.value = index
}
function getData() {
uni.showLoading({ title: '加载中' })
const index = tabCurrentIndex.value
getMemberCoupons(navList.value[index].params).then((res) => {
uni.stopPullDownRefresh()
if (res.data.success) {
const data = res.data.result.records
if (data.length == 0) {
if (res.data.pageNumber == 1) {
navList.value[index].whetherEmpty = true
} else {
navList.value[index].loadStatus = 'noMore'
}
} else if (data.length < 10) {
navList.value[index].loadStatus = 'noMore'
navList.value[index].dataList.push(...data)
} else {
navList.value[index].dataList.push(...data)
}
}
hideLoadingIfNeeded()
})
}
function changeTab(e: any) {
tabCurrentIndex.value = e.detail.current
}
function loadData() {
const index = tabCurrentIndex.value
if (navList.value[index].loadStatus != 'noMore') {
navList.value[index].params.pageNumber++
getData()
}
}
function useItNow(item: any) {
uni.navigateTo({
url: `/pages/navigation/search/searchPage?promotionsId=${item.couponId}&promotionType=COUPON`,
})
}
function couponDetail(item: any) {
uni.navigateTo({
url: '/pages/cart/coupon/couponDetail?item=' + encodeURIComponent(JSON.stringify(item)),
})
}
</script>
<style lang="scss" scoped>
.b-content {
background: #f7f8fa;
min-height: 100vh;
}
.coupon-tabs {
background: #fff;
height: 88rpx;
box-shadow: 0 1rpx 8rpx rgba(0, 0, 0, 0.04);
position: relative;
z-index: 2;
}
:deep(.u-tabs),
:deep(.u-tabs__wrapper),
:deep(.u-tabs__wrapper__scroll-view-wrapper),
:deep(.u-tabs__wrapper__scroll-view),
:deep(.u-tabs__wrapper__nav) {
background: #fff;
height: 88rpx;
}
:deep(.u-tabs__wrapper__nav__item) {
min-height: 88rpx;
}
:deep(.u-tabs__wrapper__nav__item__text) {
color: #333;
font-size: 28rpx;
}
.swiper-box {
height: calc(100vh - 88rpx - var(--status-bar-height));
}
.list-scroll-content {
height: 100%;
width: 100%;
padding: 24rpx;
box-sizing: border-box;
}
.coupon-card {
display: flex;
align-items: stretch;
background: #fff;
border-radius: 24rpx;
margin-bottom: 24rpx;
position: relative;
box-shadow: none;
border: 1rpx solid #f0f1f5;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
&:active {
transform: scale(0.98);
}
&::before,
&::after {
content: "";
position: absolute;
width: 32rpx;
height: 32rpx;
background: #f7f8fa;
border-radius: 50%;
left: 204rpx;
z-index: 2;
box-shadow: inset 0 0 0 1rpx #eceef2;
}
&::before {
top: -16rpx;
}
&::after {
bottom: -16rpx;
}
&.coupon-used {
.coupon-card-left {
background: linear-gradient(135deg, #f5f5f5 0%, #eeeeee 100%);
color: #999;
}
.coupon-status-btn {
background: #ccc;
box-shadow: none;
}
}
&.coupon-expired {
.coupon-card-left {
background: linear-gradient(135deg, #f5f5f5 0%, #eeeeee 100%);
color: #999;
}
.coupon-status-btn {
background: #ccc;
box-shadow: none;
}
}
}
.coupon-card-left {
width: 220rpx;
min-height: 180rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: #ff3b30;
position: relative;
background: linear-gradient(135deg, #fff5f5 0%, #ffecec 100%);
}
.coupon-price-symbol {
font-size: 32rpx;
font-weight: 700;
margin-right: 4rpx;
}
.coupon-price-value {
font-size: 56rpx;
font-weight: 900;
line-height: 1;
letter-spacing: -1rpx;
}
.coupon-divider {
position: absolute;
left: 220rpx;
top: 24rpx;
bottom: 24rpx;
width: 0;
border-left: 2rpx dashed #eceef2;
}
.coupon-card-right {
flex: 1;
padding: 32rpx 32rpx 32rpx 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
background: #fff;
}
.coupon-info {
display: flex;
flex-direction: column;
gap: 12rpx;
flex: 1;
min-width: 0;
}
.coupon-card-name {
font-size: 30rpx;
color: #111;
font-weight: 700;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.coupon-card-desc {
font-size: 24rpx;
color: #8a8f99;
font-weight: 500;
}
.coupon-card-time {
font-size: 22rpx;
color: #b0b3bf;
}
.coupon-status-btn {
flex-shrink: 0;
padding: 16rpx 32rpx;
border-radius: 40rpx;
font-size: 26rpx;
font-weight: 700;
color: #fff;
background: linear-gradient(135deg, #ff6b35, #ff4b2b);
&:active {
opacity: 0.9;
}
&.disabled {
background: #ccc;
box-shadow: none;
}
}
</style>

View File

@@ -0,0 +1,87 @@
<template>
<view class="error-page">
<u-navbar title="支付失败" :border="false" :fixed="true" :placeholder="true" :auto-back="true"></u-navbar>
<view class="error-content">
<u-icon name="close-circle-fill" color="#f56c6c" size="120"></u-icon>
<text class="error-title">支付失败</text>
<text class="error-desc">{{ errorMessage || '支付过程中出现错误,请重试' }}</text>
<view class="btn-group">
<u-button type="primary" text="返回订单" @click="goBack"></u-button>
<u-button type="warning" text="去支付" @click="rePay"></u-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const errorMessage = ref('')
const orderSn = ref('')
onLoad((options: any) => {
if (options) {
errorMessage.value = options.message || ''
orderSn.value = options.orderSn || ''
}
})
function goBack() {
uni.navigateBack({
fail: () => {
uni.switchTab({ url: '/pages/tabbar/user/my' })
}
})
}
function rePay() {
if (orderSn.value) {
uni.navigateTo({
url: `/pages/cart/payment/payOrder?orderSn=${orderSn.value}`
})
} else {
goBack()
}
}
</script>
<style lang="scss" scoped>
.error-page {
min-height: 100vh;
background: #fff;
}
.error-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 200rpx 40rpx;
}
.error-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-top: 40rpx;
}
.error-desc {
font-size: 28rpx;
color: #999;
margin-top: 20rpx;
text-align: center;
}
.btn-group {
display: flex;
gap: 24rpx;
margin-top: 60rpx;
width: 100%;
:deep(.u-button) {
flex: 1;
}
}
</style>

View File

@@ -66,410 +66,264 @@
</div>
</div>
</template>
<script>
import * as API_Trade from "@/api/trade";
import {payCallback} from '@/api/members'
export default {
data() {
return {
//路径传参
routerVal: "",
//收银台参数
cashierParams: "",
//支付方式集合
payList: "",
//支付sn
sn: "",
//订单类型
orderType: "",
//支付异常
exception: {},
//支付表单
payForm: {},
//支付类型 APP/WECHAT_MP/H5/NATIVE app/微信小程序/h5/二维码
paymentType: "",
// 支付客户端 APP/NATIVE/JSAPI/H5
paymentClient: "",
//余额
walletValue: 0.0,
// 支付倒计时(毫秒)
autoCancelTime: 0,
};
},
onLoad(val) {
this.routerVal = val;
<script setup lang="ts">
import { ref, getCurrentInstance, onMounted } from 'vue'
import { onLoad, onBackPress } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Trade from '@/api/trade'
import { payCallback } from '@/api/members'
import { unitPrice } from '@/utils/filters.js'
//初始化参数
// #ifdef APP-PLUS
this.paymentType = "APP";
this.paymentClient = "APP";
//#endif
// #ifdef MP-WEIXIN
this.paymentType = "WECHAT_MP";
this.paymentClient = "MP";
//#endif
// #ifdef H5
this.paymentType = "H5";
//如果是微信浏览器则使用公众号支付否则使用h5
// 区别是h5是通过浏览器外部调用微信app进行支付而JSAPI则是 在微信浏览器内部,或者小程序 调用微信支付
this.paymentClient = this.isWeiXin() ? "JSAPI" : "H5";
//#endif
const store = useStore()
const { proxy } = getCurrentInstance()!
const routerVal = ref<Record<string, string>>({})
const cashierParams = ref<Record<string, any>>({ price: 0 })
const payList = ref<string[]>([])
const sn = ref('')
const orderType = ref('')
const paymentType = ref('')
const paymentClient = ref('')
const walletValue = ref(0)
const autoCancelTime = ref(0)
onLoad((val) => {
routerVal.value = val || {}
// #ifdef APP-PLUS
paymentType.value = 'APP'
paymentClient.value = 'APP'
// #endif
// #ifdef MP-WEIXIN
paymentType.value = 'WECHAT_MP'
paymentClient.value = 'MP'
// #endif
// #ifdef H5
paymentType.value = 'H5'
paymentClient.value = isWeiXin() ? 'JSAPI' : 'H5'
// #endif
})
//
},
onBackPress(e) {
if (e.from == "backbutton") {
if(this.routerVal.recharge_sn){
uni.switchTab({
url: '/pages/tabbar/user/my'
});
}
else{
uni.navigateTo({
url: "/pages/order/myOrder?status=0",
});
}
return true; //阻止默认返回行为
}
},
mounted() {
this.cashierData();
},
methods: {
onBackPress((e) => {
if (e.from == 'backbutton') {
if (routerVal.value.recharge_sn) {
uni.switchTab({ url: '/pages/tabbar/user/my' })
} else {
uni.navigateTo({ url: '/pages/order/myOrder?status=0' })
}
return true
}
return false
})
/**
* 支付成功后跳转
*/
callback(paymentMethod){
uni.navigateTo({
url: "/pages/cart/payment/success?paymentMethod=" +
paymentMethod +
"&payPrice=" +
this.cashierParams.price+
"&orderType="+this.orderType
});
},
/**
* 获取收银详情
*/
cashierData() {
let parms = {};
onMounted(() => {
cashierData()
})
if (this.routerVal.recharge_sn) {
// 判断当前是否是充值
this.sn = this.routerVal.recharge_sn;
this.orderType = "RECHARGE";
} else if (this.routerVal.trade_sn) {
this.sn = this.routerVal.trade_sn;
this.orderType = "TRADE";
} else {
this.sn = this.routerVal.order_sn;
this.orderType = "ORDER";
}
parms.sn = this.sn;
parms.orderType = this.orderType;
parms.clientType = this.paymentType;
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
API_Trade.getCashierData(parms).then((res) => {
if(res.data.success){
this.cashierParams = res.data.result;
function callback(paymentMethod: string) {
uni.navigateTo({
url:
'/pages/cart/payment/success?paymentMethod=' +
paymentMethod +
'&payPrice=' +
cashierParams.value.price +
'&orderType=' +
orderType.value,
})
}
// #ifdef MP-WEIXIN
this.payList = res.data.result.support.filter((item) => {
return item != "ALIPAY";
});
// #endif
function cashierData() {
const parms: Record<string, string> = {}
if(this.routerVal.recharge_sn){
this.payList = res.data.result.support.filter((item) => {
return item != "WALLET";
})
}
else{
this.payList = res.data.result.support;
}
// #ifdef H5
//判断是否微信浏览器
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
this.payList = res.data.result.support.filter((item) => {
return item != "ALIPAY";
});
// 充值的话仅保留微信支付
if(this.orderType == "RECHARGE"){
this.payList = res.data.result.support.filter((item) => {
return item == "WECHAT";
});
}
}
// #endif
if (routerVal.value.recharge_sn) {
sn.value = routerVal.value.recharge_sn
orderType.value = 'RECHARGE'
} else if (routerVal.value.trade_sn) {
sn.value = routerVal.value.trade_sn
orderType.value = 'TRADE'
} else {
sn.value = routerVal.value.order_sn
orderType.value = 'ORDER'
}
parms.sn = sn.value
parms.orderType = orderType.value
parms.clientType = paymentType.value
this.walletValue = res.data.result.walletValue;
const cancelAt = Number(res.data.result.autoCancel);
this.autoCancelTime = cancelAt > 0 ? Math.max(cancelAt - Date.now(), 0) : 0;
}
else if(res.data.code == 32000){
setTimeout(()=>{
uni.redirectTo({
url: `/pages/order/myOrder?status=0`
});
},500)
}
});
},
API_Trade.getCashierData(parms).then((res) => {
if (res.data.success) {
cashierParams.value = res.data.result
// #ifdef MP-WEIXIN
payList.value = res.data.result.support.filter((item: string) => item != 'ALIPAY')
// #endif
awaitPay(payment){
this.$u.throttle(()=>{
this.pay(payment)
}, 2000)
},
if (routerVal.value.recharge_sn) {
payList.value = res.data.result.support.filter((item: string) => item != 'WALLET')
} else {
payList.value = res.data.result.support
}
padTime(val) {
return String(val ?? 0).padStart(2, '0');
},
// #ifdef H5
const ua = window.navigator.userAgent.toLowerCase()
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
payList.value = res.data.result.support.filter((item: string) => item != 'ALIPAY')
if (orderType.value == 'RECHARGE') {
payList.value = res.data.result.support.filter((item: string) => item == 'WECHAT')
}
}
// #endif
onPayTimeout() {
uni.showToast({
title: '支付超时,请重新下单',
icon: 'none',
});
setTimeout(() => {
uni.redirectTo({
url: '/pages/order/myOrder?status=0',
});
}, 1500);
},
walletValue.value = res.data.result.walletValue
const cancelAt = Number(res.data.result.autoCancel)
autoCancelTime.value = cancelAt > 0 ? Math.max(cancelAt - Date.now(), 0) : 0
} else if (res.data.code == 32000) {
setTimeout(() => {
uni.redirectTo({ url: '/pages/order/myOrder?status=0' })
}, 500)
}
})
}
//订单支付
async pay(payment) {
// 支付编号
const sn = this.sn;
// 交易类型【交易号|订单号】
const orderType = this.orderType;
function awaitPay(payment: string) {
proxy.$u.throttle(() => {
pay(payment)
}, 2000)
}
const clientType = this.paymentType;
let params = {
sn,
orderType,
clientType,
};
function padTime(val: number) {
return String(val ?? 0).padStart(2, '0')
}
//支付方式 WECHAT/ALIPAY
const paymentMethod = payment;
// 客户端类型 APP/NATIVE/JSAPI/H5
const paymentClient = this.paymentClient;
uni.showLoading({
title: "正在唤起支付...",
mask:true
});
// #ifdef APP-PLUS
//APP pay
// 初始化支付签名
await API_Trade.initiatePay(paymentMethod, paymentClient, params).then(
(signXml) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
//如果支付异常
if (!signXml.data.success) {
uni.showToast({
title: signXml.data.message,
duration: 2000
});
return;
}
let payForm = signXml.data.result;
let paymentType = paymentMethod === "WECHAT" ? "wxpay" : "alipay";
if(paymentMethod === "WALLET"){
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
}
else{
uni.requestPayment({
provider: paymentType,
orderInfo: payForm || '',
success: (e) => {
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
},
fail: (e) => {
console.log(this);
this.exception = e;
uni.showModal({
content: "支付失败,如果您已支付,请勿反复支付",
showCancel: false,
});
},
});
}
}
);
//APP pay
// #endif
function onPayTimeout() {
uni.showToast({ title: '支付超时,请重新下单', icon: 'none' })
setTimeout(() => {
uni.redirectTo({ url: '/pages/order/myOrder?status=0' })
}, 1500)
}
//#ifdef H5
//H5 pay
await API_Trade.initiatePay(paymentMethod, paymentClient, params).then(
(res) => {
let response = res.data;
//如果非支付宝支付才需要进行判定因为支付宝h5支付是直接输出的没有返回所谓的消息状态
if(paymentMethod !== "ALIPAY"){
//如果支付异常
if (!response.success) {
uni.showToast({
title: response.message,
duration: 2000,
icon:"none"
});
return;
}
}
if (paymentMethod === "ALIPAY") {
document.write(response);
} else if (paymentMethod === "WECHAT") {
if (this.isWeiXin()) {
//微信公众号支付
WeixinJSBridge.invoke(
"getBrandWCPayRequest",
response.result,
(res) => {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok但并不保证它绝对可靠。
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
} else {
uni.showModal({
content: "支付失败,如果您已支付,请勿反复支付",
showCancel: false,
});
}
}
);
if (this.$store.state.isShowToast){ uni.hideLoading() };
} else {
window.location.href = JSON.parse(response.result).h5_url;
const searchParams = {
...params,
price:this.cashierParams,
}
const timer = setInterval(()=>{
payCallback(searchParams).then(res=>{
if(res.data.result){
clearTimeout(timer);
uni.navigateTo({
url:"/pages/order/myOrder"
})
}
})
},3000)
if (this.$store.state.isShowToast){ uni.hideLoading() };
}
} else if (paymentMethod === "WALLET") {
uni.showToast({
title: response.message,
icon: "none",
});
if (response.success) {
this.callback(paymentMethod)
}
}
}
);
//H5pay
// #endif
function goPayError(message = '支付失败,如果您已支付,请勿反复支付') {
const query = [`orderSn=${encodeURIComponent(sn.value)}`, `message=${encodeURIComponent(message)}`]
uni.navigateTo({ url: `/pages/cart/payment/error?${query.join('&')}` })
}
//#ifdef MP-WEIXIN
//微信小程序
await API_Trade.initiatePay(paymentMethod, paymentClient, params).then(
(res) => {
let response = res.data.result;
//如果支付异常
if (!res.data.success) {
uni.showModal({
content: res.data.message,
showCancel: false,
})
return;
}
if (paymentMethod === "WECHAT") {
uni.requestPayment({
provider: "wxpay",
appid: response.appid,
timeStamp: response.timeStamp,
nonceStr: response.nonceStr,
package: response.package,
signType: response.signType,
paySign: response.paySign,
success: (e) => {
console.log(e);
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
},
fail: (e) => {
console.log(e);
this.exception = e;
uni.showModal({
content: "支付失败,如果您已支付,请勿反复支付",
showCancel: false,
});
},
});
} else {
uni.showToast({
icon: "none",
title: "支付成功!",
});
this.callback(paymentMethod)
}
}
);
// #endif
},
isWeiXin() {
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == "micromessenger") {
return true;
} else {
return false;
}
},
},
};
async function pay(payment: string) {
const params = {
sn: sn.value,
orderType: orderType.value,
clientType: paymentType.value,
}
const paymentMethod = payment
const client = paymentClient.value
uni.showLoading({ title: '正在唤起支付...', mask: true })
// #ifdef APP-PLUS
await API_Trade.initiatePay(paymentMethod, client, params).then((signXml) => {
hideLoadingIfNeeded()
if (!signXml.data.success) {
uni.showToast({ title: signXml.data.message, duration: 2000 })
return
}
const payForm = signXml.data.result
const provider = paymentMethod === 'WECHAT' ? 'wxpay' : 'alipay'
if (paymentMethod === 'WALLET') {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
} else {
uni.requestPayment({
provider,
orderInfo: payForm || '',
success: () => {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
},
fail: () => {
goPayError()
},
})
}
})
// #endif
// #ifdef H5
await API_Trade.initiatePay(paymentMethod, client, params).then((res) => {
const response = res.data
if (paymentMethod !== 'ALIPAY' && !response.success) {
uni.showToast({ title: response.message, duration: 2000, icon: 'none' })
return
}
if (paymentMethod === 'ALIPAY') {
document.write(response)
} else if (paymentMethod === 'WECHAT') {
if (isWeiXin()) {
WeixinJSBridge.invoke('getBrandWCPayRequest', response.result, (payRes: any) => {
if (payRes.err_msg == 'get_brand_wcpay_request:ok') {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
} else {
goPayError()
}
})
hideLoadingIfNeeded()
} else {
window.location.href = JSON.parse(response.result).h5_url
const searchParams = { ...params, price: cashierParams.value }
const timer = setInterval(() => {
payCallback(searchParams).then((cbRes) => {
if (cbRes.data.result) {
clearInterval(timer)
uni.navigateTo({ url: '/pages/order/myOrder' })
}
})
}, 3000)
hideLoadingIfNeeded()
}
} else if (paymentMethod === 'WALLET') {
uni.showToast({ title: response.message, icon: 'none' })
if (response.success) callback(paymentMethod)
}
})
// #endif
// #ifdef MP-WEIXIN
await API_Trade.initiatePay(paymentMethod, client, params).then((res) => {
const response = res.data.result
if (!res.data.success) {
uni.showModal({ content: res.data.message, showCancel: false })
return
}
if (paymentMethod === 'WECHAT') {
uni.requestPayment({
provider: 'wxpay',
appid: response.appid,
timeStamp: response.timeStamp,
nonceStr: response.nonceStr,
package: response.package,
signType: response.signType,
paySign: response.paySign,
success: () => {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
},
fail: () => {
goPayError()
},
})
} else {
uni.showToast({ icon: 'none', title: '支付成功!' })
callback(paymentMethod)
}
})
// #endif
}
function isWeiXin() {
const ua = window.navigator.userAgent.toLowerCase()
return ua.match(/MicroMessenger/i) == 'micromessenger'
}
</script>
<style scoped lang="scss">
.method_icon {

View File

@@ -49,7 +49,13 @@
<!-- 倒计时 -->
<div class="count-down" v-if="!isOver && master.toBeGroupedNum">
<u-count-down bg-color="#ededed" :hide-zero-day="true" @end="isOver" :timestamp="timeStamp"></u-count-down>
<u-count-down
v-if="timeStamp > 0"
bgColor="#ededed"
:time="timeStamp * 1000"
format="HH:mm:ss"
@finish="onCountDownEnd"
></u-count-down>
</div>
<div class="user-list" v-if="data.pintuanMemberVOS">
@@ -59,173 +65,184 @@
</div>
</div>
<popupGoods :addr="addr" ref="popupGoods" :buyMask="maskFlag" @closeBuy="closePopupBuy" :goodsDetail="goodsDetail" :goodsSpec="goodsSpec" v-if="goodsDetail.id " @handleClickSku="getGoodsDetail" />
<shares @close="closeShare" :link="'/pages/cart/payment/shareOrderGoods?sn='+this.routers.sn+'&sku='+this.routers.sku+'&goodsId='+this.routers.goodsId" type="pintuan"
:thumbnail="data.promotionGoods.thumbnail" :goodsName="data.promotionGoods.goodsName" v-if="shareFlag " />
<popupGoods
:addr="addr"
ref="popupGoodsRef"
:buyMask="maskFlag"
@closeBuy="closePopupBuy"
:goodsDetail="goodsDetail"
:goodsSpec="goodsSpec"
v-if="goodsDetail.id"
@handleClickSku="getGoodsDetail"
/>
<shares
@close="closeShare"
:link="shareLink"
type="pintuan"
:thumbnail="data.promotionGoods.thumbnail"
:goodsName="data.promotionGoods.goodsName"
v-if="shareFlag"
/>
</view>
</template>
<script>
import { getGoods } from "@/api/goods.js";
import { getPinTuanShare } from "@/api/order";
import shares from "@/components/m-share/index";
import storage from "@/utils/storage.js";
import popupGoods from "@/components/m-buy/goods"; //购物车商品的模块
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getGoods } from '@/api/goods.js'
import { getPinTuanShare } from '@/api/order'
import shares from '@/components/m-share/index'
import storage from '@/utils/storage.js'
import popupGoods from '@/components/m-buy/goods'
import { unitPrice } from '@/utils/filters.js'
export default {
data() {
return {
flag: false, //判断接口是否正常请求
addr: {
id: "",
},
maskFlag: false, //商品弹框
timeStamp: 0,
shareFlag: false,
data: "",
isMaster: true,
selectedGoods: "", //选择的商品规格昵称
routers: "", //传参数据
goodsDetail: "", //商品详情
goodsSpec: "",
master: "", // 团长
PromotionList: "", //优惠集合
isGroup: false, //是否拼团
isOver: false, //是否结束活动
isBuy: false, //当前用户是是否购买
};
},
components: {
shares,
popupGoods,
},
watch: {
isGroup(val) {
if (val) {
let timer = setInterval(() => {
this.$refs.popupGoods.buyType = "PINTUAN";
clearInterval(timer);
}, 100);
} else {
this.$refs.popupGoods.buyType = "";
const store = useStore()
const popupGoodsRef = ref<any>(null)
const flag = ref(false)
const addr = ref({ id: '' })
const maskFlag = ref(false)
const timeStamp = ref(0)
const shareFlag = ref(false)
const data = ref<any>(null)
const isMaster = ref(true)
const selectedGoods = ref<any>(null)
const routers = ref<Record<string, string>>({})
const goodsDetail = ref<any>({})
const goodsSpec = ref<any>(null)
const master = ref<any>(null)
const PromotionList = ref<any>(null)
const isGroup = ref(false)
const isOver = ref(false)
const isBuy = ref(false)
const shareLink = computed(() => {
const { sn, sku, goodsId } = routers.value
return `/pages/cart/payment/shareOrderGoods?sn=${sn}&sku=${sku}&goodsId=${goodsId}`
})
watch(isGroup, (val) => {
if (val) {
const timer = setInterval(() => {
if (popupGoodsRef.value) {
popupGoodsRef.value.buyType = 'PINTUAN'
}
},
},
onLoad(options) {
this.routers = options;
},
mounted() {
this.init(this.routers.sn, this.routers.sku);
},
methods: {
closeShare() {
this.shareFlag = false;
},
// 这里的话得先跳到商品详情才能购买商品
toBuy() {
this.maskFlag = true;
this.$refs.popupGoods.parentOrder = {
...this.master,
orderSn: this.routers.sn,
};
this.$refs.popupGoods.isMask = true;
this.$refs.popupGoods.isClose = true;
this.$refs.popupGoods.buyType = "PINTUAN";
},
// 分享
share() {
this.shareFlag = true;
},
closePopupBuy(val) {
this.maskFlag = false;
},
// 实例化本页面
async init(sn, sku) {
let res = await getPinTuanShare(sn, sku);
if (res.data.success && res.data.result.promotionGoods) {
this.flag = true;
this.data = res.data.result;
this.selectedGoods = res.data.result.promotionGoods;
let endTime = Date.parse(
res.data.result.promotionGoods.endTime.replace(/-/g, "/")
);
// 获取当前剩余的拼团商品时间
let timeStamp = Date.parse(new Date(endTime)) / 1000;
clearInterval(timer)
}, 100)
} else if (popupGoodsRef.value) {
popupGoodsRef.value.buyType = ''
}
})
// 获取当前时间时间戳
let dateTime = Date.parse(new Date()) / 1000;
onLoad((options) => {
routers.value = options || {}
})
this.timeStamp = parseInt(timeStamp - dateTime);
onMounted(() => {
init(routers.value.sn, routers.value.sku)
})
this.timeStamp <= 0 ? (this.isOver = true) : (this.isOver = false);
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
// 获取剩余拼团人数
this.master =
res.data.result.pintuanMemberVOS.length != 0 &&
res.data.result.pintuanMemberVOS.filter((item) => {
return item.orderSn == "";
})[0];
function closeShare() {
shareFlag.value = false
}
// 获取当前是否是拼团本人
if (
storage.getUserInfo(this.routers.sku, this.routers.goodsId).id ==
this.master.memberId
) {
this.isMaster = true;
} else {
this.isMaster = false;
// 获取商品详情
this.getGoodsDetail({
id: this.routers.sku,
goodsId: this.routers.goodsId,
});
function onCountDownEnd() {
isOver.value = true
}
function toBuy() {
maskFlag.value = true
if (!popupGoodsRef.value) return
popupGoodsRef.value.parentOrder = {
...master.value,
orderSn: routers.value.sn,
}
popupGoodsRef.value.isMask = true
popupGoodsRef.value.isClose = true
popupGoodsRef.value.buyType = 'PINTUAN'
}
function share() {
shareFlag.value = true
}
function closePopupBuy() {
maskFlag.value = false
}
async function init(sn: string, sku: string) {
const res = await getPinTuanShare(sn, sku)
if (res.data.success && res.data.result.promotionGoods) {
flag.value = true
data.value = res.data.result
selectedGoods.value = res.data.result.promotionGoods
const endTime = Date.parse(
res.data.result.promotionGoods.endTime.replace(/-/g, '/')
)
const endTimestamp = Date.parse(new Date(endTime) as any) / 1000
const dateTime = Date.parse(new Date() as any) / 1000
timeStamp.value = parseInt(String(endTimestamp - dateTime))
isOver.value = timeStamp.value <= 0
master.value =
res.data.result.pintuanMemberVOS.length != 0 &&
res.data.result.pintuanMemberVOS.filter((item: any) => item.orderSn == '')[0]
if (
storage.getUserInfo(routers.value.sku, routers.value.goodsId).id ==
master.value.memberId
) {
isMaster.value = true
} else {
isMaster.value = false
getGoodsDetail({
id: routers.value.sku,
goodsId: routers.value.goodsId,
})
}
if (storage.getUserInfo().id) {
const bought = res.data.result.pintuanMemberVOS.filter(
(item: any) => item.memberId == storage.getUserInfo().id
)
isBuy.value = bought.length != 0
}
} else {
uni.showToast({
title: '当前拼团单有误!请联系管理员重试',
duration: 2000,
icon: 'none',
})
}
}
function getGoodsDetail(val: { id: string; goodsId: string }) {
const { id, goodsId } = val
uni.showLoading({ title: '加载中', mask: true })
getGoods(id, goodsId).then((response) => {
goodsDetail.value = response.data.result.data
selectedGoods.value = response.data.result.data
goodsSpec.value = response.data.result.specs
hideLoadingIfNeeded()
PromotionList.value = response.data.result.promotionMap
if (PromotionList.value) {
Object.keys(PromotionList.value).forEach((item) => {
if (item.indexOf('PINTUAN') == 0) {
isGroup.value = true
}
})
}
})
}
// 获取当前商品是否已经购买
if (storage.getUserInfo().id) {
let isBuy = res.data.result.pintuanMemberVOS.filter((item) => {
return item.memberId == storage.getUserInfo().id;
});
isBuy.length != 0 ? (this.isBuy = true) : (this.isBuy = false);
}
} else {
uni.showToast({
title: "当前拼团单有误!请联系管理员重试",
duration: 2000,
icon: "none",
});
}
},
// 获取商品详情
getGoodsDetail(val) {
let { id, goodsId } = val;
uni.showLoading({
title: "加载中",
mask: true,
});
getGoods(id, goodsId).then((response) => {
this.goodsDetail = response.data.result.data;
this.selectedGoods = response.data.result.data;
this.goodsSpec = response.data.result.specs;
if (this.$store.state.isShowToast){ uni.hideLoading() };
this.PromotionList = response.data.result.promotionMap;
// 判断是否拼团活动 如果有则显示拼团活动信息
this.PromotionList &&
Object.keys(this.PromotionList).forEach((item) => {
if (item.indexOf("PINTUAN") == 0) {
this.isGroup = true;
}
});
});
},
handleClickHome() {
uni.switchTab({
url: "/pages/tabbar/home/index",
});
},
},
};
function handleClickHome() {
uni.switchTab({ url: '/pages/tabbar/home/index' })
}
</script>
<style lang="scss" scoped>

View File

@@ -5,7 +5,7 @@
{{unitPrice(Number(payPrice)) }}
</div>
<div class="pay-btns">
<div v-show="!from" @click="checkOrder">查看{{ this.orderType == "RECHARGE" ? '余额' : '订单' }}</div>
<div v-show="!from" @click="checkOrder">查看{{ orderType == "RECHARGE" ? '余额' : '订单' }}</div>
<div @click="navigateTo('/pages/tabbar/home/index', 'switch')">回到首页</div>
</div>
</div>
@@ -24,73 +24,55 @@
</div>
</template>
<script>
import goodsRecommend from "@/components/m-goods-recommend";
export default {
data() {
return {
checked: false,
paymentMethod: "",
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import goodsRecommend from '@/components/m-goods-recommend'
import { unitPrice } from '@/utils/filters.js'
from: "",
payPrice: 0,
goodsList: [],
activeColor: this.$mainColor,
};
},
components: {
goodsRecommend,
},
onLoad(options) {
this.paymentMethod = options.paymentMethod || "";
this.from = options.from || "";
this.payPrice = options.payPrice || 0;
this.orderType = options.orderType;
const paymentMethod = ref('')
const from = ref('')
const payPrice = ref<number | string>(0)
const orderType = ref('')
},
methods: {
paymentTypeFilter(val) {
switch (val) {
case "WECHAT":
return "微信";
case "ALIPAY":
return "支付宝";
case "WALLET":
return "余额支付";
default:
return "";
}
},
checkOrder() {
/**
* 查看订单
* 1.充值跳转到明细里面
* 2.支付跳转到订单详情
*/
if (this.orderType == "RECHARGE") {
uni.reLaunch({
url: `/pages/mine/deposit/operation`,
});
} else {
this.navigateTo("/pages/order/myOrder?status=0");
}
},
onLoad((options) => {
paymentMethod.value = options.paymentMethod || ''
from.value = options.from || ''
payPrice.value = options.payPrice || 0
orderType.value = options.orderType || ''
})
navigateTo(url, type) {
if (type === "switch") {
uni.switchTab({
url,
});
} else {
uni.redirectTo({
url,
});
}
},
},
};
</script>
<style scoped lang="scss">
function paymentTypeFilter(val: string) {
switch (val) {
case 'WECHAT':
return '微信'
case 'ALIPAY':
return '支付宝'
case 'WALLET':
return '余额支付'
default:
return ''
}
}
function checkOrder() {
if (orderType.value == 'RECHARGE') {
uni.reLaunch({
url: '/pages/mine/deposit/operation',
})
} else {
navigateTo('/pages/order/myOrder?status=0')
}
}
function navigateTo(url: string, type?: string) {
if (type === 'switch') {
uni.switchTab({ url })
} else {
uni.redirectTo({ url })
}
}
</script><style scoped lang="scss">
.subscribe {
justify-content: space-between;
align-items: center;

View File

@@ -2,21 +2,16 @@
<default-page v-if="type" :type="type" title="您的设备已断网" :isBtn="false" />
</template>
<script>
import defaultPage from '@/components/default-page/default-page.vue';
export default {
components: {
defaultPage
},
data() {
return {
type: undefined
}
},
onLoad(options) {
}
}
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import defaultPage from '@/components/default-page/default-page.vue'
const type = ref<string>()
onLoad(() => {
type.value = 'msg'
})
</script>
<style>

View File

@@ -1,7 +1,14 @@
<template>
<view class="add-address">
<u-form :model="form" ref="uForm" error-type="toast" :rules="rules">
<u-form-item label="收货人" label-width="130" prop="name" :border-bottom="true">
<up-form
:model="form"
ref="uForm"
error-type="toast"
:rules="rules"
label-position="left"
label-width="180rpx"
>
<up-form-item label="收货人" label-width="180rpx" prop="name" :border-bottom="true">
<u-input
v-model="form.name"
border="none"
@@ -9,9 +16,9 @@
clearable
placeholder="请输入收货人姓名"
/>
</u-form-item>
</up-form-item>
<u-form-item label="手机号码" label-width="130" prop="mobile" :border-bottom="true">
<up-form-item label="手机号码" label-width="180rpx" prop="mobile" :border-bottom="true">
<u-input
v-model="form.mobile"
type="number"
@@ -20,18 +27,18 @@
input-align="right"
placeholder="请输入收货人手机号码"
/>
</u-form-item>
</up-form-item>
<u-form-item label="所在区域" label-width="130" prop="___path" :border-bottom="true">
<up-form-item label="所在区域" label-width="180rpx" prop="___path" :border-bottom="true">
<view class="form-value" @click="showPicker">
{{ form.___path || '请选择所在地区' }}
</view>
<template #right>
<u-icon name="arrow-right" color="#ccc" size="16"></u-icon>
</template>
</u-form-item>
</up-form-item>
<u-form-item class="detailAddress" label="详细地址" label-width="130" prop="detail" :border-bottom="true">
<up-form-item class="detailAddress" label="详细地址" label-width="180rpx" prop="detail" :border-bottom="true">
<u-input
type="textarea"
v-model="form.detail"
@@ -40,16 +47,16 @@
border="none"
placeholder="街道楼牌号等"
/>
</u-form-item>
</up-form-item>
<u-form-item label="地址别名" label-width="130" :border-bottom="false">
<up-form-item label="地址别名" label-width="180rpx" :border-bottom="false">
<u-input
v-model="form.alias"
border="none"
input-align="right"
placeholder="请输入地址别名"
/>
</u-form-item>
</up-form-item>
<view class="default-row">
<u-checkbox
@@ -63,7 +70,7 @@
</view>
<view class="saveBtn" @click="save">保存</view>
</u-form>
</up-form>
<m-city
:provinceData="list"
@@ -76,266 +83,208 @@
<uniMap v-if="mapFlag" @close="closeMap" @callback="callBackAddress" />
</view>
</template>
<script>
import { addAddress, editAddress, getAddressDetail } from "@/api/address.js";
import city from "@/components/m-city/m-city.vue";
import uniMap from "@/components/uniMap";
import permision from "@/js_sdk/wa-permission/permission.js";
export default {
components: {
"m-city": city,
uniMap,
<script setup lang="ts">
import { ref, reactive, computed, getCurrentInstance } from 'vue'
import { onLoad, onShow, onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { addAddress as addAddressApi, editAddress, getAddressDetail } from '@/api/address.js'
import MCity from '@/components/m-city/m-city.vue'
import uniMap from '@/components/uniMap'
import permision from '@/js_sdk/wa-permission/permission.js'
const store = useStore()
const { proxy } = getCurrentInstance()!
const lightColor = computed(() => store.getters.lightColor)
const mapFlag = ref(false)
const routerVal = ref<Record<string, string>>({})
const uForm = ref<any>(null)
const cityPicker = ref<any>(null)
const form = reactive<Record<string, any>>({
detail: '',
name: '',
mobile: '',
consigneeAddressIdPath: [],
consigneeAddressPath: [],
___path: '',
isDefault: false,
})
const list = ref([
{
id: '',
localName: '请选择',
children: [],
},
onShow() {
// 判断当前系统权限定位是否开启
},
methods: {
// 关闭地图
closeMap() {
this.mapFlag = false;
])
const rules = {
name: [
{
required: true,
message: '收货人姓名不能为空',
trigger: ['blur', 'change'],
},
// 打开地图并访问权限
clickUniMap() {
// #ifdef APP-PLUS
if (plus.os.name == "iOS") {
// ios系统
permision.judgeIosPermission("location")
? (this.mapFlag = true)
: this.refuseMap();
} else {
// 安卓
this.requestAndroidPermission(
"android.permission.ACCESS_FINE_LOCATION"
);
}
// #endif
// #ifndef APP-PLUS
this.mapFlag = true;
// #endif
],
mobile: [
{
required: true,
message: '手机号码不能为空',
trigger: ['blur', 'change'],
},
// 如果拒绝权限 提示区设置
refuseMap() {
uni.showModal({
title: "温馨提示",
content: "您已拒绝定位,请开启",
confirmText: "去设置",
success(res) {
if (res.confirm) {
//打开授权设置
// #ifndef MP-WEIXIN
uni.getSystemInfo({
success(res) {
if (res.platform == "ios") {
//IOS
plus.runtime.openURL("app-settings://");
} else if (res.platform == "android") {
//安卓
let main = plus.android.runtimeMainActivity();
let Intent = plus.android.importClass(
"android.content.Intent"
);
let mIntent = new Intent("android.settings.ACTION_SETTINGS");
main.startActivity(mIntent);
}
},
});
// #endif
}
},
});
{
validator: (_rule: unknown, value: string) => proxy.$u.test.mobile(value),
message: '手机号码不正确',
trigger: ['change', 'blur'],
},
// 获取安卓是否拥有地址权限
async requestAndroidPermission(permisionID) {
var result = await permision.requestAndroidPermission(permisionID);
if (result == 1) {
this.mapFlag = true;
} else {
this.refuseMap();
}
],
___path: [
{
required: true,
message: '请选择所在区域',
trigger: ['change'],
},
// 选择地址后数据的回调
callBackAddress(val) {
console.log(val)
uni.showLoading({
title: "加载中",
});
if (val.regeocode && val) {
let address = val.regeocode;
this.form.detail = address.formatted_address; //地址详情
this.form.___path = val.data.result.name;
this.form.consigneeAddressIdPath = val.data.result.id; // 地址id分割
this.form.consigneeAddressPath = val.data.result.name; //地址名称, ''分割
this.form.lat = val.latitude; //纬度
this.form.lon = val.longitude; //经度
uni.hideLoading();
}
this.mapFlag = !this.mapFlag; //关闭地图
],
detail: [
{
required: true,
message: '请填写详细地址',
trigger: ['blur', 'change'],
},
],
}
// 保存当前 地址
save() {
this.$refs.uForm.validate().then(() => {
const params = { ...this.form };
delete params.___path;
onShow(() => {})
if (Array.isArray(params.consigneeAddressIdPath)) {
params.consigneeAddressIdPath = params.consigneeAddressIdPath.join(",");
}
if (Array.isArray(params.consigneeAddressPath)) {
params.consigneeAddressPath = params.consigneeAddressPath.join(",");
}
onLoad((option) => {
uni.showLoading({ title: '加载中' })
routerVal.value = option || {}
if (option.id) {
getAddressDetail(option.id).then((res) => {
const params = res.data.result
params.___path = params.consigneeAddressPath
Object.assign(form, params)
if (store.state.isShowToast) uni.hideLoading()
})
}
uni.hideLoading()
})
if (!params.id) {
addAddress(params).then((res) => {
if (res.data.success) {
uni.navigateBack();
} else {
uni.showToast({
title: res.data.message || "保存失败",
icon: "none",
});
onReady(() => {
uForm.value?.setRules(rules)
})
function closeMap() {
mapFlag.value = false
}
function refuseMap() {
uni.showModal({
title: '温馨提示',
content: '您已拒绝定位,请开启',
confirmText: '去设置',
success(res) {
if (res.confirm) {
// #ifndef MP-WEIXIN
uni.getSystemInfo({
success(sysRes) {
if (sysRes.platform == 'ios') {
plus.runtime.openURL('app-settings://')
} else if (sysRes.platform == 'android') {
const main = plus.android.runtimeMainActivity()
const Intent = plus.android.importClass('android.content.Intent')
const mIntent = new Intent('android.settings.ACTION_SETTINGS')
main.startActivity(mIntent)
}
});
} else {
delete params.updateBy;
delete params.updateTime;
editAddress(params).then((res) => {
if (res.data.success) {
uni.navigateBack();
} else {
uni.showToast({
title: res.data.message || "保存失败",
icon: "none",
});
}
});
}
}).catch(() => {});
},
})
// #endif
}
},
})
}
// 三级地址联动回调
getpickerParentValue(e) {
// 将需要绑定的地址设置为空,并赋值
this.form.consigneeAddressIdPath = [];
this.form.consigneeAddressPath = [];
let name = "";
async function requestAndroidPermission(permisionID: string) {
const result = await permision.requestAndroidPermission(permisionID)
if (result == 1) {
mapFlag.value = true
} else {
refuseMap()
}
}
e.forEach((item, index) => {
if (item.id) {
// 遍历数据
this.form.consigneeAddressIdPath.push(item.id);
this.form.consigneeAddressPath.push(item.localName);
name += item.localName;
this.form.___path = name;
}
if (index == e.length - 1) {
//如果是最后一个
let _town = item.children.filter((_child) => {
return _child.id == item.id;
});
function callBackAddress(val: any) {
uni.showLoading({ title: '加载中' })
if (val.regeocode && val) {
const address = val.regeocode
form.detail = address.formatted_address
form.___path = val.data.result.name
form.consigneeAddressIdPath = val.data.result.id
form.consigneeAddressPath = val.data.result.name
form.lat = val.latitude
form.lon = val.longitude
uni.hideLoading()
}
mapFlag.value = !mapFlag.value
}
this.form.lat = _town[0].center.split(",")[1];
this.form.lon = _town[0].center.split(",")[0];
}
});
},
function save() {
uForm.value?.validate().then(() => {
const params = { ...form }
delete params.___path
// 显示三级地址联动
showPicker() {
this.$refs.cityPicker.show();
},
},
mounted() {},
data() {
return {
lightColor: this.$lightColor, //高亮颜色
mapFlag: false, // 地图选择开
routerVal: "",
form: {
detail: "", //地址详情
name: "", //收货人姓名
mobile: "", //手机号码
consigneeAddressIdPath: [], //地址id
consigneeAddressPath: [], //地址名字
___path: "", //所在区域
isDefault: false, //是否默认地址
},
// 表单提交校验规则
rules: {
name: [
{
required: true,
message: "收货人姓名不能为空",
trigger: ["blur", "change"],
},
],
mobile: [
{
required: true,
message: "手机号码不能为空",
trigger: ["blur", "change"],
},
{
validator: (rule, value, callback) => {
return this.$u.test.mobile(value);
},
message: "手机号码不正确",
trigger: ["change", "blur"],
},
],
___path: [
{
required: true,
message: "请选择所在区域",
trigger: ["change"],
},
],
detail: [
{
required: true,
message: "请填写详细地址",
trigger: ["blur", "change"],
},
],
},
list: [
{
id: "",
localName: "请选择",
children: [],
},
],
};
},
onLoad(option) {
uni.showLoading({
title: "加载中",
});
this.routerVal = option;
// 如果当前是编辑地址,则需要查询出地址详情信息
if (option.id) {
getAddressDetail(option.id).then((res) => {
const params = res.data.result;
params.___path = params.consigneeAddressPath;
this["form"] = params;
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
if (Array.isArray(params.consigneeAddressIdPath)) {
params.consigneeAddressIdPath = params.consigneeAddressIdPath.join(',')
}
uni.hideLoading();
},
// 初始化rules必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules);
},
};
if (Array.isArray(params.consigneeAddressPath)) {
params.consigneeAddressPath = params.consigneeAddressPath.join(',')
}
if (!params.id) {
addAddressApi(params).then((res) => {
if (res.data.success) {
uni.navigateBack()
} else {
uni.showToast({ title: res.data.message || '保存失败', icon: 'none' })
}
})
} else {
delete params.updateBy
delete params.updateTime
editAddress(params).then((res) => {
if (res.data.success) {
uni.navigateBack()
} else {
uni.showToast({ title: res.data.message || '保存失败', icon: 'none' })
}
})
}
}).catch(() => {})
}
function getpickerParentValue(e: any[]) {
form.consigneeAddressIdPath = []
form.consigneeAddressPath = []
let name = ''
e.forEach((item, index) => {
if (item.id) {
form.consigneeAddressIdPath.push(item.id)
form.consigneeAddressPath.push(item.localName)
name += item.localName
form.___path = name
}
if (index == e.length - 1) {
const _town = item.children.filter((_child: any) => _child.id == item.id)
form.lat = _town[0].center.split(',')[1]
form.lon = _town[0].center.split(',')[0]
}
})
}
function showPicker() {
cityPicker.value?.show()
}
</script>
<style scoped lang="scss">
page {

View File

@@ -42,126 +42,95 @@
</view>
</template>
<script>
import * as API_Trade from "@/api/trade";
import * as API_Address from "@/api/address.js";
export default {
data() {
return {
addressList: [], //地址列表
showAction: false, //是否显示下栏框
removeList: [
{
text: "确定",
},
],
tips: {
text: "确定要删除该收货人信息吗?",
},
removeId: "", //删除的地址id
routerVal: "",
params: {
pageNumber: 1,
pageSize: 1000,
},
};
},
onPullDownRefresh() {
//下拉刷新
this.addressList = [];
this.getAddressList();
},
onLoad: function (val) {
this.routerVal = val;
},
onShow() {
this.addressList = [];
this.getAddressList();
},
onHide() {},
methods: {
async selectAddressData(val) {
await API_Trade.setAddressId(val.id, this.routerVal.way);
<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'
uni.navigateBack({
delta: 1,
});
},
//获取地址列表
getAddressList() {
uni.showLoading();
const store = useStore()
API_Address.getAddressList(
this.params.pageNumber,
this.params.pageSize
).then((res) => {
res.data.result.records.forEach((item) => {
item.consigneeAddressPath = item.consigneeAddressPath.split(",");
});
this.addressList = res.data.result.records;
console.log(this.addressList);
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 }
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
//删除地址
removeAddress(id) {
this.removeId = id;
this.showAction = true;
},
deleteAddressMessage() {
API_Address.deleteAddress(this.removeId).then((res) => {
if (res.statusCode == 200) {
uni.showToast({
icon: "none",
title: "删除成功",
});
this.getAddressList();
} else {
uni.showToast({
icon: "none",
title: res.data.message,
duration: 2000,
});
}
});
},
//新建。编辑地址
addAddress(id) {
if (id) {
uni.navigateTo({
url:
"/pages/mine/address/add?id=" +
id +
"&way=" +
this.routerVal.way +
"&type=order",
});
} else {
uni.navigateTo({
url:
"/pages/mine/address/add?way=" + this.routerVal.way + "&type=order",
});
}
},
//设为默认地址
setDefault(item) {
delete item.updateBy;
delete item.updateTime;
delete item.deleteFlag;
onPullDownRefresh(() => {
addressList.value = []
getAddressList()
})
item.isDefault ? "" : (item.isDefault = !item.isDefault);
onLoad((val) => {
routerVal.value = val || {}
})
API_Address.editAddress(item).then((res) => {
uni.showToast({
title: "设置默认地址成功",
icon: "none",
});
this.getAddressList();
});
},
},
};
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>

View File

@@ -1,6 +1,6 @@
<template>
<view class="address">
<u-empty class="empty" v-if="this.addressList.length === 0" text="暂无收货地址" mode="address"></u-empty>
<u-empty class="empty" v-if="addressList.length === 0" text="暂无收货地址" mode="address"></u-empty>
<view class="list" >
<view class="item c-content" v-for="(item, index) in addressList" :key="index">
<view class="basic">
@@ -43,116 +43,90 @@
</view>
</template>
<script>
import * as API_Address from "@/api/address.js";
export default {
data() {
return {
addressList: [], //地址列表
showAction: false, //是否显示下栏框
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onShow, onPullDownRefresh, onBackPress } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { tipsToLogin } from '@/utils/filters.js'
import * as API_Address from '@/api/address.js'
removeList: [
{
text: "确定",
},
],
tips: {
text: "确定要删除该收货人信息吗?",
},
removeId: "", //删除的地址id
routerVal: "",
params: {
pageNumber: 1,
pageSize: 1000,
},
};
},
// 返回上一级
onBackPress(e) {
uni.switchTab({
url: "/pages/tabbar/user/my",
});
return true;
},
onLoad: function (val) {
this.routerVal = val;
},
onPullDownRefresh() {
//下拉刷新
this.addressList = [];
this.getAddressList();
},
/**
* 进入页面检测当前账户是否登录
*/
onShow() {
if (this.tipsToLogin()) {
this.getAddressList();
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 }
onBackPress(() => {
uni.switchTab({ url: '/pages/tabbar/user/my' })
return true
})
onLoad((val) => {
routerVal.value = val || {}
})
onPullDownRefresh(() => {
addressList.value = []
getAddressList()
})
onShow(() => {
if (tipsToLogin()) {
getAddressList()
}
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
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 })
}
},
methods: {
//获取地址列表
getAddressList() {
uni.showLoading();
API_Address.getAddressList(
this.params.pageNumber,
this.params.pageSize
).then((res) => {
res.data.result.records.forEach((item) => {
item.consigneeAddressPath = item.consigneeAddressPath.split(",");
});
this.addressList = res.data.result.records;
})
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
//删除地址
removeAddress(id) {
this.removeId = id;
this.showAction = true;
},
// 删除地址
deleteAddressMessage() {
API_Address.deleteAddress(this.removeId).then((res) => {
if (res.statusCode == 200) {
uni.showToast({
icon: "none",
title: "删除成功",
});
this.getAddressList();
} else {
uni.showToast({
icon: "none",
title: res.data.message,
duration: 2000,
});
}
});
},
//新建。编辑地址
addAddress(id) {
uni.navigateTo({
url: `/pages/mine/address/add${id ? "?id=" + id : ""}`,
});
},
//设为默认地址
setDefault(item) {
delete item.updateBy;
delete item.updateTime;
delete item.deleteFlag;
function addAddress(id?: string) {
uni.navigateTo({
url: `/pages/mine/address/add${id ? '?id=' + id : ''}`,
})
}
item.isDefault ? "" : (item.isDefault = !item.isDefault);
API_Address.editAddress(item).then(() => {
uni.showToast({
title: "设置默认地址成功",
icon: "none",
});
this.getAddressList();
});
},
},
};
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>

View File

@@ -20,67 +20,57 @@
</view>
</template>
<script>
import * as API_Trade from "@/api/trade";
import * as API_Store from "@/api/store.js";
export default {
data() {
return {
storeAddressList: [], //地址列表
showAction: false, //是否显示下栏框
removeList: [
{
text: "确定",
},
],
tips: {
text: "确定要删除该收货人信息吗?",
},
removeId: "", //删除的地址id
routerVal: "",
params: {
pageNumber: 1,
pageSize: 1000,
},
};
},
onPullDownRefresh() {
//下拉刷新
this.storeAddressList = [];
this.getAddressList();
},
onLoad: function (val) {
this.routerVal = val;
},
onShow() {
this.storeAddressList = [];
this.getAddressList();
},
onHide() {},
methods: {
async selectAddressData(val) {
await API_Trade.setStoreAddressId(val.id, this.routerVal.way);
<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_Store from '@/api/store.js'
uni.navigateBack({
delta: 1,
});
},
//获取地址列表
getAddressList() {
uni.showLoading();
const store = useStore()
API_Store.getStoreAddress(
this.routerVal.storeId,
this.params
).then((res) => {
this.storeAddressList = res.data.result.records;
console.log(this.storeAddressList);
const storeAddressList = 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 }
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
onPullDownRefresh(() => {
storeAddressList.value = []
getAddressList()
})
onLoad((val) => {
routerVal.value = val || {}
})
onShow(() => {
storeAddressList.value = []
getAddressList()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
async function selectAddressData(val: any) {
await API_Trade.setStoreAddressId(val.id, routerVal.value.way)
uni.navigateBack({ delta: 1 })
}
function getAddressList() {
uni.showLoading()
API_Store.getStoreAddress(routerVal.value.storeId, params).then((res) => {
storeAddressList.value = res.data.result.records
hideLoadingIfNeeded()
})
}
function deleteAddressMessage() {
// 保留 action-sheet 回调占位,当前页面无删除入口
}
</script>
<style lang="scss" scoped>

View File

@@ -52,84 +52,64 @@
</view>
</template>
<script>
import { getUserRecharge, getWalletLog, getUserWallet } from "@/api/members";
export default {
data() {
return {
walletNum: 0,
current: 0,
swiperCurrent: 0,
userInfo: "",
params: {
pageNumber: 1,
pageSize: 10,
order: "desc",
},
depositData: [],
rechargeList: "",
walletLogList: "",
list: [{ name: "预存款变动明细" }],
};
},
watch: {
swiperCurrent(index) {
this.swiperCurrent = index;
},
},
async mounted() {
this.getWallet();
const result = await getUserWallet();
this.walletNum = result.data.result.memberWallet;
},
methods: {
isOutgoing(serviceType) {
return serviceType === "WALLET_PAY" || serviceType === "WALLET_WITHDRAWAL";
},
isIncoming(serviceType) {
return (
serviceType === "WALLET_REFUND" ||
serviceType === "WALLET_RECHARGE" ||
serviceType === "WALLET_COMMISSION"
);
},
getMoneyClass(serviceType) {
if (this.isOutgoing(serviceType)) return "out";
if (this.isIncoming(serviceType)) return "in";
return "";
},
formatLogMoney(logItem) {
const amount = this.unitPrice(logItem.money);
if (this.isOutgoing(logItem.serviceType)) return `-${amount}`;
if (this.isIncoming(logItem.serviceType)) return `+${amount}`;
return amount;
},
getRecharge() {
getUserRecharge(this.params).then((res) => {
if (res.data.success && res.data.result.records.length) {
this.depositData.push(...res.data.result.records);
}
});
},
getWallet() {
getWalletLog(this.params).then((res) => {
if (res.data.success && res.data.result.records.length) {
this.depositData.push(...res.data.result.records);
}
});
},
changed(index) {
this.depositData = [];
this.swiperCurrent = index;
this.params.pageNumber = 1;
this.getWallet();
},
loadMore() {
this.params.pageNumber++;
this.getWallet();
},
},
};
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getWalletLog, getUserWallet } from '@/api/members'
import { unitPrice } from '@/utils/filters.js'
const walletNum = ref(0)
const swiperCurrent = ref(0)
const params = ref({
pageNumber: 1,
pageSize: 10,
order: 'desc',
})
const depositData = ref<any[]>([])
const list = [{ name: '预存款变动明细' }]
onMounted(async () => {
getWallet()
const result = await getUserWallet()
walletNum.value = result.data.result.memberWallet
})
function isOutgoing(serviceType: string) {
return serviceType === 'WALLET_PAY' || serviceType === 'WALLET_WITHDRAWAL'
}
function isIncoming(serviceType: string) {
return (
serviceType === 'WALLET_REFUND' ||
serviceType === 'WALLET_RECHARGE' ||
serviceType === 'WALLET_COMMISSION'
)
}
function getMoneyClass(serviceType: string) {
if (isOutgoing(serviceType)) return 'out'
if (isIncoming(serviceType)) return 'in'
return ''
}
function formatLogMoney(logItem: any) {
const amount = unitPrice(logItem.money)
if (isOutgoing(logItem.serviceType)) return `-${amount}`
if (isIncoming(logItem.serviceType)) return `+${amount}`
return amount
}
function getWallet() {
getWalletLog(params.value).then((res) => {
if (res.data.success && res.data.result.records.length) {
depositData.value.push(...res.data.result.records)
}
})
}
function loadMore() {
params.value.pageNumber++
getWallet()
}
</script>
<style lang="scss" scoped>

View File

@@ -12,8 +12,7 @@
</view>
</template>
<script>
export default {};
<script setup lang="ts">
</script>
<style lang="scss" scoped>

View File

@@ -38,33 +38,30 @@
</view>
</template>
<script>
import { getUserWallet } from "@/api/members";
export default {
data() {
return {
walletNum: 0,
};
},
async onShow() {
if (this.isLogin("auth")) {
let result = await getUserWallet();
this.walletNum = result.data.result.memberWallet;
} else {
this.navigateToLogin("redirectTo");
}
},
methods: {
back() {
uni.switchTab({
url: "/pages/tabbar/user/my",
});
},
navigateTo(url) {
uni.navigateTo({ url });
},
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { getUserWallet } from '@/api/members'
import { isLogin, navigateToLogin, unitPrice } from '@/utils/filters.js'
const walletNum = ref(0)
onShow(async () => {
if (isLogin('auth')) {
const result = await getUserWallet()
walletNum.value = result.data.result.memberWallet
} else {
navigateToLogin('redirectTo')
}
})
function back() {
uni.switchTab({ url: '/pages/tabbar/user/my' })
}
function navigateTo(url: string) {
uni.navigateTo({ url })
}
</script>
<style lang="scss" scoped>

View File

@@ -19,40 +19,30 @@
</view>
</template>
<script>
import { recharge } from "@/api/members";
export default {
data() {
return {
price: "",
flag: true,
};
},
watch: {
price(val) {
this.flag = !(Number(val) > 0);
},
},
methods: {
async handlerRecharge() {
const amount = Number(this.price);
if (!(amount > 0)) {
uni.showToast({
title: "请输入充值金额",
icon: "none",
});
return;
}
<script setup lang="ts">
import { ref, watch } from 'vue'
import { recharge } from '@/api/members'
const res = await recharge({ price: amount });
if (res.data.success) {
uni.navigateTo({
url: `/pages/cart/payment/payOrder?orderType=RECHARGE&recharge_sn=${res.data.result.rechargeSn}`,
});
}
},
},
};
const price = ref('')
const flag = ref(true)
watch(price, (val) => {
flag.value = !(Number(val) > 0)
})
async function handlerRecharge() {
const amount = Number(price.value)
if (!(amount > 0)) {
uni.showToast({ title: '请输入充值金额', icon: 'none' })
return
}
const res = await recharge({ price: amount })
if (res.data.success) {
uni.navigateTo({
url: `/pages/cart/payment/payOrder?orderType=RECHARGE&recharge_sn=${res.data.result.rechargeSn}`,
})
}
}
</script>
<style lang="scss" scoped>

View File

@@ -25,182 +25,149 @@
</view>
</template>
<script>
import { getWithdrawApplyPage, getWithdrawApplyWechatTransferInfo } from "@/api/members";
export default {
data() {
return {
loaded: false,
params: {
pageNumber: 1,
pageSize: 10,
order: "desc",
},
records: [],
};
},
onShow() {
this.params.pageNumber = 1;
this.records = [];
this.loaded = false;
this.getData();
},
methods: {
withdrawStatusText(applyStatus) {
switch (applyStatus) {
case "APPLY":
return "申请中";
case "VIA_AUDITING":
return "审核通过";
case "D_VIA_AUDITING":
return "分销提现审核通过";
case "FAIL_AUDITING":
return "审核未通过";
case "D_FAIL_AUDITING":
return "分销提现审核未通过";
case "WAIT_USER_CONFIRM":
return "等待用户确认";
case "SUCCESS":
return "提现成功";
case "ERROR":
return "提现失败";
default:
return applyStatus || "";
<script setup lang="ts">
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { getWithdrawApplyPage, getWithdrawApplyWechatTransferInfo } from '@/api/members'
import { unitPrice } from '@/utils/filters.js'
const loaded = ref(false)
const params = ref({
pageNumber: 1,
pageSize: 10,
order: 'desc',
})
const records = ref<any[]>([])
onShow(() => {
params.value.pageNumber = 1
records.value = []
loaded.value = false
getData()
})
function withdrawStatusText(applyStatus: string) {
switch (applyStatus) {
case 'APPLY':
return '申请中'
case 'VIA_AUDITING':
return '审核通过'
case 'D_VIA_AUDITING':
return '分销提现审核通过'
case 'FAIL_AUDITING':
return '审核未通过'
case 'D_FAIL_AUDITING':
return '分销提现审核未通过'
case 'WAIT_USER_CONFIRM':
return '等待用户确认'
case 'SUCCESS':
return '提现成功'
case 'ERROR':
return '提现失败'
default:
return applyStatus || ''
}
}
function getData() {
getWithdrawApplyPage(params.value).then((res) => {
loaded.value = true
if (res.data.success && res.data.result.records.length != 0) {
records.value.push(...res.data.result.records)
}
})
}
function loadMore() {
params.value.pageNumber++
getData()
}
function confirmWechatReceive(item: any) {
const id = item && item.id
if (!id) {
uni.showToast({ title: '缺少提现记录ID', duration: 2000, icon: 'none' })
return
}
uni.showLoading({ title: '加载中' })
getWithdrawApplyWechatTransferInfo(id)
.then((res) => {
if (!res.data || !res.data.success) return
const info = res.data.result || {}
const mchId = info.mchId
const wechatPackage = info.wechatPackage
let appId = info.appId
if (typeof wx !== 'undefined' && wx.getAccountInfoSync) {
try {
const accountInfo = wx.getAccountInfoSync()
const mpAppId = accountInfo && accountInfo.miniProgram && accountInfo.miniProgram.appId
if (mpAppId) appId = mpAppId
} catch (e) {}
}
},
getData() {
getWithdrawApplyPage(this.params).then((res) => {
this.loaded = true;
if (res.data.success) {
if (res.data.result.records.length != 0) {
this.records.push(...res.data.result.records);
}
if (!mchId || !appId || !wechatPackage) {
uni.showToast({ title: '微信确认参数缺失', duration: 2000, icon: 'none' })
return
}
const openResultToast = (errMsg?: string) => {
if (errMsg === 'requestMerchantTransfer:ok') {
uni.showToast({ title: '已唤起确认页面', duration: 2000, icon: 'none' })
return
}
if (errMsg === 'requestMerchantTransfer:cancel') {
uni.showToast({ title: '已取消', duration: 2000, icon: 'none' })
return
}
});
},
loadMore() {
this.params.pageNumber++;
this.getData();
},
confirmWechatReceive(item) {
const id = item && item.id;
if (!id) {
uni.showToast({
title: "缺少提现记录ID",
duration: 2000,
icon: "none",
});
return;
title: errMsg ? `唤起失败:${errMsg}` : '唤起失败',
duration: 2500,
icon: 'none',
})
}
uni.showLoading({
title: "加载中",
});
getWithdrawApplyWechatTransferInfo(id)
.then((res) => {
if (!res.data || !res.data.success) return;
const info = res.data.result || {};
const mchId = info.mchId;
const wechatPackage = info.wechatPackage;
let appId = info.appId;
if (typeof wx !== "undefined" && wx.getAccountInfoSync) {
try {
const accountInfo = wx.getAccountInfoSync();
const mpAppId = accountInfo && accountInfo.miniProgram && accountInfo.miniProgram.appId;
if (mpAppId) appId = mpAppId;
} catch (e) {}
}
if (!mchId || !appId || !wechatPackage) {
if (typeof wx !== 'undefined' && wx.getSystemInfoSync) {
try {
const sys = wx.getSystemInfoSync()
if (sys && sys.platform === 'devtools') {
uni.showToast({
title: "微信确认参数缺失",
duration: 2000,
icon: "none",
});
return;
}
const openResultToast = (errMsg) => {
if (errMsg === "requestMerchantTransfer:ok") {
uni.showToast({
title: "已唤起确认页面",
duration: 2000,
icon: "none",
});
return;
}
if (errMsg === "requestMerchantTransfer:cancel") {
uni.showToast({
title: "已取消",
duration: 2000,
icon: "none",
});
return;
}
uni.showToast({
title: errMsg ? `唤起失败:${errMsg}` : "唤起失败",
title: '开发者工具可能不支持,请真机测试',
duration: 2500,
icon: "none",
});
};
if (typeof wx !== "undefined" && wx.getSystemInfoSync) {
try {
const sys = wx.getSystemInfoSync();
if (sys && sys.platform === "devtools") {
uni.showToast({
title: "开发者工具可能不支持,请真机测试",
duration: 2500,
icon: "none",
});
}
} catch (e) {}
icon: 'none',
})
}
} catch (e) {}
}
if (typeof wx !== "undefined" && wx.canIUse && wx.canIUse("requestMerchantTransfer")) {
wx.requestMerchantTransfer({
mchId,
appId,
package: wechatPackage,
success: (r) => {
openResultToast(r && (r.errMsg || r.err_msg));
},
fail: (r) => {
openResultToast(r && (r.errMsg || r.err_msg));
},
});
return;
}
if (typeof WeixinJSBridge !== "undefined" && WeixinJSBridge.invoke) {
WeixinJSBridge.invoke(
"requestMerchantTransfer",
{
mchId,
appId,
package: wechatPackage,
},
(r) => {
openResultToast(r && (r.errMsg || r.err_msg));
}
);
return;
}
uni.showToast({
title: "请在微信内打开确认收款",
duration: 2000,
icon: "none",
});
if (typeof wx !== 'undefined' && wx.canIUse && wx.canIUse('requestMerchantTransfer')) {
wx.requestMerchantTransfer({
mchId,
appId,
package: wechatPackage,
success: (r: any) => openResultToast(r && (r.errMsg || r.err_msg)),
fail: (r: any) => openResultToast(r && (r.errMsg || r.err_msg)),
})
.finally(() => {
uni.hideLoading();
});
},
},
};
return
}
if (typeof WeixinJSBridge !== 'undefined' && WeixinJSBridge.invoke) {
WeixinJSBridge.invoke(
'requestMerchantTransfer',
{ mchId, appId, package: wechatPackage },
(r: any) => openResultToast(r && (r.errMsg || r.err_msg))
)
return
}
uni.showToast({ title: '请在微信内打开确认收款', duration: 2000, icon: 'none' })
})
.finally(() => {
uni.hideLoading()
})
}
</script>
<style lang="scss" scoped>

View File

@@ -58,77 +58,68 @@
</view>
</template>
<script>
import { getUserWallet, withdrawalApply, withdrawalSettingVO } from "@/api/members";
export default {
data() {
return {
price: "",
walletNum: 0,
minPrice: 0,
type: "",
connectNumber: "",
realName: "",
};
},
computed: {
typeLabel() {
if (this.type === "ALI") return "支付宝";
if (this.type) return "微信";
return "--";
},
},
async mounted() {
const result = await getUserWallet();
const res = await withdrawalSettingVO();
this.walletNum = result.data.result.memberWallet;
this.minPrice = res.data.result.minPrice;
this.type = res.data.result.type;
},
methods: {
cashd() {
const amount = Number(this.price);
if (!this.$u.test.amount(parseInt(amount))) {
uni.showToast({
title: "请输入正确金额",
duration: 2000,
icon: "none",
});
return;
}
<script setup lang="ts">
import { ref, computed, getCurrentInstance, onMounted } from 'vue'
import { getUserWallet, withdrawalApply, withdrawalSettingVO } from '@/api/members'
import { unitPrice } from '@/utils/filters.js'
const params = { price: amount };
if (this.type === "ALI") {
if (!this.connectNumber || !this.realName) {
uni.showToast({
title: "请输入真实姓名和第三方登录账号",
duration: 2000,
icon: "none",
});
return;
}
params.connectNumber = this.connectNumber;
params.realName = this.realName;
}
const { proxy } = getCurrentInstance()!
withdrawalApply(params).then((res) => {
if (res.data.success) {
uni.showToast({
title: "提现成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack({ delta: 1 });
}, 1000);
}
});
},
handleAll() {
this.price = String(this.walletNum || "");
},
},
};
const price = ref('')
const walletNum = ref(0)
const minPrice = ref(0)
const type = ref('')
const connectNumber = ref('')
const realName = ref('')
const typeLabel = computed(() => {
if (type.value === 'ALI') return '支付宝'
if (type.value) return '微信'
return '--'
})
onMounted(async () => {
const result = await getUserWallet()
const res = await withdrawalSettingVO()
walletNum.value = result.data.result.memberWallet
minPrice.value = res.data.result.minPrice
type.value = res.data.result.type
})
function cashd() {
const amount = Number(price.value)
if (!proxy.$u.test.amount(parseInt(String(amount)))) {
uni.showToast({ title: '请输入正确金额', duration: 2000, icon: 'none' })
return
}
const params: Record<string, any> = { price: amount }
if (type.value === 'ALI') {
if (!connectNumber.value || !realName.value) {
uni.showToast({
title: '请输入真实姓名和第三方登录账号',
duration: 2000,
icon: 'none',
})
return
}
params.connectNumber = connectNumber.value
params.realName = realName.value
}
withdrawalApply(params).then((res) => {
if (res.data.success) {
uni.showToast({ title: '提现成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
}
})
}
function handleAll() {
price.value = String(walletNum.value || '')
}
</script>
<style lang="scss" scoped>

View File

@@ -1,11 +1,10 @@
<template>
<view></view>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>
<script setup lang="ts">
// 占位页,暂无业务逻辑
</script>
<style lang="scss" scoped>
</style>

View File

@@ -3,160 +3,100 @@
<view>
<h4>实名认证请上传真实的个人信息认证通过后将无法修改</h4>
<view>
<u-form :model="ruleForm" label-width="200rpx" ref="uForm">
<u-form-item label="姓名" prop="name">
<u-input v-model="ruleForm.name" placeholder="请输入您的真实姓名" />
</u-form-item>
<u-form-item label="身份证" prop="idNumber">
<u-input v-model="ruleForm.idNumber" placeholder="请输入身份证号码" />
</u-form-item>
<u-form-item label="银行开户行" prop="settlementBankBranchName">
<u-input v-model="ruleForm.settlementBankBranchName" type="text" placeholder="请输入银行开户行" />
</u-form-item>
<u-form-item label="银行开户名" prop="settlementBankAccountName">
<u-input v-model="ruleForm.settlementBankAccountName" type="text" placeholder="请输入银行开户名" />
</u-form-item>
<u-form-item label="银行账号" prop="settlementBankAccountNum">
<u-input v-model="ruleForm.settlementBankAccountNum" type="text" placeholder="请输入银行账号" />
</u-form-item>
<!-- <u-form-item label="身份证正面照" prop="name">
<u-upload></u-upload>
</u-form-item>
<u-form-item label="身份证反面照" prop="name">
<u-upload></u-upload>
</u-form-item>
<u-form-item label="手持身份证照" prop="name">
<u-upload></u-upload>
</u-form-item> -->
</u-form>
<u-button :customStyle="{'background':$lightColor,'color':'#fff' }" @click="submit">提交</u-button>
<up-form
:model="formData"
label-position="left"
label-width="180rpx"
ref="uFormRef"
>
<up-form-item label="姓名" prop="name">
<u-input v-model="formData.name" placeholder="请输入您的真实姓名" />
</up-form-item>
<up-form-item label="身份证" prop="idNumber">
<u-input v-model="formData.idNumber" placeholder="请输入身份证号码" />
</up-form-item>
<up-form-item label="银行开户行" prop="settlementBankBranchName">
<u-input v-model="formData.settlementBankBranchName" placeholder="请输入银行开户行" />
</up-form-item>
<up-form-item label="银行开户名" prop="settlementBankAccountName">
<u-input v-model="formData.settlementBankAccountName" placeholder="请输入银行开户名" />
</up-form-item>
<up-form-item label="银行账号" prop="settlementBankAccountNum">
<u-input v-model="formData.settlementBankAccountNum" placeholder="请输入银行账号" />
</up-form-item>
</up-form>
<u-button :customStyle="{ background: lightColor, color: '#fff' }" @click="submitForm">提交</u-button>
</view>
</view>
<view class="tips">
<view>您提交的信息正在审核</view>
<view>提交认证申请后工作人员将在三个工作日进行核对完成审核</view>
</view>
</view>
</template>
<script>
import { applyDistribution } from "@/api/goods";
export default {
data() {
return {
ruleForm: {
name: "",
idNumber: "",
settlementBankBranchName: "", // 银行开户行
settlementBankAccountName: "", //银行开户名
settlementBankAccountNum: "", //银行账号
},
rules: {
name: [
{
required: true,
message: "请输入姓名",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说返回true表示校验通过返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return this.$u.test.chinese(value);
},
message: "姓名输入不正确",
// 触发器可以同时用blur和change
trigger: ["change", "blur"],
},
],
// 银行开户行
settlementBankBranchName: [
{
required: true,
message: "请输入银行开户行",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
], //银行开户名
settlementBankAccountName: [
{
required: true,
message: "银行开户名",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
],
//银行账号
settlementBankAccountNum: [
{
required: true,
message: "请输入银行账号",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
],
idNumber: [
{
required: true,
message: "请输入身份证",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说返回true表示校验通过返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return this.$u.test.idCard(value);
},
message: "身份证号码不正确",
// 触发器可以同时用blur和change
trigger: ["change", "blur"],
},
],
},
};
},
methods: {
submit() {
this.$refs.uForm.validate((valid) => {
if (valid) {
applyDistribution(this.ruleForm).then((res) => {
if (res.data.success) {
uni.showToast({
title: "认证提交成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack();
}, 500);
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
} else {
uni.showToast({
title: "请填写有效信息",
duration: 2000,
icon: "none",
});
}
});
<script setup lang="ts">
import { reactive, computed, ref, getCurrentInstance } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { applyDistribution } from '@/api/goods'
const store = useStore()
const { proxy } = getCurrentInstance()!
const lightColor = computed(() => store.getters.lightColor)
const uFormRef = ref<any>(null)
const formData = reactive({
name: '',
idNumber: '',
settlementBankBranchName: '',
settlementBankAccountName: '',
settlementBankAccountNum: '',
})
const rules = {
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{
validator: (_rule: unknown, value: string) => proxy.$u.test.chinese(value),
message: '姓名输入不正确',
trigger: ['change', 'blur'],
},
},
onReady() {
this.$refs.uForm.setRules(this.rules);
},
};
],
settlementBankBranchName: [{ required: true, message: '请输入银行开户行', trigger: 'blur' }],
settlementBankAccountName: [{ required: true, message: '银行开户名', trigger: 'blur' }],
settlementBankAccountNum: [{ required: true, message: '请输入银行账号', trigger: 'blur' }],
idNumber: [
{ required: true, message: '请输入身份证', trigger: 'blur' },
{
validator: (_rule: unknown, value: string) => proxy.$u.test.idCard(value),
message: '身份证号码不正确',
trigger: ['change', 'blur'],
},
],
}
onReady(() => {
uFormRef.value?.setRules(rules)
})
function submitForm() {
uFormRef.value?.validate().then(() => {
applyDistribution(formData).then((res) => {
if (res.data.success) {
uni.showToast({ title: '认证提交成功!', duration: 2000, icon: 'none' })
setTimeout(() => uni.navigateBack(), 500)
} else {
uni.showToast({ title: res.data.message, duration: 2000, icon: 'none' })
}
})
}).catch(() => {
uni.showToast({ title: '请填写有效信息', duration: 2000, icon: 'none' })
})
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 32rpx;

View File

@@ -1,30 +1,38 @@
<template>
<view class="log-list">
<!-- 提现记录 -->
<view class="log-way" v-if="cashLogData.length != 0" v-for="(item, index) in cashLogData" :key="index">
<view
class="log-way"
v-if="withdrawLogList.length != 0"
v-for="(item, index) in withdrawLogList"
:key="'cash-' + index"
>
<view class="log-item">
<view class="log-item-view">
<view class="title">{{
item.distributionCashStatus == "APPLY"
? "待处理"
: item.distributionCashStatus == "VIA_AUDITING"
? "通过"
: "拒绝"
item.distributionCashStatus == 'APPLY'
? '待处理'
: item.distributionCashStatus == 'VIA_AUDITING'
? '通过'
: '拒绝'
}}</view>
<view class="price">+{{unitPrice(item.price) }}</view>
<view class="price">+{{ unitPrice(item.price) }}</view>
</view>
<view class="log-item-view">
<view>{{ item.createTime }}</view>
<view></view>
</view>
</view>
</view>
<!-- 分销业绩 -->
<view class="log-way" v-if="achievementData.length != 0" v-for="(item, index) in achievementData" :key="index">
<view
class="log-way"
v-if="achievementList.length != 0"
v-for="(item, index) in achievementList"
:key="'ach-' + index"
>
<view class="log-item">
<view class="log-item-view">
<view class="title">{{ item.goodsName }}</view>
<view class="price">提成金额+{{unitPrice(item.rebate) }}</view>
<view class="price">提成金额+{{ unitPrice(item.rebate) }}</view>
</view>
<view class="log-item-view">
<view>创建时间{{ item.createTime }}</view>
@@ -38,101 +46,98 @@
</view>
</view>
</view>
<view class="empty" v-if="empty">
<u-loadmore :status="status" :icon-type="iconType" bg-color="#f7f7f7" />
<view class="empty" v-if="isEmpty">
<u-loadmore :status="loadStatus" :icon-type="iconType" bg-color="#f7f7f7" />
</view>
</view>
</template>
<script>
import { cashLog, distributionOrderList } from "@/api/goods";
export default {
data () {
return {
cashLogData: [], //提现记录数据集合
achievementData: [], //分销业绩数据合集,
status: "loadmore",
iconType: "flower",
empty: false,
params: {
pageNumber: 1,
pageSize: 10,
},
type: 0,
routers: "",
achParams: {
pageNumber: 1,
pageSize: 10,
},
};
},
onLoad (option) {
let title;
option.type == 0 ? (title = "分销业绩") : (title = "提现记录");
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { cashLog, distributionOrderList } from '@/api/goods'
import { unitPrice } from '@/utils/filters.js'
uni.setNavigationBarTitle({
title: title, //这是修改后的导航栏文字
});
this.routers = option;
this.type = option.type;
option.type == 0 ? this.achievement() : this.history();
},
mounted () { },
onReachBottom () {
this.status = "loading";
this.type == 0 ? this.achParams.pageNumber++ : this.params.pageNumber++;
this.type == 0 ? this.achievement() : this.history();
},
methods: {
// 业绩
achievement () {
uni.showLoading({
title: "加载中",
});
distributionOrderList(this.achParams).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
this.achievementData.push(...res.data.result.records);
} else {
this.status = "nomore";
this.empty = true;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
// 初始化提现历史
history () {
uni.showLoading({
title: "加载中",
});
cashLog(this.params).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
this.cashLogData.push(...res.data.result.records);
} else {
this.status = "nomore";
this.empty = true;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
const store = useStore()
const withdrawLogList = ref<any[]>([])
const achievementList = ref<any[]>([])
const loadStatus = ref('loadmore')
const iconType = ref('flower')
const isEmpty = ref(false)
const listType = ref(0)
const routeQuery = ref<Record<string, string>>({})
const withdrawParams = ref({ pageNumber: 1, pageSize: 10 })
const achievementParams = ref({ pageNumber: 1, pageSize: 10 })
onLoad((option) => {
const type = Number(option.type ?? 0)
listType.value = type
routeQuery.value = option || {}
uni.setNavigationBarTitle({
title: type === 0 ? '分销业绩' : '提现记录',
})
type === 0 ? fetchAchievementList() : fetchWithdrawLog()
})
onReachBottom(() => {
loadStatus.value = 'loading'
if (listType.value === 0) {
achievementParams.value.pageNumber++
fetchAchievementList()
} else {
withdrawParams.value.pageNumber++
fetchWithdrawLog()
}
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function fetchAchievementList() {
uni.showLoading({ title: '加载中' })
distributionOrderList(achievementParams.value).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
achievementList.value.push(...res.data.result.records)
} else {
loadStatus.value = 'nomore'
isEmpty.value = true
}
hideLoadingIfNeeded()
})
}
function fetchWithdrawLog() {
uni.showLoading({ title: '加载中' })
cashLog(withdrawParams.value).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
withdrawLogList.value.push(...res.data.result.records)
} else {
loadStatus.value = 'nomore'
isEmpty.value = true
}
hideLoadingIfNeeded()
})
}
</script>
<style lang="scss" scoped>
.empty {
margin: 40rpx 0;
}
.price {
color: $main-color;
font-weight: bold;
}
.log-list {
padding: 0 8rpx;
overflow: hidden;
margin: 20rpx 0;
}
.log-way {
margin: 10rpx 0;
overflow: hidden;
@@ -140,26 +145,17 @@ export default {
border-radius: 10rpx;
padding: 20rpx 0;
}
.title {
font-size: 30rpx;
font-weight: bold;
}
.log-item-view {
padding: 8rpx 32rpx;
display: flex;
font-size: 13px;
justify-content: space-between;
}
.log-item-footer {
padding: 8rpx 32rpx;
display: flex;
font-size: 13px;
justify-content: space-between;
}
.log-item-footer,
.log-item-footers {
padding: 8rpx 32rpx;
display: flex;

View File

@@ -1,18 +1,15 @@
<template>
<view>
<view class="nav-list">
<view class="total">可提现金额</view>
<view class="price">{{unitPrice(distributionData.canRebate) }}</view>
<view class="frozen"
>冻结金额{{unitPrice(distributionData.commissionFrozen) }}</view
>
<view class="price">{{ unitPrice(distributionData.canRebate) }}</view>
<view class="frozen">冻结金额{{ unitPrice(distributionData.commissionFrozen) }}</view>
</view>
<view class="nav">
<view class="nav-item">
<u-icon
size="50"
@click="handleClick('/pages/mine/distribution/list?id='+distributionData.id+'&name='+distributionData.memberName)"
@click="navigateTo(`/pages/mine/distribution/list?id=${distributionData.id}&name=${distributionData.memberName}`)"
color="#ff6b35"
name="bag-fill"
></u-icon>
@@ -20,72 +17,54 @@
</view>
<view
class="nav-item"
@click="handleClick(`/pages/mine/distribution/history?type=0&id=${distributionData.id}&name=${distributionData.memberName}`)"
@click="navigateTo(`/pages/mine/distribution/history?type=0&id=${distributionData.id}&name=${distributionData.memberName}`)"
>
<u-icon size="50" color="#ff6b35" name="order"></u-icon>
<view>分销业绩</view>
</view>
<view
class="nav-item"
@click="handleClick('/pages/mine/distribution/history?type=1')"
>
<view class="nav-item" @click="navigateTo('/pages/mine/distribution/history?type=1')">
<u-icon size="50" color="#ff6b35" name="red-packet-fill"></u-icon>
<view>提现记录</view>
</view>
<view
class="nav-item"
@click="handleClick('/pages/mine/distribution/withdrawal')"
>
<view class="nav-item" @click="navigateTo('/pages/mine/distribution/withdrawal')">
<u-icon size="50" color="#ffc71c" name="rmb-circle-fill"></u-icon>
<view>提现</view>
</view>
</view>
</view>
</template>
<script>
<script setup lang="ts">
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { distribution } from '@/api/goods'
import { unitPrice } from '@/utils/filters.js'
import { distribution } from "@/api/goods";
export default {
const store = useStore()
const distributionData = ref<Record<string, any>>({})
data() {
return {
distributionData: "",
};
},
methods: {
handleClick(url) {
uni.navigateTo({
url,
});
},
queryGoods(src) {
uni.navigateTo({
url: `/pages/mine/distribution/${src}`,
});
},
/**
* 初始化推广商品
*/
init() {
uni.showLoading({
title: "加载中",
});
distribution().then((res) => {
if (res.data.result) {
this.distributionData = res.data.result;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
onShow() {
this.init();
},
};
onShow(() => {
fetchDistributionInfo()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function navigateTo(url: string) {
uni.navigateTo({ url })
}
function fetchDistributionInfo() {
uni.showLoading({ title: '加载中' })
distribution().then((res) => {
if (res.data.result) {
distributionData.value = res.data.result
}
hideLoadingIfNeeded()
})
}
</script>
<style lang="scss" scoped>
@@ -124,6 +103,5 @@ export default {
justify-content: center;
gap: 20rpx;
width: 33%;
// color: #fff;
}
</style>

View File

@@ -1,104 +1,127 @@
<template>
<view class="wrapper">
<view class="wrapper" :style="themeStyle">
<u-tabs
:list="list"
:list="stepList"
:scrollable="false"
v-model:current="current"
v-model:current="currentStep"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
></u-tabs>
<!-- 推广人资料 -->
<view class="message">
<u-form :model="ruleForm" label-width="250rpx" ref="uForm">
<u-form-item label="会员昵称" prop="name">
<u-input v-model="ruleForm.name" />
</u-form-item>
<u-form-item label="账户类型" prop="name"> </u-form-item>
<u-form-item
label="收款人姓名"
placeholder="请输入收款人姓名"
prop="name"
>
<u-input v-model="ruleForm.name" />
</u-form-item>
<u-form-item
label="收款账号"
placeholder="请输入收款人账号"
prop="name"
>
<u-input v-model="ruleForm.name" />
</u-form-item>
<u-form-item
label="银行名称"
placeholder="请输入开户银行支行名称"
prop="name"
>
<u-input v-model="ruleForm.name" />
</u-form-item>
</u-form>
<u-button :customStyle="{'background':$lightColor,'color':'#fff' }" @click="submit">提交</u-button>
<view class="feedBack-box">
<up-form
:model="formData"
label-position="top"
ref="uFormRef"
>
<up-form-item label="会员昵称" prop="name">
<u-input
border="none"
class="field-input"
v-model="formData.name"
:custom-style="fieldInputStyle"
/>
</up-form-item>
<up-form-item label="账户类型" prop="name"></up-form-item>
<up-form-item label="收款人姓名" prop="name">
<u-input
border="none"
class="field-input"
v-model="formData.name"
placeholder="请输入收款人姓名"
:custom-style="fieldInputStyle"
/>
</up-form-item>
<up-form-item label="收款账号" prop="name">
<u-input
border="none"
class="field-input"
v-model="formData.name"
placeholder="请输入收款人账号"
:custom-style="fieldInputStyle"
/>
</up-form-item>
<up-form-item label="银行名称" prop="name">
<u-input
border="none"
class="field-input"
v-model="formData.name"
placeholder="请输入开户银行支行名称"
:custom-style="fieldInputStyle"
/>
</up-form-item>
</up-form>
</view>
<view class="submit" @click="submitForm">提交</view>
</view>
</template>
<script>
export default {
components: {},
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules);
},
data() {
return {
current:0,
lightColor: this.$lightColor,
list: [
{
name: "推广人资料",
},
{
name: "平台审核",
},
{
name: "完成",
},
],
ruleForm: {
name: "",
radio: "",
},
rules: {
name: [
{
required: true,
message: "请输入姓名",
// 可以单个或者同时写两个触发验证方式
trigger: "blur",
},
],
},
};
},
};
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getThemeStyle } from '@/utils/theme'
import { fieldInputStyle } from '@/utils/form-style.js'
const store = useStore()
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const lightColor = computed(() => store.getters.lightColor)
const currentStep = ref(0)
const uFormRef = ref<any>(null)
const stepList = [
{ name: '推广人资料' },
{ name: '平台审核' },
{ name: '完成' },
]
const formData = reactive({
name: '',
radio: '',
})
const rules = {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
}
onReady(() => {
uFormRef.value?.setRules(rules)
})
function submitForm() {
uFormRef.value?.validate().catch(() => {
uni.showToast({ title: '请填写有效信息', icon: 'none' })
})
}
</script>
<style lang="scss" scoped>
.menu {
height: 88rpx;
line-height: 88rpx;
background: $main-color;
display: flex;
> .menu-item {
flex: 1;
text-align: center;
color: $light-color;
}
}
.active {
color: #fff !important;
}
.message {
padding: 0 32rpx;
<style lang="scss">
page {
background: #f8f8f8;
}
</style>
<style lang="scss" scoped>
@import '@/pages/passport/entry/seller/entry-form.scss';
.wrapper {
box-sizing: border-box;
min-height: 100vh;
padding: 20rpx 24rpx 40rpx;
background: #f8f8f8;
@include seller-entry-form;
}
.feedBack-box {
background: #fff;
border-radius: 20rpx;
padding: 32rpx;
margin-top: 20rpx;
}
.submit {
@include seller-entry-submit;
}
</style>

View File

@@ -58,44 +58,44 @@
<!-- 导航栏 -->
<view class="nav">
<view class="nav-item" @click="handleMyGoods(true)" :class="{ checked: params.checked }">已选择</view>
<view class="nav-item" @click="handleMyGoods(false)" :class="{ checked: !params.checked }">未选择</view>
<view class="nav-item" @click="toggleGoodsTab(true)" :class="{ checked: queryParams.checked }">已选择</view>
<view class="nav-item" @click="toggleGoodsTab(false)" :class="{ checked: !queryParams.checked }">未选择</view>
<!-- <view class="nav-item" @click="popup = !popup">筛选</view> -->
</view>
<!-- 商品列表 -->
<view class="goods-list">
<scroll-view class="body-view" scroll-y @scrolltolower="renderDate">
<scroll-view class="body-view" scroll-y @scrolltolower="loadMore">
<block v-for="(item, index) in goodsList" :key="item.id">
<u-swipe-action v-if="params.checked" class="distribution-swipe">
<u-swipe-action v-if="queryParams.checked" class="distribution-swipe">
<u-swipe-action-item
:show="item.___selected"
@open="openAction(item)"
@open="openSwipeAction(item)"
:name="index"
:options="options"
@click="changeActionTab(item)"
:options="swipeOptions"
@click="confirmUnbindPrompt(item)"
>
<view class="goods-item">
<view class="goods-item-img" @click="handleNavgationGoods(item)">
<view class="goods-item-img" @click="navigateToGoods(item)">
<u-image width="176rpx" height="176rpx" :src="item.thumbnail"></u-image>
</view>
<view class="goods-item-desc">
<view class="-item-title" @click="handleNavgationGoods(item)">
<view class="-item-title" @click="navigateToGoods(item)">
{{ item.goodsName }}
</view>
<view class="-item-price" @click="handleNavgationGoods(item)">
<view class="-item-price" @click="navigateToGoods(item)">
佣金:
<span> {{ unitPrice(item.commission) }}</span>
</view>
<view class="-item-bottom">
<view class="-item-bootom-money" @click="handleNavgationGoods(item)">
<view class="-item-bootom-money" @click="navigateToGoods(item)">
<view class="-item-yj">
<span>{{ unitPrice(item.price) }}</span>
</view>
</view>
<view>
<view class="click" @click="handleLink(item)">分销商品</view>
<view class="click" @click="shareDistributionGoods(item)">分销商品</view>
</view>
</view>
</view>
@@ -104,25 +104,25 @@
</u-swipe-action>
<view v-else class="goods-item">
<view class="goods-item-img" @click="handleNavgationGoods(item)">
<view class="goods-item-img" @click="navigateToGoods(item)">
<u-image width="176rpx" height="176rpx" :src="item.thumbnail"></u-image>
</view>
<view class="goods-item-desc">
<view class="-item-title" @click="handleNavgationGoods(item)">
<view class="-item-title" @click="navigateToGoods(item)">
{{ item.goodsName }}
</view>
<view class="-item-price" @click="handleNavgationGoods(item)">
<view class="-item-price" @click="navigateToGoods(item)">
佣金:
<span> {{ unitPrice(item.commission) }}</span>
</view>
<view class="-item-bottom">
<view class="-item-bootom-money" @click="handleNavgationGoods(item)">
<view class="-item-bootom-money" @click="navigateToGoods(item)">
<view class="-item-yj">
<span>{{ unitPrice(item.price) }}</span>
</view>
</view>
<view>
<view class="click" @click="handleClickGoods(item)">立即选取</view>
<view class="click" @click="selectGoods(item)">立即选取</view>
</view>
</view>
</view>
@@ -136,210 +136,164 @@
</view>
</view>
<canvas class="canvas-hide" canvas-id="qrcode" />
<drawCanvas ref="drawCanvas" v-if="showFlag" :res="res" />
<u-modal v-model:show="deleteShow" :confirm-style="{'color':lightColor}" @confirm="delectConfirm" show-cancel-button :content="deleteContent" :async-close="true"></u-modal>
<drawCanvas ref="drawCanvasRef" v-if="showPoster" :res="posterData" />
<u-modal
v-model:show="showUnbindModal"
:confirm-style="{ color: lightColor }"
@confirm="confirmUnbind"
show-cancel-button
:content="unbindModalContent"
:async-close="true"
></u-modal>
</view>
</template>
<script>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import {
distributionGoods,
checkedDistributionGoods,
getMpCode,
} from "@/api/goods";
} from '@/api/goods'
import drawCanvas from '@/components/m-canvas'
import { unitPrice } from '@/utils/filters.js'
import drawCanvas from "@/components/m-canvas";
export default {
data() {
return {
lightColor: this.$lightColor,
deleteContent: "解绑该商品?", //删除显示的信息
// 商品栏右侧滑动按钮
options: [
{
text: "解绑",
style: {
backgroundColor: this.$lightColor, //高亮颜色
},
},
],
showFlag: false, //分销分享开关
empty: false,
popup: false, //弹出层开关
active_color: this.$mainColor,
current: 0,
params: {
pageNumber: 1,
pageSize: 10,
checked: true,
},
goodsList: [],
// 分销分享 实例
res: {
container: {
width: 600,
height: 960,
background: "#fff",
title: "分享背景",
},
// 分销分享
bottom: {
img: "",
code: "",
price: 0,
},
},
routers: "",
deleteShow: false, //删除模态框
goodsVal: false, //分销商铺信息
};
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const swipeOptions = computed(() => [
{
text: '解绑',
style: { backgroundColor: lightColor.value },
},
components: {
drawCanvas,
])
const unbindModalContent = '解绑该商品?'
const showPoster = ref(false)
const showUnbindModal = ref(false)
const routeQuery = ref<Record<string, string>>({})
const selectedGoods = ref<any>(null)
const drawCanvasRef = ref<any>(null)
const queryParams = ref({
pageNumber: 1,
pageSize: 10,
checked: true,
})
const goodsList = ref<any[]>([])
const posterData = ref({
container: {
width: 600,
height: 960,
background: '#fff',
title: '分享背景',
},
onLoad(options) {
this.routers = options;
bottom: {
img: '',
code: '',
price: 0,
desc: '',
},
watch: {},
onShow() {
this.goodsList = [];
this.init();
},
methods: {
/**
* 滑动删除
*/
changeActionTab(val) {
this.deleteShow = true;
this.goodsVal = val;
},
})
/**
* 点击解绑商品
*/
delectConfirm() {
checkedDistributionGoods({ id: this.goodsVal.id, checked: false }).then(
(res) => {
if (res.data.success) {
uni.showToast({
title: "此商品解绑成功",
duration: 2000,
});
this.deleteShow = false;
this.goodsList = [];
this.init();
}
}
);
},
onLoad((options) => {
routeQuery.value = options || {}
})
/**
* 左滑打开删除
*/
openAction(val) {
this.goodsList.forEach((item) => {
item["___selected"] = false;
});
val["___selected"] = true;
},
onShow(() => {
goodsList.value = []
queryParams.value.pageNumber = 1
fetchGoodsList()
})
/**
* 查看图片
*/
handleNavgationGoods(val) {
uni.navigateTo({
url: `/pages/product/goods?id=${val.skuId}&goodsId=${val.goodsId}`,
});
},
function confirmUnbindPrompt(item: any) {
showUnbindModal.value = true
selectedGoods.value = item
}
async handleLink(goods) {
uni.showToast({
title: "请请按住保存图片",
duration: 2000,
icon: "none",
});
let page = `pages/product/goods`;
let scene = `${goods.skuId},${goods.goodsId},${this.routers.id}`;
let result = await getMpCode({ page, scene });
if (result.data.success) {
let callback = result.data.result;
this.res.container.title = `${goods.goodsName}`;
this.res.bottom.code = `data:image/png;base64,${callback}`;
this.res.bottom.price = this.unitPrice(
goods.price,
"¥"
);
this.res.bottom.desc = `${goods.goodsName}`;
this.res.bottom.img = `${goods.thumbnail}`;
function confirmUnbind() {
checkedDistributionGoods({ id: selectedGoods.value.id, checked: false }).then((res) => {
if (res.data.success) {
uni.showToast({ title: '此商品解绑成功', duration: 2000 })
showUnbindModal.value = false
goodsList.value = []
queryParams.value.pageNumber = 1
fetchGoodsList()
}
})
}
if (this.showFlag) {
this.$refs.drawCanvas.init();
}
this.showFlag = true;
} else {
uni.showToast({
title: `制作二维码失败!请稍后重试`,
duration: 2000,
icon: "none",
});
}
},
function openSwipeAction(item: any) {
goodsList.value.forEach((row) => {
row.___selected = false
})
item.___selected = true
}
change(index) {
this.current = index;
},
// 点击我的选品库
handleMyGoods(flag) {
this.goodsList = [];
this.params.checked = flag;
this.init();
},
function navigateToGoods(item: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.skuId}&goodsId=${item.goodsId}`,
})
}
// 选择商品
handleClickGoods(val) {
checkedDistributionGoods({ id: val.id, checked: true }).then((res) => {
if (res.data.success) {
uni.showToast({
title: "已添加到我的选品库",
duration: 2000,
icon: "none",
});
async function shareDistributionGoods(goods: any) {
uni.showToast({ title: '请请按住保存图片', duration: 2000, icon: 'none' })
const page = 'pages/product/goods'
const scene = `${goods.skuId},${goods.goodsId},${routeQuery.value.id}`
const result = await getMpCode({ page, scene })
if (result.data.success) {
const callback = result.data.result
posterData.value.container.title = `${goods.goodsName}`
posterData.value.bottom.code = `data:image/png;base64,${callback}`
posterData.value.bottom.price = unitPrice(goods.price, '¥')
posterData.value.bottom.desc = `${goods.goodsName}`
posterData.value.bottom.img = `${goods.thumbnail}`
if (showPoster.value) {
drawCanvasRef.value?.init()
}
showPoster.value = true
} else {
uni.showToast({ title: '制作二维码失败!请稍后重试', duration: 2000, icon: 'none' })
}
}
setTimeout(() => {
this.goodsList = [];
this.init();
}, 500);
}
});
},
function toggleGoodsTab(isSelected: boolean) {
goodsList.value = []
queryParams.value.checked = isSelected
queryParams.value.pageNumber = 1
fetchGoodsList()
}
init() {
distributionGoods(this.params).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
res.data.result.records.forEach((item) => {
item["___selected"] = false;
});
this.goodsList.push(...res.data.result.records);
}
if (this.goodsList.length === 0) {
this.empty = true;
}
});
},
function selectGoods(item: any) {
checkedDistributionGoods({ id: item.id, checked: true }).then((res) => {
if (res.data.success) {
uni.showToast({ title: '已添加到我的选品库', duration: 2000, icon: 'none' })
setTimeout(() => {
goodsList.value = []
queryParams.value.pageNumber = 1
fetchGoodsList()
}, 500)
}
})
}
/**
* 底部加载数据
*/
renderDate() {
function fetchGoodsList() {
distributionGoods(queryParams.value).then((res) => {
if (res.data.success && res.data.result.records.length >= 1) {
res.data.result.records.forEach((item: any) => {
item.___selected = false
})
goodsList.value.push(...res.data.result.records)
}
})
}
this.params.pageNumber += 1;
this.init();
},
},
};
function loadMore() {
queryParams.value.pageNumber += 1
fetchGoodsList()
}
</script>
<style lang="scss" scoped>

View File

@@ -1,129 +1,182 @@
<template>
<view>
<view class="withdrawal-list">
<view class="title">提现金额</view>
<view class="content">
<view class="price">
<span> </span>
<u-input v-model="price" placeholder="" type="number" />
</view>
<view class="all">
<view @click="handleAll" :style="{ color: $mainColor }">全部</view>
<view style="font-size: 24rpx; color: #999"
>可提现金额<span>{{unitPrice(distributionData.canRebate) }}</span
></view
>
<view class="wrapper" :style="themeStyle">
<view class="feedBack-box">
<view class="box-title">提现金额</view>
<view class="amount-row">
<text class="currency"></text>
<u-input
v-model="price"
type="digit"
border="none"
placeholder="请输入提现金额"
class="amount-input"
/>
</view>
<view class="amount-extra">
<view class="all-btn" @click="fillAllAmount">全部</view>
<view class="balance-tip">
可提现金额
<text class="balance-value">{{ unitPrice(distributionData.canRebate) }}</text>
</view>
</view>
</view>
<view class="submit" @click="cashd">提现</view>
<view class="submit" @click="submitWithdraw">提现</view>
</view>
</template>
<script>
import { distribution, cash } from "@/api/goods";
export default {
data() {
return {
price: 0,
distributionData: "",
};
},
mounted() {
this.init();
},
methods: {
cashd() {
this.price = this.price + "";
if (this.$u.test.amount(parseInt(this.price))) {
cash({ price: this.price }).then((res) => {
if(res.data.success){
uni.showToast({
title: '提现成功!',
duration: 2000,
icon:"none"
});
setTimeout(()=>{
uni.navigateBack({
delta: 1
});
},1000)
}
});
} else {
uni.showToast({
title: "请输入正确金额",
duration: 2000,
icon: "none",
});
<script setup lang="ts">
import { ref, computed, getCurrentInstance, onMounted } from 'vue'
import { useStore } from '@/store'
import { distribution, cash } from '@/api/goods'
import { unitPrice } from '@/utils/filters.js'
import { getThemeStyle } from '@/utils/theme'
const store = useStore()
const { proxy } = getCurrentInstance()!
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const price = ref('')
const distributionData = ref<Record<string, any>>({})
onMounted(() => {
fetchDistributionInfo()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function fetchDistributionInfo() {
uni.showLoading({ title: '加载中' })
distribution().then((res) => {
if (res.data.result) {
distributionData.value = res.data.result
}
hideLoadingIfNeeded()
})
}
function submitWithdraw() {
const amount = String(price.value ?? '')
if (proxy.$u.test.amount(parseInt(amount))) {
cash({ price: amount }).then((res) => {
if (res.data.success) {
uni.showToast({ title: '提现成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
}
},
handleAll() {
this.price = this.distributionData.canRebate;
},
/**
* 初始化推广商品
*/
init() {
uni.showLoading({
title: "加载中",
});
distribution().then((res) => {
if (res.data.result) {
this.distributionData = res.data.result;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
})
} else {
uni.showToast({ title: '请输入正确金额', duration: 2000, icon: 'none' })
}
}
function fillAllAmount() {
price.value = distributionData.value.canRebate ?? ''
}
</script>
<style lang="scss" scoped>
::v-deep .u-input__input,
.u-input {
font-size: 80rpx !important;
height: 102rpx !important;
}
::v-deep .u-input__input{
height: 100%;
font-size: 80rpx;
}
.content {
display: flex;
> .price {
width: 60%;
margin: 20rpx 0;
font-size: 80rpx;
display: flex;
}
> .all {
justify-content: center;
width: 40%;
display: flex;
flex-direction: column;
align-items: flex-end;
}
}
.withdrawal-list {
margin: 20rpx 0;
background: #fff;
padding: 16rpx 32rpx;
}
.title {
font-size: 35rpx;
}
.submit {
margin: 80rpx auto;
width: 94%;
background: $light-color;
height: 90rpx;
color: #fff;
border-radius: 10rpx;
text-align: center;
line-height: 90rpx;
<style lang="scss">
page {
background: #f8f8f8;
}
</style>
<style lang="scss" scoped>
@import '@/pages/passport/entry/seller/entry-form.scss';
.wrapper {
box-sizing: border-box;
min-height: 100vh;
padding: 20rpx 24rpx 40rpx;
background: #f8f8f8;
}
.feedBack-box {
background: #fff;
border-radius: 20rpx;
padding: 32rpx;
}
.box-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
line-height: 1.4;
}
.amount-row {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 24rpx;
padding: 16rpx 24rpx;
background: #fafafa;
border-radius: 12rpx;
}
.currency {
flex-shrink: 0;
margin-right: 16rpx;
font-size: 56rpx;
font-weight: 600;
line-height: 1;
color: #333;
}
.amount-input {
flex: 1;
min-width: 0;
}
:deep(.amount-input .u-input) {
padding: 0 !important;
}
:deep(.amount-input .u-input__content) {
background: transparent !important;
}
:deep(.amount-input .u-input__content__field-wrapper__field) {
height: 72rpx !important;
min-height: 72rpx !important;
font-size: 56rpx !important;
font-weight: 600 !important;
color: #333 !important;
}
:deep(.amount-input .u-input__content__field-wrapper__field--placeholder) {
font-size: 32rpx !important;
font-weight: 400 !important;
color: #c0c4cc !important;
}
.amount-extra {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 24rpx;
}
.all-btn {
font-size: 28rpx;
color: var(--theme-light, #ff6b35);
}
.balance-tip {
font-size: 24rpx;
color: #999;
}
.balance-value {
margin: 0 4rpx;
color: #333;
}
.submit {
@include seller-entry-submit;
}
</style>

View File

@@ -1,62 +1,42 @@
<template>
<div class="wrapper">
<u-parse :lazy-load="true" :selectable="true" :content="res.content" v-if="res"></u-parse>
<u-parse :lazy-load="true" :selectable="true" :content="article.content" v-if="article"></u-parse>
</div>
</template>
<script>
import { getArticleDetailByType } from "@/api/article";
export default {
data() {
return {
res: "",
way: {
USER_AGREEMENT: {
title: "服务协议",
type: "USER_AGREEMENT",
},
PRIVACY_POLICY: {
title: "隐私政策",
type: "PRIVACY_POLICY",
},
LICENSE_INFORMATION: {
title: "证照信息",
type: "LICENSE_INFORMATION",
},
ABOUT: {
title: "关于我们",
type: "ABOUT",
},
STORE_REGISTER: {
title: "店铺入驻协议",
type: "STORE_REGISTER",
},
},
};
},
mounted() {},
onLoad(option) {
console.log(this.way)
uni.setNavigationBarTitle({
title: this.way[option.type].title,
});
this.init(option);
},
methods: {
init(option) {
getArticleDetailByType(this.way[option.type].type).then((res) => {
if (res.data.success) {
this.res = res.data.result;
console.log(res)
}
});
},
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getArticleDetailByType } from '@/api/article'
const ARTICLE_TYPE_MAP: Record<string, { title: string; type: string }> = {
USER_AGREEMENT: { title: '服务协议', type: 'USER_AGREEMENT' },
PRIVACY_POLICY: { title: '隐私政策', type: 'PRIVACY_POLICY' },
LICENSE_INFORMATION: { title: '证照信息', type: 'LICENSE_INFORMATION' },
ABOUT: { title: '关于我们', type: 'ABOUT' },
STORE_REGISTER: { title: '店铺入驻协议', type: 'STORE_REGISTER' },
}
const article = ref<any>(null)
onLoad((option) => {
const meta = ARTICLE_TYPE_MAP[option.type]
if (!meta) return
uni.setNavigationBarTitle({ title: meta.title })
fetchArticle(meta.type)
})
function fetchArticle(type: string) {
getArticleDetailByType(type).then((res) => {
if (res.data.success) {
article.value = res.data.result
}
})
}
</script>
<style lang="scss" scoped>
.wrapper {
padding: 16rpx;
}
</style>
</style>

View File

@@ -196,515 +196,356 @@
</view>
</template>
<script>
// rpx和px的比率
var l
// 可用窗口高度
var wh
// 顶部空盒子的高度
var mgUpHeight
<script setup lang="ts">
import { ref, nextTick, getCurrentInstance } from 'vue'
import {
getTalkMessage,
onLoad,
onHide,
onUnload,
onPullDownRefresh,
onPageScroll,
} from '@dcloudio/uni-app'
import {
getTalkMessage as fetchTalkMessageApi,
getTalkByUser,
jumpObtain
} from "@/api/im.js";
import SocketService from "@/utils/socket_service.js";
import storage from "@/utils/storage.js";
import {
beautifyTime
} from "@/utils/filters.js"
jumpObtain,
} from '@/api/im.js'
import SocketService from '@/utils/socket_service.js'
import storage from '@/utils/storage.js'
import { beautifyTime, unitPrice } from '@/utils/filters.js'
import config from '@/config/config.js'
import { textReplaceEmoji, emojistwo } from '@/utils/emojis.js';
export default {
// 页面卸载后清除imGoodId
onUnload () {
// #ifdef H5
uni.setStorageSync("imGoodId", '');
// #endif
import { textReplaceEmoji, emojistwo } from '@/utils/emojis.js'
if (this.socketOpen == true) {
uni.closeSocket();
}
},
onLoad (options) {
// 没有goodsid则不显示 发送商品弹窗
this.showHideModel = options.goodsid
// 发送后刷新页面不显示 发送商品弹窗 local里面imGoodId不为空显示
// #ifdef H5
this.localImGoodsId = uni.getStorageSync("imGoodId");
// #endif
this.resolve = options
// 请求商品信息
if (this.resolve.goodsid) {
this.commodityDetails()
}
var query = uni.getSystemInfoSync()
// rpx和px的比率
let l: number
// 可用窗口高度
let wh: number
// 顶部空盒子的高度
let mgUpHeight: number
l = query.screenWidth / 750
wh = query.windowHeight
this.scrollHeight = (query.windowHeight - 44) + "px"
this.user = storage.getUserInfo()
this.toUser = storage.getTalkToUser()
const { proxy } = getCurrentInstance()!
if (options.talkId) {
this.params.talkId = options.talkId;
this.getTalkMessage()
} else {
this.getTalk(options.userId)
const socketOpen = ref(false)
const showHideModel = ref<string | undefined>(undefined)
const localImGoodsId = ref('')
const showHide = ref(true)
const anData = ref<Record<string, any>>({})
const animationData = ref<Record<string, any>>({})
const msgList = ref<any[]>([])
const oldHeight = ref(0)
const params = ref({
talkId: '',
pageSize: 10,
pageNumber: 1,
})
const msg = ref('')
const go = ref(0)
const user = ref<Record<string, any>>({})
const toUser = ref<Record<string, any>>({})
const scrollHeight = ref(0)
const ws = new SocketService()
const resolve = ref<Record<string, any>>({})
const goodListData = ref<Record<string, any>>({})
const reconnectCount = ref(0)
const inputHeight = ref(0)
const isShow = ref(false)
}
// this.ws.connect();
this.socket();
},
// 页面隐藏
onHide () {
uni.closeSocket();
},
onUnload () {
uni.closeSocket();
},
onPullDownRefresh () {
this.params.pageNumber = this.params.pageNumber + 1
this.getTalkMessage()
setTimeout(function () {
uni.stopPullDownRefresh();
}, 1000);
},
data () {
return {
textReplaceEmoji,
emojistwo,
socketOpen: false, //是否连接
storage,
fixed: 'fixed',
bottom: '50px',
width: '100%',
showHideModel: undefined,
localImGoodsId: '',
showHide: true,
msgLoad: false,
anData: {},
animationData: {},
msgList: [],
oldHeight: 0,
params: { //搜索条件
talkId: '',
pageSize: 10,
pageNumber: 1,
},
goToIndex: 0, // 前往位置
msg: "",
go: 0,
newMessageNum: 0,
user: {},
toUser: {},
scrollHeight: 0,
ws: new SocketService(),
resolve: {},
goodListData: {},
count: 0, //判断socket断开连接请求次数
inputHeight:0,
isShow:false,
}
},
onPageScroll (e) {
// #ifdef APP-PLUS
uni.hideKeyboard()
this.isShow = false
// #endif
},
methods: {
navigateToBottom(){
// #ifdef H5
this.isShow = true
this.$refs.inputRef.focus()
// #endif
// #ifdef APP-PLUS
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: 5000000,
duration: 50,
success: () => {
setTimeout(() => {
this.isShow = true
}, 200);
;
},
fail: () => { },
complete: () => {}
});
});
// #endif
},
eventHandle(){
this.inputHeight = 0
this.isShow = false
},
inputBindFocus(e){
if (e.detail.height) {
// #ifdef APP-PLUS
// 判断是否是ios
if (uni.getSystemInfoSync().platform == 'ios') {
this.inputHeight = e.detail.height - 40 //这个高度就是软键盘的高度
}else{
this.inputHeight = e.detail.height
}
// #endif
// #ifndef APP-PLUS
this.inputHeight = e.detail.height //这个高度就是软键盘的高度
// #endif
}
},
sendMessage () {
if (this.msg == "") {
return 0;
}
if (this.socketOpen == false) {
return
}
let msg = {
operation_type: "MESSAGE",
to: this.toUser.userId,
from: this.user.id,
message_type: "MESSAGE",
context: this.msg,
talk_id: this.params.talkId,
}
let data = JSON.stringify(msg);
uni.sendSocketMessage({
data: data,
});
this.msgList.push({
"text": this.msg,
"my": true,
"messageType": 'MESSAGE'
})
let type = 'down';
this.msgGo(type)
this.msg = ""
},
sendGoodsMessage () {
let msg = {
operation_type: "MESSAGE",
to: this.toUser.userId,
from: this.user.id,
message_type: "GOODS",
context: this.goodListData,
talk_id: this.params.talkId,
}
let data = JSON.stringify(msg);
uni.sendSocketMessage({
data: data
});
this.msgList.push({
"text": JSON.stringify(this.goodListData),
"my": true,
"messageType": 'GOODS'
})
this.showHide = false
// #ifdef H5
uni.setStorageSync("imGoodId", 1111111);
// #endif
this.$nextTick(() => {
uni.pageScrollTo({
scrollTop: 2000000,
duration: 300
});
})
},
socket () {
var _this = this;
uni.closeSocket();
this.socketOpen = false;
try {
//WebSocket的地址
var url = config.baseWsUrl + '/' + storage.getAccessToken();
// 连接
uni.connectSocket({
url: url,
});
// 监听WebSocket连接已打开
uni.onSocketOpen(function (res) {
_this.socketOpen = true;
});
if (!this.socketOpen) {
// 监听连接失败
uni.onSocketError(function (err) {
if (this.count < 3) {
if (err && err.code != 1000) {
_this.socketOpen = true;
setTimeout(() => {
uni.connectSocket({
url: url,
});
}, 2000)
}
} else {
uni.closeSocket();
}
this.count++
});
}
// 监听收到信息
uni.onSocketMessage(function (res) {
res.data = JSON.parse(res.data)
console.log(res.data.result);
if (res.data.messageResultType == 'MESSAGE') {
_this.msgList.push(res.data.result)
console.log(_this.msgList)
}
console.log(res.data)
_this.msgGo()
})
} catch (e) {
uni.closeSocket();
}
// 监听是否断线,断线进行重新连接
uni.onSocketClose((res) => {
if (res.code != null && res.code != 1000) {
this.socket()
}
})
},
beautifyTime,
//订单详情
linkTosOrders (val) {
let order = JSON.parse(val)
uni.navigateTo({
url: '/pages/order/orderDetail?sn=' + order.sn,
});
},
// 跳转商品详情页
jumpGoodDesc (item) {
let info = JSON.parse(item.text)
uni.navigateTo({
url: `/pages/product/goods?id=${info.id}&goodsId=${info.goodsId}`,
});
},
//取消发送
cancelModel () {
this.showHide = false
},
// 请求商品详情
commodityDetails () {
jumpObtain(this.resolve.skuid, this.resolve.goodsid).then((res) => {
this.goodListData = res.data.result.data
})
},
// 切换输入法时移动输入框(按照官方的上推页面的原理应该会自动适应不同的键盘高度-->官方bug)
goPag (kh) {
this.retractBox(0, 250)
if (this.keyHeight != 0) {
if (kh - this.keyHeight > 0) {
this.retractBox(this.keyHeight - kh, 250)
}
}
},
// 移动顶部的空盒子
messageBoxMove (x, t) {
var animation = uni.createAnimation({
duration: t,
timingFunction: 'linear',
})
this.animation = animation
animation.height(x).step()
this.anData = animation.export()
},
// 保持消息体可见
msgGo (type) {
const query = uni.createSelectorQuery()
// 延时100ms保证是最新的高度
setTimeout(() => {
// 获取消息体高度
query.select('#msgList').boundingClientRect(data => {
// 如果超过scorll高度就滚动scorll
if (type == 'up') {
this.go = data.height - this.oldHeight
} else if (type == 'down') {
this.go = data.height - wh + 120
}
// if (this.oldHeight > 0) {
// this.go = data.height - this.oldHeight
// } else {
// // if (data.height - (wh - 32) > 0) {
// this.go = data.height - wh + 120
// }
// 保证键盘第一次拉起时消息体能保持可见
var moveY = wh - data.height
// 超出页面则缩回空盒子
if (moveY - mgUpHeight < 0) {
// 小于0则视为0
if (moveY < 0) {
this.messageBoxMove(0, 200)
} else {
// 否则缩回盒子对应的高度
this.messageBoxMove(moveY, 200)
}
}
uni.pageScrollTo({
scrollTop: this.go,
duration: 0
})
this.oldHeight = data.height
}).exec();
}, 100)
},
// 回答问题的业务逻辑
answer (id) {
// 这里应该传入问题的id,模拟就用index代替了
},
// 不建议输入框聚焦时操作此动画
ckAdd () {
if (!this.showTow) {
this.retractBox(-180, 350)
} else {
this.retractBox(0, 200)
}
this.showTow = !this.showTow
},
hideKey () {
uni.hideKeyboard()
},
// 拉起/收回附加栏
retractBox (x, t) {
var animation = uni.createAnimation({
duration: t,
timingFunction: 'ease',
})
this.animation = animation
animation.translateY(x).step()
this.animationData = animation.export()
},
async getTalkMessage () {
let type = '';
await getTalkMessage(this.params).then(res => {
if (res.data.success) {
if (this.msgList.length >= 10) {
this.msgList.unshift(...res.data.result)
type = 'up'
} else {
this.msgList.unshift(...res.data.result)
type = 'down'
}
this.msgList.forEach(item => {
if (item.fromUser === this.user.id) {
item.my = true
}
})
}
})
console.log(this.msgList);
this.msgGo(type)
},
// 上拉加载
touchMoreMessage (e) {
if (e.target.scrollTop == 0) {
this.params.pageNumber = this.params.pageNumber + 1
this.getTalkMessage()
}
},
async getTalk (userId) {
getTalkByUser(userId).then(res => {
if (res.data.success) {
this.toUser = res.data.result
this.params.talkId = res.data.result.id
this.getTalkMessage()
}
})
},
// 处理消息时间是否显示
compareTime (index, datetime) {
if (datetime == undefined) {
return false;
}
if (typeof datetime == "number") {
datetime = this.unixToDate(datetime, "yyyy-MM-dd hh:mm");
}
if (this.msgList[index].is_revoke == 1) {
return false;
}
if (datetime) {
datetime = datetime.replace(/-/g, "/");
}
let time = Math.floor(Date.parse(datetime) / 1000);
let currTime = Math.floor(new Date().getTime() / 1000);
// 当前时间5分钟内时间不显示
if (currTime - time < 300) return false;
// 判断是否是最后一条消息,最后一条消息默认显示时间
if (index == this.msgList.length - 1) {
return true;
}
let nextDate
if (this.msgList[index + 1] && this.msgList[index + 1].createTime) {
nextDate = this.msgList[index + 1].createTime.replace(/-/g, "/");
if (nextDate - datetime < 300) return false;
}
return !(
this.unixToDate(new Date(datetime), "{y}-{m}-{d} {h}:{i}") ==
this.unixToDate(new Date(nextDate), "{y}-{m}-{d} {h}:{i}")
);
},
/**
* 将unix时间戳转换为指定格式
* @param unix 时间戳【秒】
* @param format 转换格式
* @returns {*|string}
*/
unixToDate (unix, format) {
if (!unix) return unix;
let _format = format || "yyyy-MM-dd hh:mm:ss";
const d = new Date(unix);
const o = {
"M+": d.getMonth() + 1,
"d+": d.getDate(),
"h+": d.getHours(),
"m+": d.getMinutes(),
"s+": d.getSeconds(),
"q+": Math.floor((d.getMonth() + 3) / 3),
S: d.getMilliseconds(),
};
if (/(y+)/.test(_format))
_format = _format.replace(
RegExp.$1,
(d.getFullYear() + "").substr(4 - RegExp.$1.length)
);
for (const k in o)
if (new RegExp("(" + k + ")").test(_format))
_format = _format.replace(
RegExp.$1,
RegExp.$1.length === 1 ?
o[k] :
("00" + o[k]).substr(("" + o[k]).length)
);
return _format;
},
onLoad((options: Record<string, string | undefined> = {}) => {
showHideModel.value = options.goodsid
// #ifdef H5
localImGoodsId.value = uni.getStorageSync('imGoodId')
// #endif
resolve.value = options
if (resolve.value.goodsid) {
commodityDetails()
}
const query = uni.getSystemInfoSync()
l = query.screenWidth / 750
wh = query.windowHeight
scrollHeight.value = query.windowHeight - 44 + 'px'
user.value = storage.getUserInfo()
toUser.value = storage.getTalkToUser()
if (options.talkId) {
params.value.talkId = options.talkId
fetchTalkMessages()
} else {
getTalk(options.userId!)
}
socket()
})
onHide(() => {
uni.closeSocket()
})
onUnload(() => {
// #ifdef H5
uni.setStorageSync('imGoodId', '')
// #endif
uni.closeSocket()
})
onPullDownRefresh(() => {
params.value.pageNumber = params.value.pageNumber + 1
fetchTalkMessages()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
})
onPageScroll(() => {
// #ifdef APP-PLUS
uni.hideKeyboard()
isShow.value = false
// #endif
})
function navigateToBottom() {
// #ifdef H5
isShow.value = true
;(proxy as any).$refs.inputRef?.focus()
// #endif
// #ifdef APP-PLUS
nextTick(() => {
uni.pageScrollTo({
scrollTop: 5000000,
duration: 50,
success: () => {
setTimeout(() => {
isShow.value = true
}, 200)
},
fail: () => {},
complete: () => {},
})
})
// #endif
}
function eventHandle() {
inputHeight.value = 0
isShow.value = false
}
function inputBindFocus(e: any) {
if (e.detail.height) {
// #ifdef APP-PLUS
if (uni.getSystemInfoSync().platform == 'ios') {
inputHeight.value = e.detail.height - 40
} else {
inputHeight.value = e.detail.height
}
// #endif
// #ifndef APP-PLUS
inputHeight.value = e.detail.height
// #endif
}
}
function sendMessage() {
if (msg.value == '') {
return 0
}
if (socketOpen.value == false) {
return
}
const payload = {
operation_type: 'MESSAGE',
to: toUser.value.userId,
from: user.value.id,
message_type: 'MESSAGE',
context: msg.value,
talk_id: params.value.talkId,
}
const data = JSON.stringify(payload)
uni.sendSocketMessage({
data: data,
})
msgList.value.push({
text: msg.value,
my: true,
messageType: 'MESSAGE',
})
const type = 'down'
msgGo(type)
msg.value = ''
}
function sendGoodsMessage() {
const payload = {
operation_type: 'MESSAGE',
to: toUser.value.userId,
from: user.value.id,
message_type: 'GOODS',
context: goodListData.value,
talk_id: params.value.talkId,
}
const data = JSON.stringify(payload)
uni.sendSocketMessage({
data: data,
})
msgList.value.push({
text: JSON.stringify(goodListData.value),
my: true,
messageType: 'GOODS',
})
showHide.value = false
// #ifdef H5
uni.setStorageSync('imGoodId', 1111111)
// #endif
nextTick(() => {
uni.pageScrollTo({
scrollTop: 2000000,
duration: 300,
})
})
}
function socket() {
uni.closeSocket()
socketOpen.value = false
try {
const url = config.baseWsUrl + '/' + storage.getAccessToken()
uni.connectSocket({
url: url,
})
uni.onSocketOpen(function () {
socketOpen.value = true
})
if (!socketOpen.value) {
uni.onSocketError(function (err: any) {
if (reconnectCount.value < 3) {
if (err && err.code != 1000) {
socketOpen.value = true
setTimeout(() => {
uni.connectSocket({
url: url,
})
}, 2000)
}
} else {
uni.closeSocket()
}
reconnectCount.value++
})
}
uni.onSocketMessage(function (res) {
const data = JSON.parse(res.data as string)
console.log(data.result)
if (data.messageResultType == 'MESSAGE') {
msgList.value.push(data.result)
console.log(msgList.value)
}
console.log(data)
msgGo()
})
} catch (e) {
uni.closeSocket()
}
uni.onSocketClose((res) => {
if (res.code != null && res.code != 1000) {
socket()
}
})
}
function linkTosOrders(val: string) {
const order = JSON.parse(val)
uni.navigateTo({
url: '/pages/order/orderDetail?sn=' + order.sn,
})
}
function jumpGoodDesc(item: any) {
const info = JSON.parse(item.text)
uni.navigateTo({
url: `/pages/product/goods?id=${info.id}&goodsId=${info.goodsId}`,
})
}
function cancelModel() {
showHide.value = false
}
function commodityDetails() {
jumpObtain(resolve.value.skuid, resolve.value.goodsid).then((res) => {
goodListData.value = res.data.result.data
})
}
function messageBoxMove(x: number, t: number) {
const animation = uni.createAnimation({
duration: t,
timingFunction: 'linear',
})
animation.height(x).step()
anData.value = animation.export()
}
function msgGo(type?: string) {
const query = uni.createSelectorQuery()
setTimeout(() => {
query
.select('#msgList')
.boundingClientRect((data: any) => {
if (type == 'up') {
go.value = data.height - oldHeight.value
} else if (type == 'down') {
go.value = data.height - wh + 120
}
const moveY = wh - data.height
if (moveY - mgUpHeight < 0) {
if (moveY < 0) {
messageBoxMove(0, 200)
} else {
messageBoxMove(moveY, 200)
}
}
uni.pageScrollTo({
scrollTop: go.value,
duration: 0,
})
oldHeight.value = data.height
})
.exec()
}, 100)
}
async function fetchTalkMessages() {
let type = ''
await fetchTalkMessageApi(params.value).then((res) => {
if (res.data.success) {
if (msgList.value.length >= 10) {
msgList.value.unshift(...res.data.result)
type = 'up'
} else {
msgList.value.unshift(...res.data.result)
type = 'down'
}
msgList.value.forEach((item) => {
if (item.fromUser === user.value.id) {
item.my = true
}
})
}
})
console.log(msgList.value)
msgGo(type)
}
function getTalk(userId: string) {
getTalkByUser(userId).then((res) => {
if (res.data.success) {
toUser.value = res.data.result
params.value.talkId = res.data.result.id
fetchTalkMessages()
}
})
}
</script>

View File

@@ -9,12 +9,12 @@
:border="false"
:auto-back="true"
></u-navbar>
<scroll-view class="list-scroll-content" scroll-y @scrolltolower="loadData(tabIndex)">
<scroll-view class="list-scroll-content" scroll-y @scrolltolower="fetchTalkList">
<!-- 消息列表 -->
<div class="iconBox">
<view class="icon-list">
<view class="icon-item" @click="cleanUnread()">
<view class="icon-item" @click="clearUnreadMessages()">
<div class="bag bag1">
<u-icon name="trash" size="50" color="#fff"></u-icon>
</div>
@@ -28,7 +28,7 @@
</view>
</view>
</div>
<u-search class="nav-search" v-model="userName" clearabled @change="userTalkList()" placeholder="搜索用户"
<u-search class="nav-search" v-model="userName" clearabled @change="fetchTalkList()" placeholder="搜索用户"
:show-action="false"></u-search>
<view class="talk-view" :key="index" v-for="(item, index) in talkList">
<view>
@@ -65,85 +65,80 @@
</view>
</template>
<script>
import { getTalkList, clearmeaager } from "@/api/im.js";
import storage from "@/utils/storage.js";
import { beautifyTime } from "@/utils/filters.js"
export default {
data () {
return {
storage,
count: {
loadStatus: "more",
},
talkList: [], //聊天列表
userName: '',
pointData: {}, //累计获取 未输入 集合
};
},
<script setup lang="ts">
import { ref } from 'vue'
import { onShow, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getTalkList, clearmeaager } from '@/api/im.js'
import storage from '@/utils/storage.js'
import { beautifyTime } from '@/utils/filters.js'
onShow () {
this.userTalkList();
},
onPullDownRefresh () {
this.userTalkList()
console.log('下拉事件');
setTimeout(function () {
uni.stopPullDownRefresh();
}, 1000);
},
/**
* 触底加载
*/
onReachBottom () {
this.userTalkList();
},
methods: {
beautifyTime,
onclickToTalkInfo (val) {
storage.setTalkToUser(val)
uni.navigateTo({
url:
"/pages/mine/im/index?talkId=" + val.id,
});
},
/**
* 获取聊天列表
*/
userTalkList () {
let params = {
userName: this.userName,
}
uni.showLoading({
title: "加载中",
});
getTalkList(params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
if (res.data.success) {
this.talkList = res.data.result;
console.log(this.talkList, 'this.talkListthis.talkList');
}
});
},
navigateTo (url) {
uni.navigateTo({
url,
});
},
cleanUnread () {
clearmeaager().then((res) => {
console.log(res);
if (res.data.code == 200) {
this.userTalkList();
uni.showToast({
icon: "none",
title: res.data.message,
});
}
const store = useStore()
const talkList = ref<any[]>([])
const userName = ref('')
onShow(() => {
fetchTalkList()
})
onPullDownRefresh(() => {
fetchTalkList()
console.log('下拉事件')
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
})
onReachBottom(() => {
fetchTalkList()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function onclickToTalkInfo(val: any) {
storage.setTalkToUser(val)
uni.navigateTo({
url: '/pages/mine/im/index?talkId=' + val.id,
})
}
function fetchTalkList() {
const params = {
userName: userName.value,
}
uni.showLoading({
title: '加载中',
})
getTalkList(params).then((res) => {
hideLoadingIfNeeded()
if (res.data.success) {
talkList.value = res.data.result
console.log(talkList.value, 'this.talkListthis.talkList')
}
})
}
function navigateTo(url: string) {
uni.navigateTo({
url,
})
}
function clearUnreadMessages() {
clearmeaager().then((res) => {
console.log(res)
if (res.data.code == 200) {
fetchTalkList()
uni.showToast({
icon: 'none',
title: res.data.message,
})
},
},
};
}
})
}
</script>
<style lang="scss" scoped>

View File

@@ -1,183 +1,98 @@
<template>
<view class="container">
<view class="list-cell b-b m-t" hover-class="cell-hover" :hover-stay-time="50">
<u-row gutter="12" justify="start" @click="navigateTo('/pages/msgTips/sysMsg/index')">
<u-col span="2" class="uCol" style="text-align:center;">
<image class="img" src="/static/mine/setting.png"></image>
</u-col>
<u-col span="7">
<p class="tit_title">系统消息</p>
<p class="tit_tips">查看系统消息</p>
</u-col>
<u-col span="3">
<view class="cell-more">
<u-tag size="mini" v-if="no_read.system_num>0" shape="circle" mode="dark" type="error" :text="no_read.system_num"></u-tag>
<span class="yticon icon-you"></span>
</view>
</u-col>
</u-row>
</view>
<!-- <view class="list-cell b-b m-t" hover-class="cell-hover" :hover-stay-time="50">
<u-row gutter="12" justify="start" @click="navigateTo('/pages/msgTips/packagemsg/index')">
<u-col span="2" class="uCol" style="text-align:center;">
<image class="img" src="/static/mine/logistics.png"></image>
</u-col>
<u-col span="7">
<p class="tit_title">物流消息</p>
<p class="tit_tips">查看物流消息</p>
</u-col>
<u-col span="3">
<view class="cell-more">
<u-tag v-if="no_read.logistics_num>0" shape="circle" mode="dark" type="warning" :text="no_read.logistics_num"></u-tag>
<span class="yticon icon-you"></span>
</view>
</u-col>
</u-row>
</view> -->
</view>
<view class="container">
<view class="list-cell b-b m-t" hover-class="cell-hover" :hover-stay-time="50">
<u-row gutter="12" justify="start" @click="navigateTo('/pages/mine/msgTips/sysMsg/index')">
<u-col span="2" class="uCol" style="text-align: center">
<image class="img" src="/static/mine/setting.png"></image>
</u-col>
<u-col span="7">
<p class="tit_title">系统消息</p>
<p class="tit_tips">查看系统消息</p>
</u-col>
<u-col span="3">
<view class="cell-more">
<u-tag
size="mini"
v-if="unreadCount.system_num > 0"
shape="circle"
mode="dark"
type="error"
:text="String(unreadCount.system_num)"
></u-tag>
<span class="yticon icon-you"></span>
</view>
</u-col>
</u-row>
</view>
</view>
</template>
<script>
import {
mapMutations
} from "vuex";
import * as API_Message from "@/api/members.js";
export default {
data() {
return {
no_read: ''
};
},
onLoad() {
this.GET_NoReadMessageNum();
},
methods: {
...mapMutations(["logout"]),
navigateTo(url) {
uni.navigateTo({
url
});
},
/** 获取未读消息数量信息 */
GET_NoReadMessageNum() {
API_Message.getNoReadMessageNum().then(response => {
this.no_read = response.data
})
}
}
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getNoReadMessageNum } from '@/api/members.js'
const unreadCount = ref<Record<string, number>>({ system_num: 0 })
onLoad(() => {
fetchUnreadCount()
})
function navigateTo(url: string) {
uni.navigateTo({ url })
}
function fetchUnreadCount() {
getNoReadMessageNum().then((response) => {
unreadCount.value = response.data || { system_num: 0 }
})
}
</script>
<style scoped lang='scss'>
.uCol {
display: flex;
justify-content: center !important;
}
.img {
width: 60rpx;
height: 60rpx;
}
.container {
background: #f9f9f9;
}
::v-deep .u-col-2 {
height: 60px;
line-height: 60px;
text-align: center !important;
}
.qicon {
text-align: center;
display: block;
font-size: 20px;
}
.redBox {
display: inline-block;
text-align: center;
line-height: 1.5em;
font-size: 12px;
min-width: 1.5em;
min-height: 1.5em;
background: #ed6533;
border-radius: 50%;
color: #fff;
}
.tit_title {
color: $u-main-color;
}
.tit_tips {
color: $u-tips-color;
}
.u-col-3 {
text-align: right !important;
padding-right: 20rpx !important;
}
.list-cell {
background: #fff;
align-items: baseline;
padding: 20rpx 0;
line-height: 60rpx;
background: #fff;
justify-content: center;
&.log-out-btn {
margin-top: 40rpx;
.cell-tit {
color: $uni-color-primary;
text-align: center;
margin-right: 0;
}
}
&.cell-hover {
background: #fafafa;
}
&.b-b:after {
left: 30rpx;
}
&.m-t {
margin-top: 16rpx;
}
.cell-more {
/* margin-top: 10rpx; */
height: 60rpx;
text-align: right;
/* display: flex;
justify-content: center; //这个是X轴居中
align-items: center; //这个是 Y轴居中 */
font-size: $font-lg;
color: $font-color-light;
/* width: 100rpx; */
}
.cell-tit {
flex: 1;
font-size: $font-base + 2rpx;
color: $font-color-dark;
margin-right: 10rpx;
}
.cell-tip {
font-size: $font-base;
color: $font-color-light;
}
}
<style scoped lang="scss">
.uCol {
display: flex;
justify-content: center !important;
}
.img {
width: 60rpx;
height: 60rpx;
}
.container {
background: #f9f9f9;
}
::v-deep .u-col-2 {
height: 60px;
line-height: 60px;
text-align: center !important;
}
.tit_title {
color: $u-main-color;
}
.tit_tips {
color: $u-tips-color;
}
.u-col-3 {
text-align: right !important;
padding-right: 20rpx !important;
}
.list-cell {
background: #fff;
align-items: baseline;
padding: 20rpx 0;
line-height: 60rpx;
justify-content: center;
&.cell-hover {
background: #fafafa;
}
&.m-t {
margin-top: 16rpx;
}
.cell-more {
height: 60rpx;
text-align: right;
font-size: $font-lg;
color: $font-color-light;
}
}
</style>

View File

@@ -1,99 +1,113 @@
<template>
<view class="container " style="font-size: 13px;">
<block v-for="(row, index) in messageList" :key="index">
<view class="msgItem">
<div class="msgMsg">
<div class="bagbar">{{$u.timeFormat(row.send_time, 'yyyy-mm-dd')}}</div>
</div>
<u-card @click="goDetail(row.sn,row.logi_id,row.ship_no)" :title="title" title-color="#666666" title-size="24" sub-title-color="#666666" sub-title-size="24" :border="false" :sub-title=row.status>
<template #body>
<view class="msg-body">
<image class="msgImg" :src="row.goods_img" mode=""></image>
<view class="msgView">
<view>{{row.goodsName}}</view>
<view class="msgNum">订单号:{{row.sn}}</view>
</view>
</view>
</template>
</u-card>
</view>
</block>
<uni-load-more :status="loadStatus"></uni-load-more>
<view class="container" style="font-size: 13px">
<block v-for="(row, index) in messageList" :key="index">
<view class="msgItem">
<div class="msgMsg">
<div class="bagbar">{{ formatSendTime(row.send_time) }}</div>
</div>
<u-card
@click="navigateToLogisticsDetail(row.sn, row.logi_id, row.ship_no)"
:title="pageTitle"
title-color="#666666"
title-size="24"
sub-title-color="#666666"
sub-title-size="24"
:border="false"
:sub-title="row.status"
>
<template #body>
<view class="msg-body">
<image class="msgImg" :src="row.goods_img" mode=""></image>
<view class="msgView">
<view>{{ row.goodsName }}</view>
<view class="msgNum">订单号:{{ row.sn }}</view>
</view>
</view>
</template>
</u-card>
</view>
</block>
<uni-load-more :status="loadStatus"></uni-load-more>
</view>
</template>
<script>
import * as API_Message from "@/api/message.js";
export default {
data() {
return {
messageList: [],
title: "物流更新通知",
subTitle: "运输中",
loadStatus:'more',
params: {
pageNumber: 1,
pageSize: 10,
},
loadStatus:'more'
};
},
onLoad(){
this.GET_LogisticsList(true);
},
onReachBottom() {
this.params.pageNumber++
this.GET_LogisticsList(false)
},
methods: {
goDetail(sn,logi_id,ship_no){
uni.navigateTo({
url:'/pages/msgTips/packagemsg/logisticsDetail?order_sn=' + sn +'&logi_id='+logi_id+'&ship_no='+ship_no,
})
},
//获取物流消息
GET_LogisticsList(reset){
if (reset) {
this.params.pageNumber = 1
}
uni.showLoading({
title:"加载中"
})
API_Message.getLogisticsMessages(this.params).then(async response => {
if (this.$store.state.isShowToast){ uni.hideLoading() }
const { data } = response
if (!data || !data.length) {
this.messageList.push(...data.data)
}
})
}
<script setup lang="ts">
import { ref, getCurrentInstance } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Message from '@/api/message.js'
const store = useStore()
const { proxy } = getCurrentInstance()!
const messageList = ref<any[]>([])
const pageTitle = '物流更新通知'
const loadStatus = ref('more')
const queryParams = ref({ pageNumber: 1, pageSize: 10 })
onLoad(() => {
fetchLogisticsList(true)
})
onReachBottom(() => {
queryParams.value.pageNumber++
fetchLogisticsList(false)
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function formatSendTime(time: number) {
return proxy.$u.timeFormat(time, 'yyyy-mm-dd')
}
function extractRecords(res: any) {
if (Array.isArray(res?.result?.records)) return res.result.records
if (Array.isArray(res?.result)) return res.result
if (Array.isArray(res?.data)) return res.data
return []
}
function navigateToLogisticsDetail(sn: string, logiId: string, shipNo: string) {
uni.navigateTo({
url: `/pages/mine/msgTips/packageMsg/logisticsDetail?order_sn=${sn}&logi_id=${logiId}&ship_no=${shipNo}`,
})
}
function fetchLogisticsList(reset: boolean) {
if (reset) {
queryParams.value.pageNumber = 1
messageList.value = []
loadStatus.value = 'more'
}
};
uni.showLoading({ title: '加载中' })
API_Message.getLogisticsMessages(queryParams.value).then((response) => {
hideLoadingIfNeeded()
const records = extractRecords(response.data)
if (records.length) {
messageList.value.push(...records)
} else {
loadStatus.value = 'noMore'
}
})
}
</script>
<style scoped lang='scss'>
.ddnumber {
color: $u-tips-color;
font-size: 24rpx;
}
.msg-body{
display: flex;
background-color: rgba(102, 110, 232, 0.0470588235294118);
.msgImg{
width: 160rpx;
height: 160rpx;
}
.msgView{
margin-left: 20rpx;
.msgNum:last-child{
margin-top: 60rpx;
}
}
<style scoped lang="scss">
.msg-body {
display: flex;
background-color: rgba(102, 110, 232, 0.0470588235294118);
.msgImg {
width: 160rpx;
height: 160rpx;
}
.msgView {
margin-left: 20rpx;
.msgNum:last-child {
margin-top: 60rpx;
}
}
}
.bagbar {
display: inline;
@@ -103,13 +117,8 @@ export default {
padding: 10rpx 20rpx;
background: $u-info-disabled;
}
.storeImg {
width: 100%;
height: 100rpx;
margin-right: 20rpx;
}
.container {
background: #F9F9F9;
background: #f9f9f9;
min-height: 100vh;
}
.msgMsg {
@@ -119,12 +128,8 @@ export default {
.msgItem {
padding: 1em 0;
}
view{
font-size: 13px;
color: #666666;
view {
font-size: 13px;
color: #666666;
}
u-card{
font-size: 13px;
color: #666666;
}
</style>
</style>

View File

@@ -2,21 +2,23 @@
<view class="logistics-detail">
<view class="card">
<view class="card-title">
<span>{{ logiList.shipper }}</span>快递 <span>{{ logiList.logisticCode }}</span>
<span>{{ logisticsInfo.shipper }}</span>快递 <span>{{ logisticsInfo.logisticCode }}</span>
</view>
<view class="time-line">
<u-time-line v-if="logiList.traces && logiList.traces.length != 0">
<u-time-line-item nodeTop="2" v-for="(item, index) in logiList.traces" :key="index">
<!-- 此处自定义了左边内容用一个图标替代 -->
<template v-slot:node >
<view v-if="index == logiList.traces.length - 1" class="u-node" :style="{ background: $lightColor }" style="padding: 0 4px">
<!-- 此处为uView的icon组件 -->
<u-time-line v-if="logisticsInfo.traces && logisticsInfo.traces.length != 0">
<u-time-line-item nodeTop="2" v-for="(item, index) in logisticsInfo.traces" :key="index">
<template #node>
<view
v-if="index == logisticsInfo.traces.length - 1"
class="u-node"
:style="{ background: lightColor }"
style="padding: 0 4px"
>
<u-icon name="pushpin-fill" color="#fff" :size="24"></u-icon>
</view>
</template>
<template v-slot:content>
<template #content>
<view>
<!-- <view class="u-order-title">待取件</view> -->
<view class="u-order-desc">{{ item.AcceptStation }}</view>
<view class="u-order-time">{{ item.AcceptTime }}</view>
</view>
@@ -29,32 +31,26 @@
</view>
</template>
<script>
import { getExpress } from "@/api/trade.js";
export default {
data() {
return {
express: "",
resData: {
title: "物流详情",
},
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getExpress } from '@/api/trade.js'
logiList: "",
activeStep: 0,
};
},
methods: {
init(sn) {
getExpress(sn).then((res) => {
this.logiList = res.data.result;
});
},
},
onLoad(option) {
let sn = option.order_sn;
this.init(sn);
},
};
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const logisticsInfo = ref<Record<string, any>>({})
onLoad((option) => {
fetchLogistics(option.order_sn)
})
function fetchLogistics(orderSn: string) {
getExpress(orderSn).then((res) => {
logisticsInfo.value = res.data.result || {}
})
}
</script>
<style lang="scss">
@@ -79,9 +75,6 @@ export default {
padding: 16rpx 32rpx;
}
}
.u-order-title {
font-weight: bold;
}
.u-order-desc {
font-size: 26rpx;
color: #666;

View File

@@ -30,20 +30,11 @@
</view>
</template>
<script>
import { mapMutations } from "vuex";
export default {
data() {
return {};
},
methods: {
...mapMutations(["logout"]),
}
};
<script setup lang="ts">
// 占位页,后续接入客服消息
</script>
<style scoped lang='scss'>
<style scoped lang="scss">
.msgTime {
font-size: 13px;
}
@@ -56,11 +47,6 @@ page {
vertical-align: middle;
border-radius: 0.4em;
}
.qicon {
text-align: center;
display: block;
font-size: 20px;
}
.redBox {
padding: 10rpx 12rpx;
display: inline-block;
@@ -71,7 +57,6 @@ page {
height: 1em;
background: #ed6533;
border-radius: 50%;
color: #fff;
}
.tit_title,
@@ -92,25 +77,14 @@ page {
}
.list-cell {
align-items: baseline;
padding: 20rpx 30rpx;
padding: 20rpx 30rpx;
line-height: 60rpx;
position: relative;
background: #fff;
justify-content: center;
&.log-out-btn {
margin-top: 40rpx;
.cell-tit {
color: $uni-color-primary;
text-align: center;
margin-right: 0;
}
}
&.cell-hover {
background: #fafafa;
}
&.b-b:after {
left: 30rpx;
}
&.m-t {
margin-top: 16rpx;
}
@@ -119,22 +93,12 @@ page {
margin-top: 10rpx;
height: 60rpx;
display: flex;
justify-content: center; //这个是X轴居中
align-items: center; //这个是 Y轴居中
justify-content: center;
align-items: center;
align-self: baseline;
font-size: $font-lg;
color: $font-color-light;
margin-left: 10rpx;
}
.cell-tit {
flex: 1;
font-size: $font-base + 2rpx;
color: $font-color-dark;
margin-right: 10rpx;
}
.cell-tip {
font-size: $font-base;
color: $font-color-light;
}
}
</style>
</style>

View File

@@ -2,17 +2,15 @@
<view class="container">
<block v-for="(row, index) in messageList" :key="index">
<view class="msgItem">
<div class="is_read">
<!-- {{row.is_read}} -->
<span v-if="row.is_read"></span>
<span v-else class="red">·</span>
</div>
<div class="msgMsg">{{$u.timeFormat(row.send_time, 'yyyy-mm-dd')}}</div>
<u-card :title="title" :title-size="35" :border="false">
<div class="is_read">
<span v-if="row.is_read"></span>
<span v-else class="red">·</span>
</div>
<div class="msgMsg">{{ formatSendTime(row.send_time) }}</div>
<u-card :title="pageTitle" :title-size="35" :border="false">
<template #body>
<view class="u-body-item u-flex u-row-between u-p-b-0">
<view class="u-body-item-title u-line-2">{{row.content}}</view>
<view class="u-body-item-title u-line-2">{{ row.content }}</view>
</view>
</template>
</u-card>
@@ -22,74 +20,85 @@
</view>
</template>
<script>
import { mapMutations } from "vuex";
import * as API_Message from "@/api/message.js";
export default {
data() {
return {
title: "系统消息",
subTitle: "未读",
finished: false,
loadStatus: "more",
params: {
pageNumber: 0,
pageSize: 5
},
messageList: []
};
},
onLoad() {
this.GET_MessageList(true);
},
onReachBottom() {
this.params.pageNumber++;
this.GET_MessageList(false);
},
methods: {
...mapMutations(["logout"]),
/** 获取站内消息 */
GET_MessageList(reset) {
if (reset) {
this.params.pageNumber = 1;
this.messageList = [];
}
uni.showLoading({
title: "加载中"
});
API_Message.getMessages(this.params).then(async response => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
const { data } = response;
if (!data || !data.length) {
this.messageList.push(...data.data);
this.handleReadPageMessages();
}
});
},
/** 设置消息已读 **/
handleReadPageMessages() {
const ids = this.messageList.map(item => item.id).join(",");
API_Message.messageMarkAsRead(ids).then(async () => {});
}
<script setup lang="ts">
import { ref, getCurrentInstance } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Message from '@/api/message.js'
const store = useStore()
const { proxy } = getCurrentInstance()!
const pageTitle = '系统消息'
const loadStatus = ref('more')
const queryParams = ref({ pageNumber: 1, pageSize: 5 })
const messageList = ref<any[]>([])
onLoad(() => {
fetchMessageList(true)
})
onReachBottom(() => {
queryParams.value.pageNumber++
fetchMessageList(false)
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function formatSendTime(time: number) {
return proxy.$u.timeFormat(time, 'yyyy-mm-dd')
}
function extractRecords(res: any) {
if (Array.isArray(res?.result?.records)) return res.result.records
if (Array.isArray(res?.result)) return res.result
if (Array.isArray(res?.data)) return res.data
return []
}
function fetchMessageList(reset: boolean) {
if (reset) {
queryParams.value.pageNumber = 1
messageList.value = []
loadStatus.value = 'more'
}
};
uni.showLoading({ title: '加载中' })
API_Message.getMessages(queryParams.value).then((response) => {
hideLoadingIfNeeded()
const res = response.data
const records = extractRecords(res)
if (records.length) {
messageList.value.push(...records)
markPageMessagesAsRead()
} else {
loadStatus.value = 'noMore'
}
})
}
function markPageMessagesAsRead() {
const ids = messageList.value.map((item) => item.id).join(',')
if (!ids) return
API_Message.messageMarkAsRead(ids)
}
</script>
<style scoped lang='scss'>
.is_read{
position: absolute;
right: 25px;
top: 80rpx;
z-index: 999;
<style scoped lang="scss">
.is_read {
position: absolute;
right: 25px;
top: 80rpx;
z-index: 999;
}
.container {
background: #f9f9f9;
min-height: 100vh;
}
.red{
color: coral;
font-size: 100rpx;
.red {
color: coral;
font-size: 100rpx;
}
.msgMsg {
text-align: center;
@@ -99,4 +108,4 @@ export default {
padding: 1em 0;
position: relative;
}
</style>
</style>

View File

@@ -14,7 +14,7 @@
:activeStyle="{ color: lightColor }"
class="collect-tabs"
:list="navList"
:scrollable="true"
:scrollable="false"
v-model:current="tabCurrentIndex"
></u-tabs>
</view>
@@ -22,278 +22,238 @@
</u-navbar>
<view class="collect-body">
<!-- 显示商品栏 -->
<view v-if="tabCurrentIndex == 0" class="tab-content">
<scroll-view class="list-scroll-content" scroll-y>
<u-empty style="margin-top: 40rpx" text="暂无收藏商品数据" mode="favor" v-if="goodsEmpty"></u-empty>
<template v-else>
<u-swipe-action
v-for="(item, index) in goodList"
:key="index"
class="collect-swipe"
<view v-if="tabCurrentIndex === 0" class="tab-content">
<u-empty style="margin-top: 40rpx" text="暂无收藏商品数据" mode="favor" v-if="goodsEmpty"></u-empty>
<template v-else>
<u-swipe-action
v-for="(item, index) in goodsList"
:key="item.skuId || item.goodsId || index"
class="collect-swipe"
>
<u-swipe-action-item
@open="openSwipeItem(item, 'goods')"
:show="item.selected"
:options="swipeOptions"
@click="removeGoodsCollection(item, index)"
:name="index"
>
<u-swipe-action-item
@open="openLeftChange(item, 'goods')"
:show="item.selected"
:options="LeftOptions"
@click="clickGoodsSwiperAction(item, index)"
:name="index"
>
<view class="goods" @click="goGoodsDetail(item)">
<u-image width="131rpx" height="131rpx" :src="item.image" mode="aspectFit">
<template #loading><u-loading></u-loading></template>
</u-image>
<view class="goods-intro">
<view class="goods-name">{{ item.goodsName }}</view>
<view class="goods-sn">{{ item.goods_sn }}</view>
<view class="goods-price">{{ unitPrice(item.price) }}</view>
</view>
<view class="goods" @click="goGoodsDetail(item)">
<u-image width="131rpx" height="131rpx" :src="item.image" mode="aspectFit">
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
<view class="goods-intro">
<view class="goods-name">{{ item.goodsName }}</view>
<view class="goods-sn">{{ item.goods_sn }}</view>
<view class="goods-price">{{ unitPrice(item.price) }}</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
</template>
</scroll-view>
</view>
</u-swipe-action-item>
</u-swipe-action>
</template>
</view>
<!-- 显示收藏的店铺栏 -->
<view v-else class="tab-content">
<scroll-view class="list-scroll-content" scroll-y>
<u-empty style="margin-top: 40rpx" text="暂无收藏店铺数据" mode="favor" v-if="storeEmpty"></u-empty>
<template v-else>
<u-swipe-action
v-for="(item, index) in storeList"
:key="index"
class="collect-swipe"
<u-empty style="margin-top: 40rpx" text="暂无收藏店铺数据" mode="favor" v-if="storeEmpty"></u-empty>
<template v-else>
<u-swipe-action
v-for="(item, index) in storeList"
:key="item.id || index"
class="collect-swipe"
>
<u-swipe-action-item
@open="openSwipeItem(item, 'store')"
:show="item.selected"
:options="swipeOptions"
@click="removeStoreCollection(item)"
:name="index"
>
<u-swipe-action-item
@open="openLeftChange(item, 'store')"
:show="item.selected"
:options="LeftOptions"
@click="clickStoreSwiperAction(item)"
:name="index"
>
<view class="store" @click="goStoreMainPage(item.id)">
<view class="intro">
<view class="store-logo">
<u-image width="102rpx" height="102rpx" :src="item.storeLogo" :alt="item.storeName"
mode="aspectFit">
<template #loading><u-loading></u-loading></template>
</u-image>
</view>
<view class="store-name">
<view>{{ item.storeName }}</view>
<u-tag size="mini" type="error" :color="$mainColor" v-if="item.selfOperated"
text="自营" mode="plain" shape="circle" />
</view>
<view class="store-collect">
<view>进店逛逛</view>
</view>
<view class="store" @click="goStoreMainPage(item.id)">
<view class="intro">
<view class="store-logo">
<u-image width="102rpx" height="102rpx" :src="item.storeLogo" :alt="item.storeName"
mode="aspectFit">
<template #loading><u-loading-icon></u-loading-icon></template>
</u-image>
</view>
<view class="store-name">
<view>{{ item.storeName }}</view>
<u-tag size="mini" type="error" :color="mainColor" v-if="item.selfOperated"
text="自营" mode="plain" shape="circle" />
</view>
<view class="store-collect">
<view>进店逛逛</view>
</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
</template>
</scroll-view>
</view>
</u-swipe-action-item>
</u-swipe-action>
</template>
</view>
</view>
</view>
</template>
<script>
import {
getGoodsCollection,
getStoreCollection,
deleteGoodsCollection,
deleteStoreCollection,
} from "@/api/members.js";
export default {
data() {
return {
lightColor:this.$lightColor,
// 商品左滑侧边栏
LeftOptions: [{
text: "取消",
style: {
backgroundColor: this.$lightColor,
},
}, ],
tabCurrentIndex: 0, //tab的下标默认为0也就是说会默认请求商品
navList: [
//tab显示数据
{
name: "商品(0)",
params: {
pageNumber: 1,
pageSize: 10,
},
},
{
name: "店铺(0)",
params: {
pageNumber: 1,
pageSize: 10,
},
},
],
goodsEmpty: false, //商品数据是否为空
storeEmpty: false, //店铺数据是否为空
goodList: [], //商品集合
storeList: [], //店铺集合
};
},
onShow() {
this.fetchReloadOrNextPage('reload')
},
onReachBottom() {
this.fetchReloadOrNextPage('next')
},
<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 {
getGoodsCollection,
getStoreCollection,
deleteGoodsCollection,
deleteStoreCollection,
} from '@/api/members.js'
methods: {
// 刷新或者下一页
fetchReloadOrNextPage(type) {
if(type == 'next'){
this.navList[this.tabCurrentIndex].params.pageNumber ++;
if (this.tabCurrentIndex == 0) {
this.getGoodList();
} else {
this.getStoreList();
}
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const mainColor = computed(() => store.getters.mainColor)
const swipeOptions = computed(() => [
{
text: '取消',
style: { backgroundColor: lightColor.value },
},
])
const tabCurrentIndex = ref(0)
const navList = ref([
{ name: '商品(0)', params: { pageNumber: 1, pageSize: 10 } },
{ name: '店铺(0)', params: { pageNumber: 1, pageSize: 10 } },
])
const goodsEmpty = ref(false)
const storeEmpty = ref(false)
const goodsList = ref<any[]>([])
const storeList = ref<any[]>([])
onShow(() => {
reloadOrLoadMore('reload')
})
onReachBottom(() => {
reloadOrLoadMore('next')
})
onPullDownRefresh(() => {
if (tabCurrentIndex.value === 0) {
navList.value[0].params.pageNumber = 1
goodsList.value = []
fetchGoodsList()
} else {
navList.value[1].params.pageNumber = 1
storeList.value = []
fetchStoreList()
}
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function reloadOrLoadMore(type: 'reload' | 'next') {
if (type === 'next') {
navList.value[tabCurrentIndex.value].params.pageNumber++
if (tabCurrentIndex.value === 0) {
fetchGoodsList()
} else {
fetchStoreList()
}
return
}
navList.value[0].params.pageNumber = 1
navList.value[1].params.pageNumber = 1
goodsEmpty.value = false
storeEmpty.value = false
goodsList.value = []
storeList.value = []
fetchGoodsList()
fetchStoreList()
}
function openSwipeItem(val: any, type: 'goods' | 'store') {
const targetList = type === 'goods' ? goodsList.value : storeList.value
targetList.forEach((item) => {
item.selected = false
})
val.selected = true
}
function removeGoodsCollection(val: any) {
deleteGoodsCollection(val.skuId).then((res) => {
if (res.statusCode === 200) {
goodsList.value = []
navList.value[0].params.pageNumber = 1
fetchGoodsList()
}
})
}
function removeStoreCollection(val: any) {
deleteStoreCollection(val.id).then((res) => {
if (res.statusCode === 200) {
storeList.value = []
navList.value[1].params.pageNumber = 1
fetchStoreList()
}
})
}
function goGoodsDetail(val: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${val.skuId}&goodsId=${val.goodsId}`,
})
}
function goStoreMainPage(id: string) {
uni.navigateTo({
url: `/pages/product/shopPage?id=${id}`,
})
}
function fetchGoodsList() {
uni.showLoading({ title: '加载中' })
getGoodsCollection(navList.value[0].params, 'GOODS')
.then((res) => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
if (res.data.success) {
const data = res.data.result
navList.value[0].name = `商品(${data.total})`
goodsEmpty.value = data.total === 0
if (data.records?.length) {
const records = data.records.map((item: any) => ({ ...item, selected: false }))
goodsList.value.push(...records)
}
else{
this.navList[0].params.pageNumber = 1;
this.navList[1].params.pageNumber = 1;
this.goodList = [];
this.storeList = [];
this.getGoodList();
this.getStoreList();
}
},
/**
* 打开商品左侧取消收藏
*/
openLeftChange(val, type) {
const way = type === "goods" ? this.goodList : this.storeList;
way.forEach((item) => {
item.selected = false;
});
val.selected = true;
},
/**
* 点击商品左侧取消收藏
*/
clickGoodsSwiperAction(val) {
deleteGoodsCollection(val.skuId).then((res) => {
if (res.statusCode == 200) {
this.storeList = [];
this.goodList = [];
this.getGoodList();
}
});
},
/**
* 点击店铺左侧取消收藏
*/
clickStoreSwiperAction(val) {
deleteStoreCollection(val.id).then((res) => {
if (res.statusCode == 200) {
this.storeList = [];
this.getStoreList();
}
});
},
/**
* 查看商品详情
*/
goGoodsDetail(val) {
//商品详情
uni.navigateTo({
url: "/pages/product/goods?id=" + val.skuId + "&goodsId=" + val.goodsId,
});
},
/**
* 查看店铺详情
*/
goStoreMainPage(id) {
//店铺主页
uni.navigateTo({
url: "/pages/product/shopPage?id=" + id,
});
},
/**
* 获取商品集合
*/
getGoodList() {
uni.showLoading({
title: "加载中",
});
getGoodsCollection(this.navList[0].params, "GOODS").then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
uni.stopPullDownRefresh();
if (res.data.success) {
let data = res.data.result;
data.selected = false;
this.navList[0].name = `商品(${data.total})`;
if (data.total == 0) {
this.goodsEmpty = true;
} else if (data.total < 10) {
this.goodsLoad = "noMore";
this.goodList.push(...data.records);
} else {
this.goodList.push(...data.records);
if (data.total.length < 10) this.goodsLoad = "noMore";
}
}
});
},
/**
* 获取店铺集合
*/
getStoreList() {
uni.showLoading({
title: "加载中",
});
getStoreCollection(this.navList[1].params, "STORE").then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
uni.stopPullDownRefresh();
if (res.data.success) {
let data = res.data.result;
data.selected = false;
this.navList[1].name = `店铺(${data.total})`;
if (data.total == 0) {
this.storeEmpty = true;
} else if (data.total < 10) {
this.storeList.push(...data.records);
}
}
});
},
},
/**
* 下拉刷新时
*/
onPullDownRefresh() {
if (this.tabCurrentIndex == 0) {
this.navList[0].params.pageNumber = 1;
this.goodList = [];
this.getGoodList();
} else {
this.navList[1].params.pageNumber = 1;
this.storeList = [];
this.getStoreList();
}
},
};
})
.catch(() => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
})
}
function fetchStoreList() {
uni.showLoading({ title: '加载中' })
getStoreCollection(navList.value[1].params, 'STORE')
.then((res) => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
if (res.data.success) {
const data = res.data.result
navList.value[1].name = `店铺(${data.total})`
storeEmpty.value = data.total === 0
if (data.records?.length) {
const records = data.records.map((item: any) => ({ ...item, selected: false }))
storeList.value.push(...records)
}
}
})
.catch(() => {
hideLoadingIfNeeded()
uni.stopPullDownRefresh()
})
}
</script>
<style lang="scss">
@@ -324,15 +284,18 @@
.collect-body {
flex: 1;
min-height: 0;
width: 100%;
}
.tab-content {
height: 100%;
width: 100%;
}
.list-scroll-content {
height: 100%;
:deep(.u-tabs),
:deep(.u-tabs__wrapper),
:deep(.u-tabs__wrapper__scroll-view-wrapper),
:deep(.u-tabs__wrapper__scroll-view),
:deep(.u-tabs__wrapper__nav) {
width: 100%;
}

View File

@@ -7,14 +7,14 @@
:auto-back="true"
>
<template #right>
<div class="light-color edit" @click="isEdit = !isEdit">{{ !isEdit ? '编辑' : '完成'}}</div>
<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="whetherEmpty"></u-empty>
<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
@@ -26,9 +26,9 @@
<u-swipe-action-item
:show="item.show"
:name="index"
@click="delTracks"
@open="open"
:options="options"
@click="deleteTracks"
@open="openSwipe"
:options="swipeOptions"
>
<view class="myTracks-item">
<u-checkbox-group v-if="isEdit" class="store-line-check">
@@ -37,7 +37,7 @@
shape="circle"
:active-color="lightColor"
v-model:checked="item.checked"
@change="checkboxChangeDP(item)"
@change="onTrackCheckChange(item)"
></u-checkbox>
</u-checkbox-group>
<view class="myTracks-item-img" @click.stop="navigateToDetail(item)">
@@ -56,7 +56,7 @@
</u-swipe-action>
<view class="myTracks-divider"></view>
</block>
<view v-if="isEdit" class="submit" @click="handleClickDeleteSelected">
<view v-if="isEdit" class="submit" @click="deleteSelectedTracks">
删除所选
</view>
</view>
@@ -64,172 +64,147 @@
</view>
</template>
<script>
import {
myTrackList,
deleteHistoryListId
} from "@/api/members.js";
import { getStoreBaseInfo } from "@/api/store.js";
<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'
export default {
data() {
return {
isEdit:false,
whetherEmpty: false, //是否数据为空
params: {
pageNumber: 1,
pageSize: 10,
order: "desc",
sort: "updateTime",
},
lightColor:this.$lightColor,
options: [{
text: '删除',
style: {
backgroundColor: '#dd524d'
}
}],
trackList: [], //足迹列表
storeNameMap: {},
};
},
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
/**
* 滑到底部加载下一页数据
*/
onReachBottom() {
this.params.pageNumber++;
this.getList();
},
onShow() {
this.params.pageNumber = 1
this.trackList = [];
this.getList();
},
onPullDownRefresh() {
this.trackList = [];
this.getList();
},
methods: {
getStoreName(item) {
return item.storeName || this.storeNameMap[item.storeId] || "";
},
async enrichStoreNames(records) {
const storeIds = [
...new Set(
records
.filter((item) => !item.storeName && item.storeId)
.map((item) => item.storeId)
),
].filter((storeId) => !this.storeNameMap[storeId]);
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>>({})
await Promise.all(
storeIds.map(async (storeId) => {
try {
const res = await getStoreBaseInfo(storeId);
const name = res.data?.result?.storeName;
if (name) {
this.storeNameMap[storeId] = name;
}
} catch (e) {
// ignore
}
})
);
},
checkboxChangeDP(val){
console.log(val)
},
// 删除所选的数据
handleClickDeleteSelected(val){
const ids = this.trackList.filter(item=>item.checked).map(item=>item.goodsId);
if(!ids.length){
uni.showToast({
title:"请选择删除数据",
icon:"none"
})
}else{
this.delTracks(0,ids)
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
}
},
/**
* 导航到店铺
*/
navigateToStore(val) {
uni.navigateTo({
url: "/pages/product/shopPage?id=" + val.storeId,
});
},
open(index) {
// 先将正在被操作的swipeAction标记为打开状态否则由于props的特性限制
// 原本为'false',再次设置为'false'会无效
this.trackList[index].show = true;
this.trackList.map((val, idx) => {
if (index != idx) this.trackList[idx].show = false;
})
},
/**
* 跳转详情
*/
navigateToDetail(item) {
uni.navigateTo({
url: "/pages/product/goods?id=" + item.id + "&goodsId=" + item.goodsId,
});
},
} catch {
// ignore
}
})
)
}
/**
* 获取我的足迹列表
*/
getList() {
uni.showLoading({
title: "加载中",
});
myTrackList(this.params).then(async (res) => {
uni.stopPullDownRefresh();
uni.hideLoading();
if (res.statusCode == 200) {
const records = res.data.result.records || [];
records.forEach((item) => {
item.show = false;
item.checked = false;
});
function onTrackCheckChange(_val: any) {
// 勾选状态由 v-model:checked 维护
}
if (!records.length) {
if (this.trackList.length === 0) {
this.whetherEmpty = true;
}
} else {
await this.enrichStoreNames(records);
this.trackList.push(...records);
}
}
});
},
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}` })
}
/**
* 删除足迹
*/
delTracks(e, ids) {
const index = typeof e === 'object' ? (e.name ?? e.index) : e;
const goodsId = ids || this.trackList[index]?.goodsId;
if (!goodsId) return;
deleteHistoryListId(goodsId).then((res) => {
if (res.data.code == 200) {
this.trackList = [];
this.params.pageNumber = 1
this.getList();
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
},
},
};
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 ?? 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>

View File

@@ -1,249 +1,245 @@
<template>
<view class="content">
<view class="portrait-box">
<image src="/static/pointTrade/point_bg_1.png" mode=""></image>
<image class="point-img" src="/static/pointTrade/tradehall.png" />
<view class="position-point">
</view>
</view>
<view class="point-summary">
<view class="point-summary-item">
<text>累计获得</text>
<text class="pcolor">{{ pointData.totalPoint || 0 }}</text>
</view>
<view class="point-summary-divider"></view>
<view class="point-summary-item">
<text>剩余积分</text>
<text class="pcolor">{{ pointData.point || 0 }}</text>
</view>
</view>
<div class="point-list">
<view class="point-item" v-for="(item, index) in pointList" :key="index">
<view class="point-item-left">
<view class="point-label">{{ item.content }}</view>
<view class="point-item-time">{{ item.createTime }}</view>
</view>
<view class="point-item-value" :class="[item.pointType == 'INCREASE' ? 'plus' : 'reduce']">
<text>{{ item.pointType == "INCREASE" ? "+" : "-" }}</text>{{ item.variablePoint }}
</view>
</view>
<uni-load-more :status="count.loadStatus"></uni-load-more>
</div>
</view>
</template>
<script>
import { getPointsData } from "@/api/members.js";
import { getMemberPointSum } from "@/api/members.js";
export default {
data() {
return {
count: {
loadStatus: "more",
},
pointList: [], //积分数据集合
params: {
pageNumber: 1,
pageSize: 10,
},
pointData: {}, //累计获取 未输入 集合
};
},
onLoad() {
this.initPointData();
this.getList();
},
/**
* 触底加载
*/
onReachBottom() {
this.params.pageNumber++;
this.getList();
},
methods: {
/**
* 获取积分数据
*/
getList() {
let params = this.params;
uni.showLoading({
title: "加载中",
});
getPointsData(params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
if (res.data.success) {
let data = res.data.result.records;
if (data.length < 10) {
this.count["loadStatus"] = "noMore";
this.pointList.push(...data);
} else {
this.pointList.push(...data);
if (data.length < 10) this.count["loadStatus"] = "noMore";
}
}
});
},
/**
* 获得累计积分使用
*/
initPointData() {
getMemberPointSum().then((res) => {
this.pointData = res.data.result;
});
},
},
};
</script>
<style lang="scss" scoped>
.point-list {
margin-top: 20rpx;
}
.title {
height: 80rpx;
text-align: center;
line-height: 80rpx;
font-size: 32rpx;
font-weight: bold;
}
.plus{
color: $light-color;
font-weight: bold;
}
.reduce{
color: $weChat-color;
font-weight: bold;
}
.point-item {
width: 100%;
min-height: 130rpx;
padding: 24rpx 20rpx;
background: #ffffff;
font-size: $font-sm;
border-bottom: 1px solid $border-color-light;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
.point-item-left {
flex: 1;
min-width: 0;
line-height: 40rpx;
}
.point-item-time {
color: #999;
}
.point-item-value {
flex-shrink: 0;
width: 100rpx;
text-align: center;
}
}
.point-summary {
display: flex;
align-items: center;
min-height: 100rpx;
padding: 28rpx 0;
background: #ffffff;
border-radius: 0 0 20rpx 20rpx;
margin: 0 20rpx;
font-size: 26rpx;
box-sizing: border-box;
.point-summary-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.point-summary-divider {
width: 1px;
height: 48rpx;
background: $border-color-light;
flex-shrink: 0;
}
.pcolor {
color: $light-color;
margin-left: 8rpx;
}
}
.content {
background: #f9f9f9;
}
.more {
text-align: right;
color: $u-tips-color;
font-size: 24rpx;
padding-right: 40rpx !important;
}
.portrait-box {
background-color: $main-color;
height: 250rpx;
background: linear-gradient(91deg, $light-color 1%, $aider-light-color 99%);
border-radius: 20rpx 20rpx 0 0;
margin: 20rpx 20rpx 0;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #ffffff;
> image:first-child {
width: 263rpx;
height: 250rpx;
position: absolute;
left: 0;
bottom: 0;
transform: rotateY(180deg);
}
.position-point {
position: absolute;
right: -2rpx;
top: 0;
.apply-point {
margin-top: 30rpx;
text-align: center;
line-height: 40rpx;
font-size: $font-sm;
color: #ffffff;
width: 142rpx;
height: 40rpx;
background: rgba(#ffffff, 0.2);
border-radius: 20rpx 0px 0px 20rpx;
}
}
.point-img {
height: 108rpx;
width: 108rpx;
margin-bottom: 30rpx;
}
.point {
font-size: 56rpx;
}
}
.point-label {
font-weight: bold;
margin-bottom: 10rpx;
color: #666666;
}
</style>
<template>
<view class="content">
<view class="portrait-box">
<image src="/static/pointTrade/point_bg_1.png" mode=""></image>
<image class="point-img" src="/static/pointTrade/tradehall.png" />
<view class="position-point">
</view>
</view>
<view class="point-summary">
<view class="point-summary-item">
<text>累计获得</text>
<text class="pcolor">{{ pointSummary.totalPoint || 0 }}</text>
</view>
<view class="point-summary-divider"></view>
<view class="point-summary-item">
<text>剩余积分</text>
<text class="pcolor">{{ pointSummary.point || 0 }}</text>
</view>
</view>
<view class="point-list">
<view class="point-item" v-for="(item, index) in pointList" :key="index">
<view class="point-item-left">
<view class="point-label">{{ item.content }}</view>
<view class="point-item-time">{{ item.createTime }}</view>
</view>
<view class="point-item-value" :class="[item.pointType == 'INCREASE' ? 'plus' : 'reduce']">
<text>{{ item.pointType == "INCREASE" ? "+" : "-" }}</text>{{ item.variablePoint }}
</view>
</view>
<uni-load-more :status="loadStatus"></uni-load-more>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getPointsData, getMemberPointSum } from '@/api/members.js'
const store = useStore()
const loadStatus = ref('more')
const pointList = ref<any[]>([])
const params = ref({ pageNumber: 1, pageSize: 10 })
const pointSummary = ref<Record<string, any>>({})
onLoad(() => {
fetchPointSummary()
fetchPointLogList()
})
onReachBottom(() => {
params.value.pageNumber++
fetchPointLogList()
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function fetchPointLogList() {
uni.showLoading({ title: '加载中' })
getPointsData(params.value).then((res) => {
hideLoadingIfNeeded()
if (res.data.success) {
const data = res.data.result.records || []
if (data.length < params.value.pageSize) {
loadStatus.value = 'noMore'
}
pointList.value.push(...data)
}
})
}
function fetchPointSummary() {
getMemberPointSum().then((res) => {
pointSummary.value = res.data.result
})
}
</script>
<style lang="scss">
page,
.content {
min-height: 100vh;
background: #f9f9f9;
}
.content {
overflow: hidden;
}
.point-list {
margin-top: 20rpx;
background: #f9f9f9;
min-height: calc(100vh - 390rpx);
padding-bottom: 24rpx;
box-sizing: border-box;
}
.title {
height: 80rpx;
text-align: center;
line-height: 80rpx;
font-size: 32rpx;
font-weight: bold;
}
.plus{
color: $light-color;
font-weight: bold;
}
.reduce{
color: $weChat-color;
font-weight: bold;
}
.point-item {
width: 100%;
min-height: 130rpx;
padding: 24rpx 20rpx;
background: #ffffff;
font-size: $font-sm;
border-bottom: 1px solid $border-color-light;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
.point-item-left {
flex: 1;
min-width: 0;
line-height: 40rpx;
}
.point-item-time {
color: #999;
}
.point-item-value {
flex-shrink: 0;
width: 100rpx;
text-align: center;
}
}
.point-summary {
display: flex;
align-items: center;
min-height: 100rpx;
padding: 28rpx 0;
background: #ffffff;
border-radius: 0 0 20rpx 20rpx;
margin: 0 20rpx;
font-size: 26rpx;
box-sizing: border-box;
.point-summary-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.point-summary-divider {
width: 1px;
height: 48rpx;
background: $border-color-light;
flex-shrink: 0;
}
.pcolor {
color: $light-color;
margin-left: 8rpx;
}
}
.more {
text-align: right;
color: $u-tips-color;
font-size: 24rpx;
padding-right: 40rpx !important;
}
.portrait-box {
background-color: $main-color;
height: 250rpx;
background: linear-gradient(91deg, $light-color 1%, $aider-light-color 99%);
border-radius: 20rpx 20rpx 0 0;
margin: 20rpx 20rpx 0;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #ffffff;
> image:first-child {
width: 263rpx;
height: 250rpx;
position: absolute;
left: 0;
bottom: 0;
transform: rotateY(180deg);
}
.position-point {
position: absolute;
right: -2rpx;
top: 0;
.apply-point {
margin-top: 30rpx;
text-align: center;
line-height: 40rpx;
font-size: $font-sm;
color: #ffffff;
width: 142rpx;
height: 40rpx;
background: rgba(#ffffff, 0.2);
border-radius: 20rpx 0px 0px 20rpx;
}
}
.point-img {
height: 108rpx;
width: 108rpx;
margin-bottom: 30rpx;
}
.point {
font-size: 56rpx;
}
}
.point-label {
font-weight: bold;
margin-bottom: 10rpx;
color: #666666;
}
.point-list .uni-load-more {
background: #f9f9f9;
}
</style>

View File

@@ -16,7 +16,7 @@
<u-cell-group class="cell-group" :border="false">
<!-- #ifdef APP-PLUS -->
<u-cell v-if="IosWhether" is-link title="去评分" @click="checkStar"></u-cell>
<u-cell v-if="showIosRating" is-link title="去评分" @click="openAppStoreRating"></u-cell>
<u-cell is-link title="功能介绍" @click="navigateTo('/pages/mine/set/versionFunctionList')"></u-cell>
<u-cell is-link title="检查更新" @click="checkUpdate"></u-cell>
<!-- #endif -->
@@ -40,93 +40,72 @@
</view>
</template>
<script>
import APPUpdate from "@/plugins/APPUpdate";
import config from "@/config/config";
import { getAppVersion } from "@/api/message.js";
export default {
data() {
return {
config,
IosWhether: false,
editionHistory: [],
versionData: {},
localVersion: "",
params: {
pageNumber: 1,
pageSize: 5,
},
};
},
onLoad() {
// #ifdef APP-PLUS
const platform = uni.getSystemInfoSync().platform;
if (platform === "android") {
this.params.type = 0;
} else {
this.IosWhether = true;
this.params.type = 1;
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import APPUpdate from '@/plugins/APPUpdate'
import config from '@/config/config'
import { getAppVersion } from '@/api/message.js'
const showIosRating = ref(false)
const versionData = ref<Record<string, any>>({})
const localVersion = ref<Record<string, any>>({})
const params = ref<Record<string, any>>({ pageNumber: 1, pageSize: 5 })
onLoad(() => {
// #ifdef APP-PLUS
const platform = uni.getSystemInfoSync().platform
if (platform === 'android') {
params.value.type = 0
} else {
showIosRating.value = true
params.value.type = 1
}
fetchRemoteVersion(platform)
plus.runtime.getProperty(plus.runtime.appid, (inf) => {
localVersion.value = {
versionCode: inf.version.replace(/\./g, ''),
version: inf.version,
}
this.getVersion(platform);
})
// #endif
plus.runtime.getProperty(plus.runtime.appid, (inf) => {
this.localVersion = {
versionCode: inf.version.replace(/\./g, ""),
version: inf.version,
};
});
// #endif
// #ifdef MP-WEIXIN
const accountInfo = wx.getAccountInfoSync()
localVersion.value = {
versionCode: accountInfo.miniProgram.version.replace(/\./g, ''),
version: accountInfo.miniProgram.version,
envVersion: accountInfo.miniProgram.envVersion,
}
// #endif
})
// #ifdef MP-WEIXIN
const accountInfo = wx.getAccountInfoSync();
this.version_number = accountInfo.miniProgram.version;
this.localVersion = {
versionCode: accountInfo.miniProgram.version.replace(/\./g, ""),
version: accountInfo.miniProgram.version,
envVersion: accountInfo.miniProgram.envVersion,
};
// #endif
},
async function fetchRemoteVersion(platform: string) {
const type = platform === 'android' ? 'ANDROID' : 'IOS'
const res = await getAppVersion(type)
if (res.data.success) {
versionData.value = res.data.result
}
}
methods: {
async getVersion(platform) {
let type;
platform == "android" ? (type = "ANDROID") : (type = "IOS");
function navigateTo(url: string) {
uni.navigateTo({ url })
}
let res = await getAppVersion(type);
if (res.data.success) {
this.versionData = res.data.result;
}
},
function openAppStoreRating() {
plus.runtime.launchApplication({
action: `itms-apps://itunes.apple.com/app/${config.iosAppId}?action=write-review`,
})
}
navigateTo(url) {
uni.navigateTo({
url,
});
},
checkStar() {
plus.runtime.launchApplication({
action: `itms-apps://itunes.apple.com/app/${config.iosAppId}?action=write-review`,
});
},
checkUpdate() {
if (
this.versionData.version.replace(/\./g, "") <
this.localVersion.versionCode
) {
APPUpdate();
} else {
uni.showToast({
title: "当前版本已是最新版",
duration: 2000,
icon: "none",
});
}
},
},
};
function checkUpdate() {
if (versionData.value.version?.replace(/\./g, '') < localVersion.value.versionCode) {
APPUpdate()
} else {
uni.showToast({ title: '当前版本已是最新版', duration: 2000, icon: 'none' })
}
}
</script>
<style lang="scss" scoped>

View File

@@ -4,9 +4,9 @@
<view class="box-title">猜你想问</view>
<view
class="feedBack-item"
:class="{ active: feedBack.type == item.value }"
@click="handleClick(index)"
v-for="(item, index) in list"
:class="{ active: feedbackForm.type == item.value }"
@click="selectFeedbackType(index)"
v-for="(item, index) in feedbackTypeList"
:key="index"
>
{{ item.text }}
@@ -15,11 +15,11 @@
<view class="feedBack-box">
<view class="box-title">问题反馈
<text class="box-tag" v-if="feedBack.type">@{{ list.find(item => item.value == feedBack.type).text }}</text>
<text class="box-tag" v-if="feedbackForm.type">@{{ feedbackTypeList.find(item => item.value == feedbackForm.type)?.text }}</text>
</view>
<u-textarea
class="field-textarea"
v-model="feedBack.context"
v-model="feedbackForm.context"
placeholder="请输入反馈信息"
border="none"
height="240"
@@ -39,7 +39,7 @@
<view class="box-title">手机号</view>
<u-input
class="field-input"
v-model="feedBack.mobile"
v-model="feedbackForm.mobile"
type="number"
maxlength="11"
border="none"
@@ -48,98 +48,69 @@
></u-input>
</view>
<view class="submit" @click="submit()">提交</view>
<view class="submit" @click="submitFeedback">提交</view>
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import config from "@/config/config";
import { feedBack } from "@/api/members.js";
import { handleUploadAfterRead } from "@/utils/uploadHelper.js";
export default {
data() {
return {
storage,
config,
feedBack: {
type: "FUNCTION",
context: "",
mobile: "",
},
inputStyle: {
background: "#fafafa",
borderRadius: "12rpx",
padding: "0 24rpx",
height: "80rpx",
},
uploadFileList: [],
list: [
{ text: "功能相关", value: "FUNCTION" },
{ text: "优化反馈", value: "OPTIMIZE" },
{ text: "其他", value: "OTHER" },
],
};
},
methods: {
// 点击反馈内容
handleClick(index) {
this.feedBack["type"] = this.list[index].value;
},
<script setup lang="ts">
import { ref, reactive, getCurrentInstance } from 'vue'
import { feedBack as submitFeedBackApi } from '@/api/members.js'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
onUploadAfterRead(event) {
handleUploadAfterRead(event, this.uploadFileList, (urls) => {
this.feedBack.images = urls.join(",");
});
},
const { proxy } = getCurrentInstance()!
/**
* 提交意见反馈
*/
submit() {
if (!this.feedBack.type) {
uni.showToast({
title: "请填写反馈类型",
duration: 2000,
icon: "none",
});
return false;
}
if (!this.feedBack.context) {
uni.showToast({
title: "请填写反馈信息",
duration: 2000,
icon: "none",
});
return false;
}
if (this.feedBack.mobile && !this.$u.test.mobile(this.feedBack.mobile)) {
uni.showToast({
title: "请填写您的正确手机号",
duration: 2000,
icon: "none",
});
return false;
}
/** 提交 */
feedBack(this.feedBack).then((res) => {
if (res.data.success) {
uni.showToast({
title: "提交成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack({
delta: 1,
});
}, 500);
}
});
},
},
};
const feedbackForm = reactive({
type: 'FUNCTION',
context: '',
mobile: '',
images: '',
})
const inputStyle = {
background: '#fafafa',
borderRadius: '12rpx',
padding: '0 24rpx',
height: '80rpx',
}
const uploadFileList = ref<any[]>([])
const feedbackTypeList = [
{ text: '功能相关', value: 'FUNCTION' },
{ text: '优化反馈', value: 'OPTIMIZE' },
{ text: '其他', value: 'OTHER' },
]
function selectFeedbackType(index: number) {
feedbackForm.type = feedbackTypeList[index].value
}
function onUploadAfterRead(event: any) {
handleUploadAfterRead(event, uploadFileList.value, (urls) => {
feedbackForm.images = urls.join(',')
})
}
function submitFeedback() {
if (!feedbackForm.type) {
uni.showToast({ title: '请填写反馈类型', duration: 2000, icon: 'none' })
return
}
if (!feedbackForm.context) {
uni.showToast({ title: '请填写反馈信息', duration: 2000, icon: 'none' })
return
}
if (feedbackForm.mobile && !proxy.$u.test.mobile(feedbackForm.mobile)) {
uni.showToast({ title: '请填写您的正确手机号', duration: 2000, icon: 'none' })
return
}
submitFeedBackApi(feedbackForm).then((res) => {
if (res.data.success) {
uni.showToast({ title: '提交成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 500)
}
})
}
</script>
<style lang="scss" scoped>

View File

@@ -5,48 +5,54 @@
<view>点击修改头像</view>
</view>
<u-form :model="form" ref="uForm" class="form">
<u-form-item label="昵称" label-width="150" :border-bottom="true">
<up-form
:model="form"
ref="uForm"
class="form"
label-position="left"
label-width="180rpx"
>
<up-form-item label="昵称" label-width="180rpx" :border-bottom="true">
<u-input
v-model="form.nickName"
border="none"
input-align="right"
placeholder="请输入昵称"
/>
</u-form-item>
</up-form-item>
<u-form-item label="性别" label-width="150" :border-bottom="true">
<up-form-item label="性别" label-width="180rpx" :border-bottom="true">
<view class="sex-row">
<u-radio-group v-model="form.sex" :active-color="lightColor" :gap="40">
<u-radio name="1" label="男" shape="circle"></u-radio>
<u-radio name="0" label="女" shape="circle"></u-radio>
</u-radio-group>
</view>
</u-form-item>
</up-form-item>
<u-form-item label="生日" label-width="150" :border-bottom="true">
<up-form-item label="生日" label-width="180rpx" :border-bottom="true">
<view class="form-value" @click="showBirthday = true">
{{ birthday || '请选择出生日期' }}
</view>
<template #right>
<u-icon name="arrow-right" color="#ccc" size="16"></u-icon>
</template>
</u-form-item>
</up-form-item>
<u-form-item label="城市" label-width="150" :border-bottom="true">
<up-form-item label="城市" label-width="180rpx" :border-bottom="true">
<view class="form-value" @click="clickRegion">
{{ form.___path || '请选择城市' }}
{{ form.regionPath || '请选择城市' }}
</view>
<template #right>
<u-icon name="arrow-right" color="#ccc" size="16"></u-icon>
</template>
</u-form-item>
</up-form-item>
<u-form-item label="手机号" label-width="150" :border-bottom="false">
<up-form-item label="手机号" label-width="180rpx" :border-bottom="false">
<view v-if="form.mobile" class="form-value">{{ form.mobile }}</view>
<view v-else class="bind-mobile" @click="navigateTo(form.username)">绑定手机号码</view>
</u-form-item>
</u-form>
<view v-else class="bind-mobile" @click="navigateToBindMobile(form.username)">绑定手机号码</view>
</up-form-item>
</up-form>
<view class="bottom">
<view class="submit" @click="submit">保存</view>
@@ -74,152 +80,135 @@
</view>
</template>
<script>
import { saveUserInfo } from "@/api/members.js";
import { upload } from "@/api/common.js";
import storage from "@/utils/storage.js";
import city from "@/components/m-city/m-city.vue";
<script setup lang="ts">
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'
import MCity from '@/components/m-city/m-city.vue'
function parseBirthdayTimestamp(str) {
if (!str) {
return Date.now();
}
const timestamp = Date.parse(String(str).replace(/-/g, "/"));
return Number.isFinite(timestamp) ? timestamp : Date.now();
function parseBirthdayTimestamp(str?: string) {
if (!str) return Date.now()
const timestamp = Date.parse(String(str).replace(/-/g, '/'))
return Number.isFinite(timestamp) ? timestamp : Date.now()
}
function formatBirthday(timestamp) {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
function formatBirthday(timestamp: number) {
const date = new Date(timestamp)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
export default {
components: { "m-city": city },
data() {
const userInfo = storage.getUserInfo() || {};
return {
lightColor: this.$lightColor,
form: {
nickName: userInfo.nickName || "",
birthday: userInfo.birthday || "",
face: userInfo.face || "/static/missing-face.png",
regionId: [],
region: userInfo.region || [],
sex: userInfo.sex != null ? String(userInfo.sex) : "1",
___path: userInfo.region,
mobile: userInfo.mobile,
username: userInfo.username,
},
birthday: userInfo.birthday || "",
birthdayValue: parseBirthdayTimestamp(userInfo.birthday),
minBirthdayDate: new Date("1950-01-01").getTime(),
maxBirthdayDate: Date.now(),
region: [
{
id: "",
localName: "请选择",
children: [],
},
],
showBirthday: false,
};
},
methods: {
getPickerParentValue(e) {
this.form.region = [];
this.form.regionId = [];
let name = "";
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
e.forEach((item, index) => {
if (item.id) {
this.form.region.push(item.localName);
this.form.regionId.push(item.id);
if (index == e.length - 1) {
name += item.localName;
} else {
name += item.localName + ",";
}
this.form.___path = name;
}
});
},
const userInfo = storage.getUserInfo() || {}
const form = reactive<Record<string, any>>({
nickName: userInfo.nickName || '',
birthday: userInfo.birthday || '',
face: userInfo.face || '/static/missing-face.png',
regionId: [],
region: userInfo.region || [],
sex: userInfo.sex != null ? String(userInfo.sex) : '1',
regionPath: userInfo.region,
mobile: userInfo.mobile,
username: userInfo.username,
})
const birthday = ref(userInfo.birthday || '')
const birthdayValue = ref(parseBirthdayTimestamp(userInfo.birthday))
const minBirthdayDate = new Date('1950-01-01').getTime()
const maxBirthdayDate = Date.now()
const region = ref([{ id: '', localName: '请选择', children: [] }])
const showBirthday = ref(false)
const cityPicker = ref<any>(null)
clickRegion() {
this.$refs.cityPicker.show();
},
function getPickerParentValue(selected: any[]) {
form.region = []
form.regionId = []
let name = ''
selected.forEach((item, index) => {
if (item.id) {
form.region.push(item.localName)
form.regionId.push(item.id)
name += index === selected.length - 1 ? item.localName : `${item.localName},`
form.regionPath = name
}
})
}
submit() {
delete this.form.___path;
const params = JSON.parse(JSON.stringify(this.form));
saveUserInfo(params).then((res) => {
if (res.statusCode == 200) {
storage.setUserInfo(res.data.result);
uni.navigateBack();
}
});
},
function clickRegion() {
cityPicker.value?.show()
}
changeFace() {
uni.chooseImage({
success: (chooseImageRes) => {
const tempFilePaths = chooseImageRes.tempFilePaths;
uni.uploadFile({
url: upload,
filePath: tempFilePaths[0],
name: "file",
header: {
accessToken: storage.getAccessToken(),
},
success: (uploadFileRes) => {
const data = JSON.parse(uploadFileRes.data);
this.form.face = data.result;
},
});
},
});
},
function submit() {
const params = JSON.parse(JSON.stringify(form))
delete params.regionPath
saveUserInfo(params).then((res) => {
if (res.statusCode === 200) {
storage.setUserInfo(res.data.result)
uni.navigateBack()
}
})
}
selectTime(e) {
const timestamp = typeof e?.value === "number" ? e.value : this.birthdayValue;
const formatted = formatBirthday(timestamp);
this.birthdayValue = timestamp;
this.form.birthday = formatted;
this.birthday = formatted;
this.showBirthday = false;
},
function changeFace() {
uni.chooseImage({
success: (chooseImageRes) => {
uni.uploadFile({
url: upload,
filePath: chooseImageRes.tempFilePaths[0],
name: 'file',
header: { accessToken: storage.getAccessToken() },
success: (uploadFileRes) => {
const data = JSON.parse(uploadFileRes.data)
form.face = data.result
},
})
},
})
}
closeBirthdayPicker() {
this.showBirthday = false;
},
function selectTime(e: any) {
const timestamp = typeof e?.value === 'number' ? e.value : birthdayValue.value
const formatted = formatBirthday(timestamp)
birthdayValue.value = timestamp
form.birthday = formatted
birthday.value = formatted
showBirthday.value = false
}
navigateTo(username) {
uni.navigateTo({
url: "/pages/mine/set/securityCenter/bindMobile?username=" + username,
});
},
function closeBirthdayPicker() {
showBirthday.value = false
}
syncUserInfo() {
const userInfo = storage.getUserInfo() || {};
this.form.nickName = userInfo.nickName || "";
this.form.birthday = userInfo.birthday || "";
this.form.face = userInfo.face || "/static/missing-face.png";
this.form.region = userInfo.region || [];
this.form.sex = userInfo.sex != null ? String(userInfo.sex) : "1";
this.form.___path = userInfo.region;
this.form.mobile = userInfo.mobile;
this.form.username = userInfo.username;
this.birthday = userInfo.birthday || "";
this.birthdayValue = parseBirthdayTimestamp(userInfo.birthday);
},
},
function navigateToBindMobile(username: string) {
uni.navigateTo({
url: `/pages/mine/set/securityCenter/bindMobile?username=${username}`,
})
}
onShow() {
this.syncUserInfo();
},
};
function syncUserInfo() {
const latest = storage.getUserInfo() || {}
form.nickName = latest.nickName || ''
form.birthday = latest.birthday || ''
form.face = latest.face || '/static/missing-face.png'
form.region = latest.region || []
form.sex = latest.sex != null ? String(latest.sex) : '1'
form.regionPath = latest.region
form.mobile = latest.mobile
form.username = latest.username
birthday.value = latest.birthday || ''
birthdayValue.value = parseBirthdayTimestamp(latest.birthday)
}
onShow(() => {
syncUserInfo()
})
</script>
<style lang="scss" scoped>

View File

@@ -1,227 +1,211 @@
<template>
<view class="box">
<view class="box-tips">
<h2 class='h2'>
绑定手机号码
</h2>
<h2 class="h2">绑定手机号码</h2>
<view class="verification"></view>
</view>
<view class="form">
<u-form :model="codeForm" ref="validateCodeForm">
<view v-if="!validateFlage">
<u-form-item label-width="120" label="手机号" prop="mobile">
<up-form
:model="codeForm"
ref="validateCodeForm"
label-position="left"
label-width="180rpx"
>
<view v-if="!phoneVerified">
<up-form-item label-width="180rpx" label="手机号" prop="mobile">
<u-input maxlength="11" v-model="codeForm.mobile" placeholder="请输入您的手机号" />
</u-form-item>
<u-form-item class="sendCode" label-width="120" prop="code" label="验证码">
<u-input v-model="codeForm.code" placeholder="请输入验证码" />
<u-code unique-key="page-edit" :seconds="seconds" @end="end" @start="start"
ref="uCode" @change="codeChange"></u-code>
<view @tap="getCode" class="text-tips">{{ tips }}</view>
</u-form-item>
</up-form-item>
<view class="submit" @click="validatePhone">绑定</view>
<myVerification keep-running @send="verification" class="verification" ref="verification"
business="BIND_MOBILE" />
<up-form-item class="sendCode" label-width="180rpx" prop="code" label="验证码">
<u-input v-model="codeForm.code" placeholder="请输入验证码" />
<u-code
unique-key="page-edit"
:seconds="seconds"
@end="onCodeCountdownEnd"
@start="onCodeCountdownStart"
ref="uCodeRef"
@change="onCodeTextChange"
></u-code>
<view @tap="requestSmsCode" class="text-tips">{{ codeTips }}</view>
</up-form-item>
<view class="submit" @click="submitBindMobile">绑定</view>
<myVerification
keep-running
@send="onVerificationPassed"
class="verification"
ref="verificationRef"
business="BIND_MOBILE"
/>
</view>
</u-form>
</up-form>
</view>
</view>
</template>
<script>
import {
sendMobile,
bindMobile
} from "@/api/login";
import { getUserInfo } from "@/api/members.js";
import storage from "@/utils/storage.js";
<script setup lang="ts">
import { ref, reactive, watch, getCurrentInstance } from 'vue'
import { onLoad, onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { sendMobile, bindMobile } from '@/api/login'
import { getUserInfo } from '@/api/members.js'
import storage from '@/utils/storage.js'
import MyVerification from '@/components/verification/verification.vue'
import myVerification from "@/components/verification/verification.vue"; //验证
import uuid from "@/utils/uuid.modified.js";
export default {
components: {
myVerification,
const store = useStore()
const { proxy } = getCurrentInstance()!
const phoneVerified = ref(false)
const verificationPassed = ref(false)
const codeForm = reactive({
mobile: '',
code: '',
username: '',
})
const codeTips = ref('')
const seconds = 69
const validateCodeForm = ref<any>(null)
const uCodeRef = ref<any>(null)
const verificationRef = ref<any>(null)
const codeRules = {
mobile: [
{
validator: (_rule: any, value: string) => proxy.$u.test.mobile(value),
message: '手机号码不正确',
trigger: ['blur'],
},
data() {
return {
uuid,
validateFlage: false, //是否进行了手机号验证
step: 0, //当前验证步骤
flage: false, //是否验证码验证
codeForm: {
mobile: "", //手机号
code: "", //验证码
username: "", //用户名
},
tips: "", //提示
seconds: 69, // 60s等待时间
// 验证码登录校验
codeRules: {
mobile: [{
validator: (rule, value, callback) => {
return this.$u.test.mobile(value);
},
message: "手机号码不正确",
trigger: ["blur"],
}, ],
code: [{
min: 4,
max: 6,
required: true,
message: "请输入验证码",
trigger: ["blur"],
}, ],
},
};
},
onLoad(options) {
this.codeForm.username = options.username;
},
onReady() {
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
this.$refs.validateCodeForm.setRules(this.codeRules);
},
watch: {
flage(val) {
if (val) {
if (this.$refs.uCode.canGetCode) {
uni.showLoading({
title: "正在获取验证码",
});
sendMobile(this.codeForm.mobile, "BIND_MOBILE").then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
// 这里此提示会被this.start()方法中的提示覆盖
if (res.data.success) {
this.$refs.uCode.start();
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
this.flage = false;
this.$refs.verification.getCode();
}
})
} else {
this.$u.toast("请倒计时结束后再发送");
}
}
},
],
code: [
{
min: 4,
max: 6,
required: true,
message: '请输入验证码',
trigger: ['blur'],
},
],
}
methods: {
// 验证码验证
verification(val) {
this.flage = val == this.$store.state.verificationKey ? true : false;
},
onLoad((options) => {
codeForm.username = options?.username || ''
})
// 验证手机号
validatePhone() {
this.$refs.validateCodeForm.validate((valid) => {
if (valid) {
bindMobile(this.codeForm).then((res) => {
if (res.data.success) {
this.validateFlage = !this.validateFlage;
// 获取最新的用户信息并更新缓存
getUserInfo().then(userRes => {
if (userRes.data.success) {
storage.setUserInfo(userRes.data.result);
}
// 显示成功提示
uni.showToast({
title: "绑定成功!",
duration: 2000,
icon: "none",
});
// 返回上一页
setTimeout(() => {
uni.navigateBack({
delta: 1,
});
}, 1000);
});
}
});
}
});
},
onReady(() => {
validateCodeForm.value?.setRules(codeRules)
})
codeChange(text) {
this.tips = text;
},
end() {
this.flage = false;
this.$refs.verification.getCode()
},
/**获取验证码 */
getCode() {
if (this.tips == "重新获取") {
this.$refs.verification.error(); //发送
}
if (!this.$u.test.mobile(this.codeForm.mobile)) {
uni.showToast({
title: "请输入正确手机号",
icon: "none",
});
return false;
}
if (!this.flage) {
this.$refs.verification.error(); //发送
return false;
}
},
start() {
this.$u.toast("验证码已发送");
this.flage = true;
this.$refs.verification.hide();
},
},
};
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
watch(verificationPassed, (val) => {
if (!val) return
if (!uCodeRef.value?.canGetCode) {
proxy.$u.toast('请倒计时结束后再发送')
return
}
.sendCode {
::v-deep .u-form-item--right__content__slot {
display: flex;
uni.showLoading({ title: '正在获取验证码' })
sendMobile(codeForm.mobile, 'BIND_MOBILE').then((res) => {
if (store.state.isShowToast) uni.hideLoading()
if (res.data.success) {
uCodeRef.value?.start()
} else {
uni.showToast({ title: res.data.message, duration: 2000, icon: 'none' })
verificationPassed.value = false
verificationRef.value?.getCode()
}
}
})
})
.h2 {
font-size: 40rpx;
font-weight: bold;
}
function onVerificationPassed(val: string) {
verificationPassed.value = val === store.state.verificationKey
}
page {
background: #fff;
}
function submitBindMobile() {
validateCodeForm.value?.validate((valid: boolean) => {
if (!valid) return
bindMobile(codeForm).then((res) => {
if (res.data.success) {
phoneVerified.value = true
getUserInfo().then((userRes) => {
if (userRes.data.success) {
storage.setUserInfo(userRes.data.result)
}
uni.showToast({ title: '绑定成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
})
}
})
})
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
function onCodeTextChange(text: string) {
codeTips.value = text
}
.submit {
background: $light-color;
}
function onCodeCountdownEnd() {
verificationPassed.value = false
verificationRef.value?.getCode()
}
.box-tips {
margin: 0 72rpx;
function requestSmsCode() {
if (codeTips.value === '重新获取') {
verificationRef.value?.error()
}
if (!proxy.$u.test.mobile(codeForm.mobile)) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
if (!verificationPassed.value) {
verificationRef.value?.error()
}
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
function onCodeCountdownStart() {
proxy.$u.toast('验证码已发送')
verificationPassed.value = true
verificationRef.value?.hide()
}
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
}
.sendCode {
::v-deep .u-form-item__body__right__content__slot {
display: flex;
}
}
.h2 {
font-size: 40rpx;
font-weight: bold;
}
page {
background: #fff;
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
.submit {
background: $light-color;
}
.box-tips {
margin: 0 72rpx;
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style>

View File

@@ -1,160 +1,99 @@
<template>
<view class="box">
<view class="box-tips">
<h2 class='h2'>
{{verificationTitle.title}}
</h2>
<view class="verification">{{verificationTitle.desc}}</view>
<h2 class="h2">{{ verificationTitle.title }}</h2>
<view class="verification">{{ verificationTitle.desc }}</view>
</view>
<view class="form">
<u-form :model="codeForm" ref="validateCodeForm">
<u-form-item label-width="120" label="旧密码">
<u-input type="password" v-model="oldPassword" placeholder="请输入您的旧密码" />
</u-form-item>
<up-form
:model="codeForm"
ref="validateCodeForm"
label-position="left"
label-width="180rpx"
>
<up-form-item label-width="180rpx" label="旧密码">
<u-input type="password" v-model="oldPassword" placeholder="请输入您的旧密码" />
</up-form-item>
<u-form-item label-width="120" label="密码">
<u-input type="password" v-model="password" placeholder="请输入您的密码" />
</u-form-item>
<up-form-item label-width="180rpx" label="密码">
<u-input type="password" v-model="password" placeholder="请输入您的密码" />
</up-form-item>
<u-form-item label-width="120" label="确认密码">
<u-input type="password" v-model="newPassword" placeholder="请再次输入您的密码" />
</u-form-item>
<up-form-item label-width="180rpx" label="确认密码">
<u-input type="password" v-model="confirmPassword" placeholder="请再次输入您的密码" />
</up-form-item>
<view class="submit" @click="updatePassword">修改密码</view>
</u-form>
<view class="submit" @click="updatePassword">修改密码</view>
</up-form>
</view>
</view>
</template>
<script>
import {
resetByMobile,
modifyPass
} from "@/api/login";
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { modifyPass } from '@/api/login'
import { md5 } from '@/utils/md5.js'
import {
md5
} from "@/utils/md5.js"; // md5
import myVerification from "@/components/verification/verification.vue"; //验证
import uuid from "@/utils/uuid.modified.js";
export default {
components: {
myVerification,
},
data() {
return {
uuid,
validateFlage: false, //是否进行了手机号验证
verificationTitle: {
title: "修改密码",
desc: "请验证并输入密码",
},
step: 0, //当前验证步骤
flage: false, //是否验证码验证
const verificationTitle = {
title: '修改密码',
desc: '请验证并输入密码',
}
const codeForm = reactive({ mobile: '', code: '' })
const oldPassword = ref('')
const password = ref('')
const confirmPassword = ref('')
codeForm: {
mobile: "", //手机号
code: "", //验证码
},
newPassword: "", //新密码
password: "", //密码
oldPassword: '', //旧密码
tips: "", //提示
seconds: 69, // 60s等待时间
// 验证码登录校验
codeRules: {
mobile: [{
validator: (rule, value, callback) => {
return this.$u.test.mobile(value);
},
message: "手机号码不正确",
trigger: ["blur"],
}, ],
code: [{
min: 4,
max: 6,
required: true,
message: "请输入验证码",
trigger: ["blur"],
}, ],
},
};
},
onReady() {
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
this.$refs.validateCodeForm.setRules(this.codeRules);
},
methods: {
// 修改密码
updatePassword() {
if(this.password !== this.newPassword){
uni.showToast({
title: "两次输入密码不一致!",
icon: "none",
});
return;
}
modifyPass({
password: md5(this.oldPassword),
newPassword: md5(this.newPassword),
}).then((res) => {
if (res.data.success) {
uni.showToast({
title: "修改成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack({
delta: 1,
});
}, 1000);
}
});
},
},
};
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
function updatePassword() {
if (password.value !== confirmPassword.value) {
uni.showToast({ title: '两次输入密码不一致!', icon: 'none' })
return
}
.sendCode {
::v-deep .u-form-item--right__content__slot {
display: flex;
modifyPass({
password: md5(oldPassword.value),
newPassword: md5(password.value),
}).then((res) => {
if (res.data.success) {
uni.showToast({ title: '修改成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
}
}
})
}
</script>
.h2 {
font-size: 40rpx;
font-weight: bold;
}
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
page {
background: #fff;
}
::v-deep .u-form-item {
margin: 40rpx 0;
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
.h2 {
font-size: 40rpx;
font-weight: bold;
}
.submit {
background: $light-color;
}
page {
background: #fff;
}
.box-tips {
margin: 0 72rpx;
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
.submit {
background: $light-color;
}
.box-tips {
margin: 0 72rpx;
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style>

View File

@@ -1,281 +1,238 @@
<template>
<view class="box">
<view class="box-tips">
<h2 class='h2'>
{{verificationTitle[validateFlage==false ? 0 : 1].title}}
</h2>
<view class="verification">{{verificationTitle[step].desc}}</view>
<h2 class="h2">{{ verificationTitle[phoneVerified ? 1 : 0].title }}</h2>
<view class="verification">{{ verificationTitle[step].desc }}</view>
</view>
<view class="form">
<u-form :model="codeForm" ref="validateCodeForm">
<view v-if="!validateFlage">
<u-form-item label-width="120" label="手机号" prop="mobile">
<up-form
:model="codeForm"
ref="validateCodeForm"
label-position="left"
label-width="180rpx"
>
<view v-if="!phoneVerified">
<up-form-item label-width="180rpx" label="手机号" prop="mobile">
<u-input maxlength="11" v-model="codeForm.mobile" placeholder="请输入您的手机号" />
</u-form-item>
</up-form-item>
<u-form-item class="sendCode" label-width="120" prop="code" label="验证码">
<up-form-item class="sendCode" label-width="180rpx" prop="code" label="验证码">
<u-input v-model="codeForm.code" placeholder="请输入验证码" />
<u-code unique-key="page-edit" :seconds="seconds" @end="end" @start="start"
ref="uCode" @change="codeChange"></u-code>
<view @tap="getCode" class="text-tips">{{ tips }}</view>
</u-form-item>
<u-code
unique-key="page-edit"
:seconds="seconds"
@end="onCodeCountdownEnd"
@start="onCodeCountdownStart"
ref="uCodeRef"
@change="onCodeTextChange"
></u-code>
<view @tap="requestSmsCode" class="text-tips">{{ codeTips }}</view>
</up-form-item>
<view class="submit" @click="validatePhone">验证</view>
<myVerification keep-running @send="verification" class="verification" ref="verification"
business="FIND_USER" />
<view class="submit" @click="verifyMobile">验证</view>
<myVerification
keep-running
@send="onVerificationPassed"
class="verification"
ref="verificationRef"
business="FIND_USER"
/>
</view>
<view v-if="validateFlage">
<u-form-item label-width="120" label="密码">
<view v-if="phoneVerified">
<up-form-item label-width="180rpx" label="密码">
<u-input type="password" v-model="password" placeholder="请输入您的密码" />
</u-form-item>
</up-form-item>
<u-form-item label-width="120" label="确认密码">
<u-input type="password" v-model="newPassword" placeholder="请再次输入您的密码" />
</u-form-item>
<up-form-item label-width="180rpx" label="确认密码">
<u-input type="password" v-model="confirmPassword" placeholder="请再次输入您的密码" />
</up-form-item>
<view class="submit" @click="updatePassword">修改密码</view>
</view>
</u-form>
</up-form>
</view>
</view>
</template>
<script>
import {
sendMobile,
resetByMobile,
resetPassword
} from "@/api/login";
<script setup lang="ts">
import { ref, reactive, watch, getCurrentInstance } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { isLogin } from '@/utils/filters.js'
import { sendMobile, resetByMobile, resetPassword } from '@/api/login'
import { md5 } from '@/utils/md5.js'
import MyVerification from '@/components/verification/verification.vue'
import {
md5
} from "@/utils/md5.js"; // md5
import myVerification from "@/components/verification/verification.vue"; //验证
import uuid from "@/utils/uuid.modified.js";
export default {
components: {
myVerification,
const store = useStore()
const { proxy } = getCurrentInstance()!
const phoneVerified = ref(false)
const verificationPassed = ref(false)
const verificationTitle = [
{ title: '安全验证', desc: '请输入当前手机号进行安全验证' },
{ title: '修改密码', desc: '请输入新密码' },
]
const step = ref(0)
const codeForm = reactive({ mobile: '', code: '' })
const password = ref('')
const confirmPassword = ref('')
const codeTips = ref('')
const seconds = 69
const validateCodeForm = ref<any>(null)
const uCodeRef = ref<any>(null)
const verificationRef = ref<any>(null)
const codeRules = {
mobile: [
{
validator: (_rule: any, value: string) => proxy.$u.test.mobile(value),
message: '手机号码不正确',
trigger: ['blur'],
},
data() {
return {
uuid,
validateFlage: false, //是否进行了手机号验证
verificationTitle: [{
title: "安全验证",
desc: "请输入当前手机号进行安全验证",
},
{
title: "修改密码",
desc: "请输入新密码",
},
],
step: 0, //当前验证步骤
flage: false, //是否验证码验证
codeForm: {
mobile: "", //手机号
code: "", //验证码
},
newPassword: "", //新密码
password: "", //密码
tips: "", //提示
seconds: 69, // 60s等待时间
// 验证码登录校验
codeRules: {
mobile: [{
validator: (rule, value, callback) => {
return this.$u.test.mobile(value);
},
message: "手机号码不正确",
trigger: ["blur"],
}, ],
code: [{
min: 4,
max: 6,
required: true,
message: "请输入验证码",
trigger: ["blur"],
}, ],
},
};
],
code: [
{
min: 4,
max: 6,
required: true,
message: '请输入验证码',
trigger: ['blur'],
},
onReady() {
// 必须要在onReady生命周期因为onLoad生命周期组件可能尚未创建完毕
this.$refs.validateCodeForm.setRules(this.codeRules);
},
watch: {
flage(val) {
if (val) {
],
}
if (this.$refs.uCode.canGetCode) {
uni.showLoading({
title: "正在获取验证码",
});
sendMobile(this.codeForm.mobile, "FIND_USER").then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
// 这里此提示会被this.start()方法中的提示覆盖
if (res.data.success) {
this.$refs.uCode.start();
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
this.flage = false;
this.$refs.verification.getCode();
}
})
} else {
this.$u.toast("请倒计时结束后再发送");
}
}
},
},
onReady(() => {
validateCodeForm.value?.setRules(codeRules)
})
methods: {
// 修改密码
updatePassword() {
if(this.password !== this.newPassword){
uni.showToast({
title: "两次输入密码不一致!",
icon: "none",
});
return;
}
resetPassword({
password: md5(this.password),
}).then((res) => {
if (res.data.success) {
uni.showToast({
title: "修改成功!",
duration: 2000,
icon: "none",
});
setTimeout(() => {
uni.navigateBack({
delta: 1,
});
}, 1000);
}
});
},
// 验证码验证
verification(val) {
this.flage = val == this.$store.state.verificationKey ? true : false;
},
// 验证手机号
validatePhone() {
this.$refs.validateCodeForm.validate((valid) => {
if (valid) {
resetByMobile(this.codeForm).then((res) => {
if (res.data.success) {
this.validateFlage = !this.validateFlage;
// 登录成功
uni.showToast({
title: "验证成功!",
icon: "none",
});
}
});
}
});
},
codeChange(text) {
this.tips = text;
},
end() {
this.flage = false;
this.$refs.verification.getCode()
},
/**判断是否是当前用户的手机号 */
isUserPhone() {
let flage = false;
let user = this.isLogin();
if (user.mobile != this.codeForm.mobile) {
uni.showToast({
title: "请输入当前绑定手机号",
icon: "none",
});
flage = false;
} else {
flage = true;
}
return flage;
},
/**获取验证码 */
getCode() {
if (this.isUserPhone()) {
if (this.tips == "重新获取") {
this.$refs.verification.error(); //发送
}
if (!this.$u.test.mobile(this.codeForm.mobile)) {
uni.showToast({
title: "请输入正确手机号",
icon: "none",
});
return false;
}
if (!this.flage) {
this.$refs.verification.error(); //发送
return false;
}
}
},
start() {
this.$u.toast("验证码已发送");
this.flage = true;
this.$refs.verification.hide();
},
},
};
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
watch(verificationPassed, (val) => {
if (!val) return
if (!uCodeRef.value?.canGetCode) {
proxy.$u.toast('请倒计时结束后再发送')
return
}
.sendCode {
::v-deep .u-form-item--right__content__slot {
display: flex;
uni.showLoading({ title: '正在获取验证码' })
sendMobile(codeForm.mobile, 'FIND_USER').then((res) => {
if (store.state.isShowToast) uni.hideLoading()
if (res.data.success) {
uCodeRef.value?.start()
} else {
uni.showToast({ title: res.data.message, duration: 2000, icon: 'none' })
verificationPassed.value = false
verificationRef.value?.getCode()
}
}
})
})
.h2 {
font-size: 40rpx;
font-weight: bold;
}
function onVerificationPassed(val: string) {
verificationPassed.value = val === store.state.verificationKey
}
page {
background: #fff;
function updatePassword() {
if (password.value !== confirmPassword.value) {
uni.showToast({ title: '两次输入密码不一致!', icon: 'none' })
return
}
resetPassword({ password: md5(password.value) }).then((res) => {
if (res.data.success) {
uni.showToast({ title: '修改成功!', duration: 2000, icon: 'none' })
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1000)
}
})
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
function verifyMobile() {
validateCodeForm.value?.validate((valid: boolean) => {
if (!valid) return
resetByMobile(codeForm).then((res) => {
if (res.data.success) {
phoneVerified.value = true
uni.showToast({ title: '验证成功!', icon: 'none' })
}
})
})
}
.submit {
background: $light-color;
}
function onCodeTextChange(text: string) {
codeTips.value = text
}
.box-tips {
margin: 0 72rpx;
}
function onCodeCountdownEnd() {
verificationPassed.value = false
verificationRef.value?.getCode()
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
function isCurrentUserPhone() {
const user = isLogin()
if (user?.mobile !== codeForm.mobile) {
uni.showToast({ title: '请输入当前绑定手机号', icon: 'none' })
return false
}
return true
}
function requestSmsCode() {
if (!isCurrentUserPhone()) return
if (codeTips.value === '重新获取') {
verificationRef.value?.error()
}
if (!proxy.$u.test.mobile(codeForm.mobile)) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
if (!verificationPassed.value) {
verificationRef.value?.error()
}
}
function onCodeCountdownStart() {
proxy.$u.toast('验证码已发送')
verificationPassed.value = true
verificationRef.value?.hide()
}
</script>
<style lang="scss" scoped>
@import url("@/pages/passport/login.scss");
::v-deep .u-form-item {
margin: 40rpx 0;
}
.sendCode {
::v-deep .u-form-item__body__right__content__slot {
display: flex;
}
}
.h2 {
font-size: 40rpx;
font-weight: bold;
}
page {
background: #fff;
}
.box {
padding: 80rpx 0;
border-radius: 20rpx;
}
.submit {
background: $light-color;
}
.box-tips {
margin: 0 72rpx;
}
.verification {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style>

View File

@@ -4,7 +4,7 @@
<u-cell-group>
<u-cell class="border-top" :isLink="false" title="面容登录">
<template #right-icon>
<u-switch @change="faceSwitchChange" active-color="#1abc9c" size="40" v-model="checked"></u-switch>
<u-switch @change="onFaceSwitchChange" active-color="#1abc9c" size="40" v-model="enabled"></u-switch>
</template>
</u-cell>
</u-cell-group>
@@ -12,74 +12,65 @@
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import { setBiolofy } from "@/api/passport.js";
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import storage from '@/utils/storage.js'
import { setBiolofy } from '@/api/passport.js'
export default {
data() {
return {
lightColor: this.$lightColor,
checked: true,
};
},
methods: {
faceSwitchChange(value) {
if (value === true) {
const res = uni.getSystemInfoSync();
plus.device.getInfo({
success: function (e) {
let params = {
mobile_type: res.model,
secret_key: e.uuid,
};
setBiolofy(params).then((res) => {
if (res.statusCode === 200) {
storage.setFaceLogin(true);
}
});
},
fail: function (e) {
//plus.nativeUI.toast('获取设备信息错误:' + JSON.stringify(e));
console.error("getDeviceInfo failed: " + JSON.stringify(e));
},
});
} else {
storage.setFaceLogin(false);
const enabled = ref(false)
onLoad(() => {
// #ifdef APP-PLUS
uni.checkIsSupportSoterAuthentication({
success(res) {
if (!res.supportMode.find((e) => e === 'facial')) {
plus.nativeUI.toast('此设备不支持面部识别')
uni.navigateBack()
}
uni.checkIsSoterEnrolledInDevice({
checkAuthMode: 'facial',
success(_res) {
if (!_res.isEnrolled) {
plus.nativeUI.toast('此设备未录入面部信息')
uni.navigateBack()
}
},
fail() {
uni.navigateBack()
},
})
},
},
onLoad() {
// #ifdef APP-PLUS
uni.checkIsSupportSoterAuthentication({
success(res) {
if (!res.supportMode.find((e) => e === "facial")) {
plus.nativeUI.toast("此设备不支持面部识别");
uni.navigateBack();
}
uni.checkIsSoterEnrolledInDevice({
checkAuthMode: "facial",
success(_res) {
if (!_res.isEnrolled) {
plus.nativeUI.toast("此设备未录入面部信息");
uni.navigateBack();
}
},
fail(_err) {
// plus.nativeUI.toast(JSON.stringify(_err));
uni.navigateBack();
},
});
fail() {
uni.navigateBack()
},
})
enabled.value = storage.getFaceLogin() || false
// #endif
})
function onFaceSwitchChange(value: boolean) {
if (value) {
const systemInfo = uni.getSystemInfoSync()
plus.device.getInfo({
success(e) {
setBiolofy({
mobile_type: systemInfo.model,
secret_key: e.uuid,
}).then((res) => {
if (res.statusCode === 200) {
storage.setFaceLogin(true)
}
})
},
fail(err) {
// plus.nativeUI.toast(JSON.stringify(err));
uni.navigateBack();
console.error('getDeviceInfo failed: ' + JSON.stringify(err))
},
});
this.checked = storage.getFaceLogin() || false;
// #endif
},
};
})
} else {
storage.setFaceLogin(false)
}
}
</script>
<style lang="scss" scoped>

View File

@@ -4,7 +4,7 @@
<u-cell-group>
<u-cell class="border-top" :isLink="false" title="指纹登录">
<template #right-icon>
<u-switch @change="fingerSwitchChange" :active-color="lightColor" size="40" v-model="checked"></u-switch>
<u-switch @change="onFingerSwitchChange" :active-color="lightColor" size="40" v-model="enabled"></u-switch>
</template>
</u-cell>
</u-cell-group>
@@ -12,60 +12,57 @@
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import { setBiolofy } from "@/api/passport.js";
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import storage from '@/utils/storage.js'
import { setBiolofy } from '@/api/passport.js'
export default {
data() {
return {
lightColor: this.$lightColor,
checked: false,
};
},
methods: {
fingerSwitchChange(value) {
if (value === true) {
const res = uni.getSystemInfoSync();
plus.device.getInfo({
success: function (e) {
let params = {
mobile_type: res.model,
secret_key: e.uuid,
};
setBiolofy(params).then((res) => {
if (res.statusCode === 200) {
storage.setFingerLogin(true);
}
});
},
fail: function (e) {
console.error("getDeviceInfo failed: " + JSON.stringify(e));
},
});
} else {
storage.setFingerLogin(false);
}
},
},
onLoad() {
// #ifdef APP-PLUS
if (!plus.fingerprint.isSupport()) {
plus.nativeUI.toast("此设备不支持指纹识别");
uni.navigateBack();
}
if (!plus.fingerprint.isKeyguardSecure()) {
plus.nativeUI.toast("此设备未设置密码锁屏");
uni.navigateBack();
}
if (!plus.fingerprint.isEnrolledFingerprints()) {
plus.nativeUI.toast("此设备未录入指纹");
uni.navigateBack();
}
this.checked = storage.getFingerLogin() || false;
// #endif
},
};
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const enabled = ref(false)
onLoad(() => {
// #ifdef APP-PLUS
if (!plus.fingerprint.isSupport()) {
plus.nativeUI.toast('此设备不支持指纹识别')
uni.navigateBack()
}
if (!plus.fingerprint.isKeyguardSecure()) {
plus.nativeUI.toast('此设备未设置密码锁屏')
uni.navigateBack()
}
if (!plus.fingerprint.isEnrolledFingerprints()) {
plus.nativeUI.toast('此设备未录入指纹')
uni.navigateBack()
}
enabled.value = storage.getFingerLogin() || false
// #endif
})
function onFingerSwitchChange(value: boolean) {
if (value) {
const systemInfo = uni.getSystemInfoSync()
plus.device.getInfo({
success(e) {
setBiolofy({
mobile_type: systemInfo.model,
secret_key: e.uuid,
}).then((res) => {
if (res.statusCode === 200) {
storage.setFingerLogin(true)
}
})
},
fail(err) {
console.error('getDeviceInfo failed: ' + JSON.stringify(err))
},
})
} else {
storage.setFingerLogin(false)
}
}
</script>
<style scoped lang="scss">

View File

@@ -2,56 +2,44 @@
<view class="securityCenter">
<u-cell-group>
<u-cell title="修改密码" @click="navigateTo('/pages/mine/set/securityCenter/updatePwdTab')"></u-cell>
<u-cell title="注销账户" @click="zhuxiao"></u-cell>
<u-cell title="注销账户" @click="confirmAccountDeletion"></u-cell>
</u-cell-group>
</view>
</template>
<script>
export default {
data() {
return {
mobile: "", //存储手机号
};
},
<script setup lang="ts">
function navigateTo(url: string) {
uni.navigateTo({ url })
}
methods: {
zhuxiao(){
uni.showModal({
title: "警告",
content: "您确定要注销当前账号吗?",
confirmText: "确定注销",
confirmColor: "#FF0000",
cancelText: "取消",
success: (res) => {
if (res.confirm) {
uni.showModal({
title: "谨慎操作",
content: "再次向您确认,您确定要注销当前账号吗?",
confirmText: "坚持注销",
confirmColor: "#FF0000",
cancelText: "取消",
success: (res) => {
if (res.confirm) {
uni.showToast({
title: "您的注销申请已经提交,待管理员审核后。会自动注销当前账号",
duration: 10000,
});
}
},
});
}
},
});
function confirmAccountDeletion() {
uni.showModal({
title: '警告',
content: '您确定要注销当前账号吗?',
confirmText: '确定注销',
confirmColor: '#FF0000',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
uni.showModal({
title: '谨慎操作',
content: '再次向您确认,您确定要注销当前账号吗?',
confirmText: '坚持注销',
confirmColor: '#FF0000',
cancelText: '取消',
success: (confirmRes) => {
if (confirmRes.confirm) {
uni.showToast({
title: '您的注销申请已经提交,待管理员审核后。会自动注销当前账号',
duration: 10000,
})
}
},
})
}
},
navigateTo(url) {
uni.navigateTo({
url: url,
});
},
},
};
})
}
</script>
<style lang="scss" scoped>

View File

@@ -7,22 +7,10 @@
</view>
</template>
<script>
export default {
data() {
return {
mobile: "", //存储手机号
};
},
methods: {
navigateTo(url) {
uni.navigateTo({
url: url,
});
},
},
};
<script setup lang="ts">
function navigateTo(url: string) {
uni.navigateTo({ url })
}
</script>
<style lang="scss" scoped>

View File

@@ -38,112 +38,95 @@
</view>
</template>
<script>
import config from "@/config/config";
export default {
data() {
return {
config,
userImage: config.defaultUserPhoto,
isCertificate: false,
userInfo: {},
fileSizeString: "0B",
};
},
<script setup lang="ts">
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import config from '@/config/config'
import { isLogin, tipsToLogin, quiteLoginOut, logoff } from '@/utils/filters.js'
methods: {
navigateTo(url) {
if (url == "/pages/set/securityCenter/securityCenter") {
url += `?mobile=${this.userInfo.mobile}`;
}
uni.navigateTo({
url: url,
});
},
const userImage = config.defaultUserPhoto
const userInfo = ref<Record<string, any>>({})
const fileSizeString = ref('0B')
getCacheSize() {
let that = this;
plus.cache.calculate(function (size) {
let sizeCache = parseInt(size);
if (sizeCache == 0) {
that.fileSizeString = "0B";
} else if (sizeCache < 1024) {
that.fileSizeString = sizeCache + "B";
} else if (sizeCache < 1048576) {
that.fileSizeString = (sizeCache / 1024).toFixed(2) + "KB";
} else if (sizeCache < 1073741824) {
that.fileSizeString = (sizeCache / 1048576).toFixed(2) + "MB";
} else {
that.fileSizeString = (sizeCache / 1073741824).toFixed(2) + "GB";
}
});
},
onShow(() => {
userInfo.value = isLogin() || {}
// #ifdef APP-PLUS
getCacheSize()
// #endif
})
checkUserInfo() {
if (this.isLogin("auth")) {
this.navigateTo("/pages/mine/set/personMsg");
} else {
this.tipsToLogin();
}
},
function navigateTo(url: string) {
if (url === '/pages/mine/set/securityCenter/securityCenter') {
url += `?mobile=${userInfo.value.mobile || ''}`
}
uni.navigateTo({ url })
}
clearCache() {
let that = this;
let os = plus.os.name;
if (os == "Android") {
let main = plus.android.runtimeMainActivity();
let sdRoot = main.getCacheDir();
let files = plus.android.invoke(sdRoot, "listFiles");
let len = files.length;
for (let i = 0; i < len; i++) {
let filePath = "" + files[i];
plus.io.resolveLocalFileSystemURL(
filePath,
function (entry) {
if (entry.isDirectory) {
entry.removeRecursively(
function () {
uni.showToast({
title: "缓存清理完成",
duration: 2000,
icon: "none",
});
that.getCacheSize();
},
function () {}
);
} else {
entry.remove();
}
},
function () {
uni.showToast({
title: "文件路径读取失败",
duration: 2000,
icon: "none",
});
}
);
}
} else {
plus.cache.clear(function () {
uni.showToast({
title: "缓存清理完成",
duration: 2000,
icon: "none",
});
that.getCacheSize();
});
}
},
},
onShow() {
this.userInfo = this.isLogin();
// #ifdef APP-PLUS
this.getCacheSize();
// #endif
},
};
function getCacheSize() {
// #ifdef APP-PLUS
plus.cache.calculate((size) => {
const sizeCache = parseInt(String(size))
if (sizeCache === 0) {
fileSizeString.value = '0B'
} else if (sizeCache < 1024) {
fileSizeString.value = `${sizeCache}B`
} else if (sizeCache < 1048576) {
fileSizeString.value = `${(sizeCache / 1024).toFixed(2)}KB`
} else if (sizeCache < 1073741824) {
fileSizeString.value = `${(sizeCache / 1048576).toFixed(2)}MB`
} else {
fileSizeString.value = `${(sizeCache / 1073741824).toFixed(2)}GB`
}
})
// #endif
}
function checkUserInfo() {
if (isLogin('auth')) {
navigateTo('/pages/mine/set/personMsg')
} else {
tipsToLogin()
}
}
function clearCache() {
// #ifdef APP-PLUS
const os = plus.os.name
if (os === 'Android') {
const main = plus.android.runtimeMainActivity()
const sdRoot = main.getCacheDir()
const files = plus.android.invoke(sdRoot, 'listFiles')
const len = files.length
for (let i = 0; i < len; i++) {
const filePath = `${files[i]}`
plus.io.resolveLocalFileSystemURL(
filePath,
(entry) => {
if (entry.isDirectory) {
entry.removeRecursively(
() => {
uni.showToast({ title: '缓存清理完成', duration: 2000, icon: 'none' })
getCacheSize()
},
() => {}
)
} else {
entry.remove()
}
},
() => {
uni.showToast({ title: '文件路径读取失败', duration: 2000, icon: 'none' })
}
)
}
} else {
plus.cache.clear(() => {
uni.showToast({ title: '缓存清理完成', duration: 2000, icon: 'none' })
getCacheSize()
})
}
// #endif
}
</script>
<style lang="scss" scoped>

View File

@@ -1,58 +1,45 @@
<template>
<div>
<u-collapse v-if="versionData.length !=0">
<u-collapse-item class="version-item" :title="item.versionName" v-for="(item, index) in versionData" :key="index">
<!-- {{item.body}} -->
{{item.content}}
<u-collapse v-if="versionList.length !== 0">
<u-collapse-item
class="version-item"
:title="item.versionName"
v-for="(item, index) in versionList"
:key="index"
>
{{ item.content }}
</u-collapse-item>
</u-collapse>
<u-empty class="empty" v-else text="暂无版本信息" mode="list"></u-empty>
</div>
</template>
<script>
import { getAppVersionList } from "@/api/message";
export default {
data() {
return {
versionData: [],
appType: "",
params: {
pageNumber: 1,
pageSize: 10,
},
};
},
mounted() {
const platform = uni.getSystemInfoSync().platform;
/**
* 获取是否是安卓
*/
if (platform === "android") {
this.appType = "ANDROID";
} else {
this.IosWhether = true;
this.appType = "IOS";
}
this.getVersionList();
},
methods: {
async getVersionList() {
let res = await getAppVersionList(this.appType, this.params);
if (res.data.success) {
this.versionData = res.data.result.records;
}
},
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getAppVersionList } from '@/api/message'
const versionList = ref<any[]>([])
const appType = ref('')
const params = { pageNumber: 1, pageSize: 10 }
onLoad(() => {
const platform = uni.getSystemInfoSync().platform
appType.value = platform === 'android' ? 'ANDROID' : 'IOS'
fetchVersionList()
})
async function fetchVersionList() {
const res = await getAppVersionList(appType.value, params)
if (res.data.success) {
versionList.value = res.data.result.records
}
}
</script>
<style scoped lang="scss">
.version-item{
padding: 10rpx;
background: #fff;
.version-item {
padding: 10rpx;
background: #fff;
}
</style>
</style>

View File

@@ -3,10 +3,10 @@
<view class="date-card">
<div class="box">
<div class="circle-box">
<div class="cricle" @click="signIn()">
<span v-if="!ifSign" :class="{ active: signFlag || ifSign }">签到</span>
<span v-else :class="{ active: signFlag || ifSign }"
:style="ifSign ? 'transform: rotateY(0deg);' : ''">已签</span>
<div class="cricle" @click="handleSignIn()">
<span v-if="!hasSignedToday" :class="{ active: signAnimating || hasSignedToday }">签到</span>
<span v-else :class="{ active: signAnimating || hasSignedToday }"
:style="hasSignedToday ? 'transform: rotateY(0deg);' : ''">已签</span>
</div>
</div>
<text class="tips">坚持每天连续签到可以获多重奖励哦</text>
@@ -22,19 +22,19 @@
<view class="week">
<text v-for="item in weekArr" :key="item.id">{{ item }}</text>
</view>
<view class="date" v-for="obj in dataObj" :key="obj.id">
<view class="item" v-for="item in obj" :key="item.id" :class="item == '' ? 'hide' : ''"
<view class="date" v-for="(obj, rowIndex) in calendarRows" :key="rowIndex">
<view class="item" v-for="(item, dayIndex) in obj" :key="dayIndex" :class="item == '' ? 'hide' : ''"
:animation="item == currentDay ? animationData : ''">
<view class="just" :class="signArr.indexOf(item) != -1 ? 'active' : ''">
<view class="just" :class="signedDays.indexOf(item) != -1 ? 'active' : ''">
<view class="top">{{ item }} </view>
<view class="bottom">
<u-icon name="error" v-if="item <= currentDay" size="24" color="#999"></u-icon>
</view>
</view>
<view class="back" :class="signArr.indexOf(item) != -1 ? 'active' : ''" :style="
signArr.indexOf(item) != -1 && ifSign
<view class="back" :class="signedDays.indexOf(item) != -1 ? 'active' : ''" :style="
signedDays.indexOf(item) != -1 && hasSignedToday
? 'transform: rotateY(0deg);'
: signArr.indexOf(item) != -1 && item != currentDay
: signedDays.indexOf(item) != -1 && item != currentDay
? 'transform: rotateY(0deg);'
: ''
">
@@ -47,11 +47,11 @@
</view>
</view>
</div>
<view class="mask" :class="{ show: maskFlag, trans: transFlag }" ref="mask">
<view class="mask" :class="{ show: showSuccessMask, trans: maskClosing }" ref="mask">
<view class="mask-header">
<text class="close"></text>
<text>签到成功</text>
<text class="close" @click="close">×</text>
<text class="close" @click="closeSuccessMask">×</text>
</view>
<view class="mask-con">
<u-icon size="120" style="margin: 50rpx 0" :color="aiderLightColor" name="checkmark"></u-icon>
@@ -61,231 +61,147 @@
</view>
</template>
<script>
import { sign, signTime } from "@/api/point.js";
export default {
data() {
return {
aiderLightColor:this.$aiderLightColor,
signFlag: false,
animationData: {},
maskFlag: false, //
transFlag: false, //动画
weekArr: ["日", "一", "二", "三", "四", "五", "六"], //周数组
dateArr: [], //每个月的天数
monthArr: [
//实例化每个月
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
], //今天一个月英文
currentMonth: "", //当月
currentMonthIndex: "", //当月
currentYear: "", //今年
currentDay: "", //今天
currentWeek: "", //获取当月一号是周几
dataObj: [], //一个月有多少天这个获取
signArr: [], //本月签到过的天数 该参数用于请求接口后获取当月都哪天签到了
signAll: [], //所有签到数据
ifSign: false, //今天是否签到
};
},
async onLoad() {
//获取签到数据
var response = await signTime(
new Date().getFullYear() + "" + this.makeUp(new Date().getMonth() + 1)
);
this.signAll = response.data.result;
//获取展示数据
this.getDate();
},
methods: {
/**
* 补0
*/
makeUp(val) {
if (val >= 10) {
return val;
} else {
return "0" + val;
}
},
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { sign, signTime } from '@/api/point.js'
/**
* 点击签到
*/
async signIn() {
await sign().then((response) => {
if (this.ifSign) return;
if (this.signFlag) return;
if (response.data.code != 200) {
uni.showToast({
title: response.data.message,
duration: 2000,
icon: "none",
});
const store = useStore()
const aiderLightColor = computed(() => store.getters.aiderLightColor)
return false;
}
var that = this;
var animation = uni.createAnimation({
duration: 200,
timingFunction: "linear",
});
this.signArr.push(this.currentDay);
this.animation = animation;
animation.rotateY(0).step();
this.animationData = animation.export();
const signAnimating = ref(false)
const animationData = ref({})
const showSuccessMask = ref(false)
const maskClosing = ref(false)
const weekArr = ['日', '一', '二', '三', '四', '五', '六']
const monthLabels = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
const currentMonth = ref('')
const currentMonthIndex = ref(0)
const currentYear = ref(0)
const currentDay = ref(0)
const calendarRows = ref<any[][]>([])
const signedDays = ref<number[]>([])
const signRecords = ref<any[]>([])
const hasSignedToday = ref(false)
setTimeout(
function () {
that.signFlag = true;
this.maskFlag = true;
this.ifSign = !this.ifSign;
animation.rotateY(0).step();
this.animationData = animation.export();
}.bind(this),
200
);
});
},
onLoad(async () => {
const response = await signTime(
`${new Date().getFullYear()}${padZero(new Date().getMonth() + 1)}`
)
signRecords.value = response.data.result
buildCalendar()
})
/**
* 签到成功后关闭弹窗
*/
close() {
var that = this;
this.maskFlag = false;
this.transFlag = true;
setTimeout(() => {
that.transFlag = false;
}, 500);
},
function padZero(val: number) {
return val >= 10 ? String(val) : `0${val}`
}
/**
* 获取今天时间
*
*/
getDate() {
var date = new Date(),
index = date.getMonth(),
curDay = null;
this.currentYear = date.getFullYear();
this.currentMonth = this.monthArr[index];
this.currentMonthIndex = index + 1;
this.currentDay = date.getDate();
if (this.currentDay == this.signArr[this.signArr.length - 1]) {
this.ifSign = true;
}
curDay = this.getWeekByDay(this.currentYear + "-" + (index + 1) + "-1");
this.getMonthDays(index, curDay);
this.curentSignData();
},
async function handleSignIn() {
if (hasSignedToday.value || signAnimating.value) return
/**
* 获取当前已经签到的时间
*/
curentSignData() {
var date = new Date(),
index = date.getMonth(),
curDay = null;
this.signArr = [];
for (var i = 0; i < this.signAll.length; i++) {
var item = this.signAll[i];
item.createTime = item.createTime.split(" ")[0];
var itemVal = item.createTime.split("-");
if (
Number(itemVal[0]) === Number(this.currentYear) &&
Number(itemVal[1]) === Number(this.currentMonthIndex)
) {
this.signArr.push(Number(itemVal[2]));
}
if (
Number(itemVal[0]) === Number(date.getFullYear()) &&
Number(itemVal[1]) === Number(index + 1) &&
Number(itemVal[2]) === Number(date.getDate())
) {
this.ifSign = true;
}
}
},
const response = await sign()
if (response.data.code !== 200) {
uni.showToast({
title: response.data.message,
duration: 2000,
icon: 'none',
})
return
}
/**
* 循环出当前月份的时间
* 例子:
* "","","","","","",1,
* 2 ,3 ,4 ,5 ,6 ,7 ,8,
* ...依次向下排
*/
getMonthDays(index, day) {
//day 当月1号是周几
this.dateArr = [];
this.dataObj = [];
for (var i = 0; i < day; i++) {
this.dateArr.push("");
}
if (
index == 0 ||
index == 2 ||
index == 4 ||
index == 6 ||
index == 7 ||
index == 9 ||
index == 11
) {
for (let i = 1; i < 32; i++) {
this.dateArr.push(i);
}
}
if (index == 3 || index == 5 || index == 8 || index == 10) {
for (let i = 1; i < 31; i++) {
this.dateArr.push(i);
}
}
if (index == 1) {
if (
(this.currentYear % 4 == 0 && this.currentYear % 100 != 0) ||
this.currentYear % 400 == 0
) {
for (let i = 1; i < 30; i++) {
this.dateArr.push(i);
}
} else {
for (let i = 1; i < 29; i++) {
this.dateArr.push(i);
}
}
}
for (var y = 0; y < 10; y++) {
if (this.dateArr.length > 7) {
this.dataObj.push(this.dateArr.splice(0, 7));
} else {
for (let i = 0; i < 7 - this.dateArr.length; i++) {
this.dateArr.push("");
}
}
}
this.dataObj.push(this.dateArr);
},
const animation = uni.createAnimation({ duration: 200, timingFunction: 'linear' })
signedDays.value.push(currentDay.value)
animation.rotateY(0).step()
animationData.value = animation.export()
/**
* 获取当前月份有几周
*/
getWeekByDay(dayValue) {
var day = new Date(Date.parse(dayValue.replace(/-/g, "/"))).getDay(); //将日期值格式化
return day;
},
},
};
setTimeout(() => {
signAnimating.value = true
showSuccessMask.value = true
hasSignedToday.value = true
animation.rotateY(0).step()
animationData.value = animation.export()
}, 200)
}
function closeSuccessMask() {
showSuccessMask.value = false
maskClosing.value = true
setTimeout(() => {
maskClosing.value = false
}, 500)
}
function buildCalendar() {
const date = new Date()
const monthIndex = date.getMonth()
currentYear.value = date.getFullYear()
currentMonth.value = monthLabels[monthIndex]
currentMonthIndex.value = monthIndex + 1
currentDay.value = date.getDate()
syncSignedDays()
const firstWeekDay = getWeekByDay(`${currentYear.value}-${monthIndex + 1}-1`)
buildMonthDays(monthIndex, firstWeekDay)
}
function syncSignedDays() {
const date = new Date()
const monthIndex = date.getMonth()
signedDays.value = []
hasSignedToday.value = false
signRecords.value.forEach((item) => {
const datePart = item.createTime.split(' ')[0]
const parts = datePart.split('-')
if (
Number(parts[0]) === currentYear.value &&
Number(parts[1]) === currentMonthIndex.value
) {
signedDays.value.push(Number(parts[2]))
}
if (
Number(parts[0]) === date.getFullYear() &&
Number(parts[1]) === monthIndex + 1 &&
Number(parts[2]) === date.getDate()
) {
hasSignedToday.value = true
}
})
}
function buildMonthDays(monthIndex: number, firstWeekDay: number) {
const daysInRow: any[] = []
const rows: any[][] = []
for (let i = 0; i < firstWeekDay; i++) {
daysInRow.push('')
}
const isLongMonth = [0, 2, 4, 6, 7, 9, 11].includes(monthIndex)
const isFebruary = monthIndex === 1
let totalDays = isLongMonth ? 31 : 30
if (isFebruary) {
const year = currentYear.value
const isLeap = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
totalDays = isLeap ? 29 : 28
}
for (let day = 1; day <= totalDays; day++) {
daysInRow.push(day)
}
while (daysInRow.length) {
const row = daysInRow.splice(0, 7)
while (row.length < 7) {
row.push('')
}
rows.push(row)
}
calendarRows.value = rows
}
function getWeekByDay(dayValue: string) {
return new Date(Date.parse(dayValue.replace(/-/g, '/'))).getDay()
}
</script>
<style scoped>
page {

View File

@@ -185,6 +185,8 @@ page {
}
.goods-list-wrap {
width: 100%;
box-sizing: border-box;
padding-bottom: 40rpx;
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,8 @@
:scrollable="false"
v-model:current="current"
@change="change"
:lineColor="$lightColor"
:activeStyle="{ color: $lightColor }"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
></u-tabs>
</view>
<div class="u-tabs-search">
@@ -194,279 +194,199 @@
</view>
</template>
<script>
import uniLoadMore from "@/components/uni-load-more/uni-load-more.vue";
import { getAfterSaleList, cancelAfterSale } from "@/api/after-sale.js";
import { getOrderList } from "@/api/order.js";
import storage from "@/utils/storage";
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { onLoad, onPullDownRefresh } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice, serviceStatusList, parseGoodsImageUrl } from '@/utils/filters.js'
import { getAfterSaleList, cancelAfterSale } from '@/api/after-sale.js'
import { getOrderList } from '@/api/order.js'
import storage from '@/utils/storage'
export default {
components: {
uniLoadMore,
},
data() {
return {
list: [
//tab表头
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const list = [
{ name: '售后申请' },
{ name: '申请中' },
{ name: '申请记录' },
]
const current = ref(0)
const tipsShow = ref(false)
const cancelShow = ref(false)
const selectedOrder = ref<any>(null)
const orderList = ref<any[]>([])
const params = reactive<Record<string, any>>({
pageNumber: 1,
pageSize: 10,
sort: 'createTime',
flowPrice: 0,
order: 'desc',
})
const logParams = reactive<Record<string, any>>({
pageNumber: 1,
pageSize: 10,
})
const status = ref('loadmore')
const keywords = ref('')
onLoad((options) => {
orderList.value = []
params.pageNumber = 1
if (options?.orderSn) params.keywords = options.orderSn
searchOrderList(current.value)
})
onPullDownRefresh(() => {
change(current.value)
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function getGoodsName(sku: any) {
return sku.goodsName || sku.name || ''
}
function getGoodsImage(goods: any, order: any, index: number) {
let image = goods.image || goods.goodsImage || goods.thumbnail
if (!image && order.groupImages) {
const images = String(order.groupImages).split(',')
image = images[index] || images[0]
}
return parseGoodsImageUrl(image)
}
function submitSearchOrderList(tabIndex: number) {
params.pageNumber = 1
logParams.pageNumber = 1
orderList.value = []
searchOrderList(tabIndex)
}
function clear(tabIndex: number) {
params.pageNumber = 1
logParams.pageNumber = 1
params.keywords = ''
orderList.value = []
searchOrderList(tabIndex)
}
function change(e: number | { index: number }) {
const index = typeof e === 'object' && e != null ? e.index : e
current.value = index
Object.assign(params, { pageNumber: 1, pageSize: 10 })
orderList.value = []
searchOrderList(index)
uni.stopPullDownRefresh()
}
function searchOrderList(index: number) {
if (index == 0) {
if (keywords.value) params.keywords = keywords.value
fetchOrderList()
} else {
Object.assign(logParams, {
pageNumber: 1,
pageSize: 10,
sort: 'createTime',
order: 'desc',
})
if (index === 1) {
logParams.serviceStatus = 'APPLY'
}
if (keywords.value) logParams.keywords = keywords.value
orderList.value = []
fetchAfterSaleLogList()
}
}
function fetchOrderList() {
uni.showLoading({ title: '加载中', mask: true })
getOrderList(params).then((res) => {
hideLoadingIfNeeded()
const records = res.data.result.records
if (records.length > 0) {
orderList.value = orderList.value.concat(records)
params.pageNumber += 1
}
status.value = records.length < 10 ? 'nomore' : 'loading'
})
}
function close(order: any, _sku: any) {
selectedOrder.value = order
cancelShow.value = true
}
async function closeService() {
uni.showLoading({ title: '加载中' })
const res = await cancelAfterSale(selectedOrder.value.sn)
if (res.data.success) {
uni.showToast({ title: '取消成功!', duration: 2000, icon: 'none' })
}
orderList.value = []
searchOrderList(current.value)
hideLoadingIfNeeded()
}
function afterDetails(order: any, _sku?: any) {
uni.navigateTo({ url: './applyDetail?sn=' + order.sn })
}
function fetchAfterSaleLogList() {
getAfterSaleList(logParams).then((res) => {
const afterSaleLogList = res.data.result.records
afterSaleLogList.forEach((item: any) => {
item.orderItems = [
{
name: "售后申请",
image: item.goodsImage,
skuId: item.skuId,
name: item.goodsName,
num: item.num,
price: item.flowPrice,
},
{
name: "申请中",
},
{
name: "申请记录",
},
],
current: 0, //当前表头索引
tipsShow: false, //提示开关
cancelShow: false, //取消显示开关
selectedOrder: "", //选中的order
orderList: [], //订单集合
params: {
pageNumber: 1,
pageSize: 10,
sort: "createTime",
flowPrice: 0,
order: "desc",
},
]
})
orderList.value = orderList.value.concat(afterSaleLogList)
status.value = afterSaleLogList.length < 10 ? 'nomore' : 'loading'
})
}
logParams: {
pageNumber: 1,
pageSize: 10,
},
status: "loadmore",
keywords: "", // 搜索订单sn
};
},
onLoad(options) {
this.orderList = [];
this.params.pageNumber = 1;
if (options.orderSn) this.params.keywords = options.orderSn;
this.searchOrderList(this.current);
},
onPullDownRefresh() {
this.change(this.current);
},
methods: {
getGoodsName(sku) {
return sku.goodsName || sku.name || "";
},
getGoodsImage(goods, order, index) {
let image = goods.image || goods.goodsImage || goods.thumbnail;
if (!image && order.groupImages) {
const images = String(order.groupImages).split(",");
image = images[index] || images[0];
}
return this.parseGoodsImageUrl(image);
},
/**
* 点击搜索执行搜索
*/
submitSearchOrderList(current) {
this.params.pageNumber = 1;
this.logParams.pageNumber = 1;
this.orderList = [];
this.searchOrderList(current);
},
// 清空
clear(current){
this.params.pageNumber = 1;
this.logParams.pageNumber = 1;
this.params.keywords = ''
this.orderList = [];
this.searchOrderList(current);
},
/**
* 切换tab页时初始化数据
*/
change(e) {
const index = typeof e === 'object' && e != null ? e.index : e;
this.current = index;
this.params = {
pageNumber: 1,
pageSize: 10,
};
this.orderList = [];
//如果是2 则读取售后申请记录列表
this.searchOrderList(index);
uni.stopPullDownRefresh();
},
function applyService(sn: string, order: any, sku: any) {
storage.setAfterSaleData({ ...order, ...sku })
uni.navigateTo({ url: `/pages/order/afterSales/afterSalesSelect?sn=${sn}` })
}
/**
* 搜索初始化
* 根据当前tab传值的索引进行更改
*/
searchOrderList(index) {
if (index == 0) {
this.keywords ? (this.params.keywords = this.keywords) : "";
this.getOrderList();
} else {
this.logParams = {
pageNumber: 1,
pageSize: 10,
sort: "createTime",
order: "desc",
};
if (index === 1) {
this.logParams.serviceStatus = "APPLY";
}
this.keywords ? (this.logParams.keywords = this.keywords) : "";
this.orderList = [];
this.getAfterSaleLogList();
}
},
function onExpress(order: any, sku: any) {
sku.storeName = order.storeName
storage.setAfterSaleData({ ...order, ...sku })
uni.navigateTo({ url: `./afterSalesDetailExpress?serviceSn=${order.sn}` })
}
/**
* 获取订单列表
*/
getOrderList() {
uni.showLoading({
title: "加载中",
mask: true,
});
getOrderList(this.params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
const orderList = res.data.result.records;
if (orderList.length > 0) {
this.orderList = this.orderList.concat(orderList);
this.params.pageNumber += 1;
}
if (orderList.length < 10) {
this.status = "nomore";
} else {
this.status = "loading";
}
});
},
function onDetail(goods: any, sku: any) {
if (current.value == 0) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId || sku.goodsId}`,
})
} else {
uni.navigateTo({
url: `/pages/product/goods?id=${goods.skuId}&goodsId=${goods.goodsId || goods.goodsId}`,
})
}
}
close(order, sku) {
console.log(order, sku);
this.selectedOrder = order;
this.cancelShow = true;
},
async closeService() {
uni.showLoading({
title: "加载中",
});
console.log(this.selectedOrder);
let res = await cancelAfterSale(this.selectedOrder.sn);
if (res.data.success) {
uni.showToast({
title: "取消成功!",
duration: 2000,
icon: "none",
});
}
this.orderList = [];
this.searchOrderList(this.current);
if (this.$store.state.isShowToast){ uni.hideLoading() };
},
/**
* 售后详情
*/
afterDetails(order) {
uni.navigateTo({
url: "./applyDetail?sn=" + order.sn,
});
},
/**
* 申请记录列表
*/
getAfterSaleLogList() {
getAfterSaleList(this.logParams).then((res) => {
let afterSaleLogList = res.data.result.records;
afterSaleLogList.forEach((item) => {
item.orderItems = [
{
image: item.goodsImage,
skuId: item.skuId,
name: item.goodsName,
num: item.num,
price: item.flowPrice,
},
];
});
this.orderList = this.orderList.concat(afterSaleLogList);
if (afterSaleLogList.length < 10) {
this.status = "nomore";
} else {
this.status = "loading";
}
});
},
/**
* 申请售后
*/
applyService(sn, order, sku) {
let data = {
...order,
...sku,
};
storage.setAfterSaleData(data);
uni.navigateTo({
url: `/pages/order/afterSales/afterSalesSelect?sn=${sn}`,
});
},
/**
* 提交物流信息
*/
onExpress(order, sku) {
sku.storeName = order.storeName;
let data = {
...order,
...sku,
};
storage.setAfterSaleData(data);
uni.navigateTo({
url: `./afterSalesDetailExpress?serviceSn=${order.sn}`,
});
},
/**
* 查看详情
*/
onDetail(goods, sku) {
// 售后申请
if (this.current == 0) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${
sku.goodsId || sku.goodsId
}`,
});
} else {
uni.navigateTo({
url: `/pages/product/goods?id=${goods.skuId}&goodsId=${
goods.goodsId || goods.goodsId
}`,
});
}
},
/**
* 底部加载数据
*/
renderDate() {
if (this.current === 0) {
this.params.pageNumber += 1;
this.getOrderList();
} else {
this.logParams.pageNumber += 1;
this.getAfterSaleLogList();
}
},
},
};
function renderDate() {
if (current.value === 0) {
params.pageNumber += 1
fetchOrderList()
} else {
logParams.pageNumber += 1
fetchAfterSaleLogList()
}
}
</script>
<style lang="scss">

View File

@@ -1,7 +1,12 @@
<template>
<view class="page-wrap">
<scroll-view scroll-y class="page-scroll">
<u-form :model="form" ref="uForm">
<up-form
:model="form"
ref="uForm"
label-position="left"
label-width="180rpx"
>
<view class="after-sales-goods-detail-view">
<view class="header">
<view>
@@ -58,7 +63,7 @@
<u-icon name="arrow-right" color="#ccc" size="16"></u-icon>
</view>
</view>
<u-form-item label="申请说明" label-width="150" :border-bottom="false" class="desc-item">
<up-form-item label="申请说明" label-width="180rpx" :border-bottom="false" class="desc-item">
<u-input
v-model="form.problemDesc"
type="textarea"
@@ -67,54 +72,54 @@
height="120"
placeholder="请描述申请售后的说明"
/>
</u-form-item>
</up-form-item>
</view>
<!-- 退款方式 / 银行信息 -->
<view class="opt-view form-block">
<u-form-item label="退款方式" label-width="150" :border-bottom="true">
<up-form-item label="退款方式" label-width="180rpx" :border-bottom="true">
<view class="form-value">{{
applyInfo.refundWay == 'ORIGINAL' ? '原路退回' : '账号退款'
}}</view>
</u-form-item>
</up-form-item>
<template v-if="
applyInfo.accountType === 'BANK_TRANSFER' &&
applyInfo.applyRefundPrice != 0
">
<u-form-item label="银行开户行" label-width="150" :border-bottom="true">
<up-form-item label="银行开户行" label-width="180rpx" :border-bottom="true">
<u-input
v-model="form.bankDepositName"
border="none"
input-align="right"
placeholder="请输入银行开户行"
/>
</u-form-item>
<u-form-item label="银行开户名" label-width="150" :border-bottom="true">
</up-form-item>
<up-form-item label="银行开户名" label-width="180rpx" :border-bottom="true">
<u-input
v-model="form.bankAccountName"
border="none"
input-align="right"
placeholder="请输入银行开户名"
/>
</u-form-item>
<u-form-item label="银行账号" label-width="150" :border-bottom="true" class="bank-account-item">
</up-form-item>
<up-form-item label="银行账号" label-width="180rpx" :border-bottom="true" class="bank-account-item">
<u-input
v-model="form.bankAccountNumber"
border="none"
input-align="right"
placeholder="请输入银行账号"
/>
</u-form-item>
</up-form-item>
</template>
<u-form-item
<up-form-item
v-if="form.serviceType !== 'RETURN_MONEY'"
label="返回方式"
label-width="150"
label-width="180rpx"
:border-bottom="false"
>
<view class="form-value">快递至第三方卖家</view>
</u-form-item>
</up-form-item>
</view>
<!-- 上传凭证 -->
@@ -133,7 +138,7 @@
<view class="opt-tip">提交服务单后,售后专员可能与您电话沟通,请保持手机畅通</view>
</view>
</u-form>
</up-form>
</scroll-view>
<view class="submit-view">
@@ -142,7 +147,7 @@
ripple
shape="circle"
v-if="applyInfo.refundWay"
:custom-style="{ backgroundColor: $lightColor, width: '100%' }"
:custom-style="{ backgroundColor: lightColor, width: '100%' }"
@click="onSubmit"
>提交申请</u-button>
</view>
@@ -158,246 +163,214 @@
</view>
</template>
<script>
import {
getAfterSaleReason,
applyReturn,
getAfterSaleInfo,
} from "@/api/after-sale";
<script setup lang="ts">
import { ref, reactive, computed, getCurrentInstance } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice, parseGoodsImageUrl } from '@/utils/filters.js'
import { getAfterSaleReason, applyReturn, getAfterSaleInfo } from '@/api/after-sale'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
import storage from '@/utils/storage.js'
import city from "@/components/m-city/m-city";
import { handleUploadAfterRead } from "@/utils/uploadHelper.js";
import storage from "@/utils/storage.js";
export default {
component: {
city,
},
data() {
return {
storage,
list: [{ id: "", localName: "请选择", children: [] }],
fileList: [],
sn: "",
sku: {},
typeValue: 0,
value: "",
type: "textarea",
border: true,
//退款原因 弹出框
reasonSelectShow: false,
reasonList: [],
applyInfo: {},
form: {
orderItemSn: "", // 订单sn
skuId: "",
reason: "", //退款原因
problemDesc: "", //退款说明
images: [], //图片凭证
num: 1, //退货数量
goodsId: "", //商品id
accountType: "",
applyRefundPrice: "",
refundWay: "",
serviceType: "", //申请类型
},
};
},
const store = useStore()
const { proxy } = getCurrentInstance()!
const $u = proxy!.$u
/**
* 判断当前内容并生成数据
*/
onLoad(options) {
let navTitle = "申请售后";
this.form.serviceType = "RETURN_GOODS";
if (options.value == 1) {
navTitle = "申请退货";
this.form.serviceType = "RETURN_GOODS";
const lightColor = computed(() => store.getters.lightColor)
const fileList = ref<any[]>([])
const sn = ref('')
const sku = ref<any>({})
const reasonSelectShow = ref(false)
const reasonList = ref<any[]>([])
const applyInfo = ref<any>({})
const uToast = ref<any>(null)
const form = reactive({
orderItemSn: '',
skuId: '',
reason: '',
problemDesc: '',
images: [] as string[],
num: 1,
goodsId: '',
accountType: '',
applyRefundPrice: '',
refundWay: '',
serviceType: 'RETURN_GOODS',
bankDepositName: '',
bankAccountName: '',
bankAccountNumber: '',
})
onLoad((options) => {
let navTitle = '申请售后'
form.serviceType = 'RETURN_GOODS'
if (options?.value == '1') {
navTitle = '申请退货'
form.serviceType = 'RETURN_GOODS'
}
if (options?.value == '2') {
navTitle = '申请换货'
form.serviceType = 'EXCHANGE_GOODS'
}
if (options?.value == '3') {
navTitle = '申请退款'
form.serviceType = 'RETURN_MONEY'
}
uni.setNavigationBarTitle({ title: navTitle })
sn.value = options?.sn || ''
sku.value = storage.getAfterSaleData()
form.orderItemSn = options?.sn || ''
form.skuId = sku.value.skuId
form.num = sku.value.num
form.goodsId = sku.value.goodsId
fetchReasonActions(form.serviceType)
init(options?.sn || '')
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function getGoodsName(item: any) {
return item.goodsName || item.name || ''
}
function getGoodsImage(item: any) {
const image = item.image || item.goodsImage || item.thumbnail
return parseGoodsImageUrl(image)
}
function gotoGoodsDetail(goodsId: string) {
if (!goodsId) return
uni.navigateTo({
url: `/pages/product/goods?id=${form.skuId}&goodsId=${goodsId}`,
})
}
async function fetchReasonActions(serviceType: string) {
uni.showLoading({ title: '加载中' })
await getAfterSaleReason(serviceType).then((res) => {
if (res.data.success) {
reasonList.value = res.data.result.map((item: any) => ({
value: item.id,
label: item.reason,
}))
}
if (options.value == 2) {
navTitle = "申请换货";
this.form.serviceType = "EXCHANGE_GOODS";
}
if (options.value == 3) {
navTitle = "申请退款";
this.form.serviceType = "RETURN_MONEY";
}
this.typeValue = options.value;
uni.setNavigationBarTitle({
title: navTitle, //此处写页面的title
});
this.sn = options.sn;
this.sku = storage.getAfterSaleData();;
})
hideLoadingIfNeeded()
}
this.form.orderItemSn = options.sn;
this.form.skuId = this.sku.skuId;
this.form.num = this.sku.num;
this.form.goodsId = this.sku.goodsId;
this.getReasonActions(this.form.serviceType);
this.init(options.sn);
},
methods: {
getGoodsName(item) {
return item.goodsName || item.name || "";
},
getGoodsImage(item) {
const image = item.image || item.goodsImage || item.thumbnail;
return this.parseGoodsImageUrl(image);
},
gotoGoodsDetail(goodsId) {
if (!goodsId) return;
uni.navigateTo({
url: `/pages/product/goods?id=${this.form.skuId}&goodsId=${goodsId}`,
});
},
/** 获取申请原因下拉框数据 */
async getReasonActions(serviceType) {
uni.showLoading({
title: "加载中",
});
await getAfterSaleReason(serviceType).then((res) => {
if (res.data.success) {
let action = [];
res.data.result.forEach((item) => {
action.push({
value: item.id,
label: item.reason,
});
});
this.reasonList = action;
}
});
if (this.$store.state.isShowToast){ uni.hideLoading() };
},
//打开地区选择器
showCitySelect() {
this.$refs.cityPicker.show();
},
// 初始化数据
init(sn) {
getAfterSaleInfo(sn).then((response) => {
if (response.data.code == 400) {
uni.showToast({
title: response.data.message,
duration: 2000,
icon: "none",
});
} else {
this.applyInfo = response.data.result;
this.form.accountType = response.data.result.accountType;
}
});
},
openReasonPicker() {
if (!this.reasonList.length) {
uni.showToast({
title: "暂无可选原因",
icon: "none",
});
return;
}
this.reasonSelectShow = true;
},
//退款原因
reasonSelectConfirm(val) {
const selected = val?.value?.[0] || val?.[0];
if (selected) {
this.form.reason = selected.label || selected.text || "";
}
},
//修改申请数量
valChange(e) {
this.form.num = e.value;
},
onUploadAfterRead(event) {
handleUploadAfterRead(event, this.fileList, (urls) => {
this.form.images = urls;
});
},
showToast(message, type = "error") {
const text = message || (type === "success" ? "操作成功" : "操作失败");
if (this.$refs.uToast) {
this.$refs.uToast.show({ message: text, type });
return;
}
function init(orderItemSn: string) {
getAfterSaleInfo(orderItemSn).then((response) => {
if (response.data.code == 400) {
uni.showToast({
title: text,
icon: type === "success" ? "success" : "none",
});
},
//提交申请
onSubmit() {
//提交申请前检测参数
if (!this.handleCheckParams()) {
return;
}
title: response.data.message,
duration: 2000,
icon: 'none',
})
} else {
applyInfo.value = response.data.result
form.accountType = response.data.result.accountType
}
})
}
uni.showLoading({
title: "加载中",
});
this.form.accountType = this.applyInfo.accountType;
this.form.refundWay = this.applyInfo.refundWay;
this.form.applyRefundPrice = this.applyInfo.applyRefundPrice;
function openReasonPicker() {
if (!reasonList.value.length) {
uni.showToast({ title: '暂无可选原因', icon: 'none' })
return
}
reasonSelectShow.value = true
}
applyReturn(this.sn, this.form).then((resp) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
if (resp.data.success) {
this.showToast("提交成功", "success");
uni.redirectTo({
url: "/pages/order/afterSales/applySuccess",
});
} else {
this.showToast(resp.data.message || "提交失败", "error");
}
});
},
//检测提交参数
handleCheckParams() {
if (this.$u.test.isEmpty(this.form.reason)) {
this.showToast("请选择退款原因");
return false;
}
if (this.$u.test.isEmpty(this.form.problemDesc)) {
this.showToast("请输入退款说明");
return false;
}
function reasonSelectConfirm(val: any) {
const selected = val?.value?.[0] || val?.[0]
if (selected) {
form.reason = selected.label || selected.text || ''
}
}
if (
this.applyInfo.accountType === "BANK_TRANSFER" &&
this.applyInfo.applyRefundPrice != 0
) {
if (this.$u.test.isEmpty(this.form.bankDepositName)) {
this.showToast("请输入银行开户行");
return false;
}
if (this.$u.test.isEmpty(this.form.bankAccountName)) {
this.showToast("请输入银行开户名");
return false;
}
if (this.$u.test.isEmpty(this.form.bankAccountNumber)) {
this.showToast("请输入银行账号");
return false;
}
if (this.$u.test.chinese(this.form.bankAccountName) === false) {
this.showToast("银行开户名需为中文");
return false;
}
if (this.$u.test.chinese(this.form.bankDepositName) === false) {
this.showToast("银行开户行需为中文");
return false;
}
}
function valChange(e: { value: number }) {
form.num = e.value
}
return true;
},
},
};
function onUploadAfterRead(event: any) {
handleUploadAfterRead(event, fileList.value, (urls) => {
form.images = urls
})
}
function showToast(message: string, type = 'error') {
const text = message || (type === 'success' ? '操作成功' : '操作失败')
if (uToast.value) {
uToast.value.show({ message: text, type })
return
}
uni.showToast({
title: text,
icon: type === 'success' ? 'success' : 'none',
})
}
function onSubmit() {
if (!validateFormParams()) return
uni.showLoading({ title: '加载中' })
form.accountType = applyInfo.value.accountType
form.refundWay = applyInfo.value.refundWay
form.applyRefundPrice = applyInfo.value.applyRefundPrice
applyReturn(sn.value, form).then((resp) => {
hideLoadingIfNeeded()
if (resp.data.success) {
showToast('提交成功', 'success')
uni.redirectTo({ url: '/pages/order/afterSales/applySuccess' })
} else {
showToast(resp.data.message || '提交失败', 'error')
}
})
}
function validateFormParams() {
if ($u.test.isEmpty(form.reason)) {
showToast('请选择退款原因')
return false
}
if ($u.test.isEmpty(form.problemDesc)) {
showToast('请输入退款说明')
return false
}
if (
applyInfo.value.accountType === 'BANK_TRANSFER' &&
applyInfo.value.applyRefundPrice != 0
) {
if ($u.test.isEmpty(form.bankDepositName)) {
showToast('请输入银行开户行')
return false
}
if ($u.test.isEmpty(form.bankAccountName)) {
showToast('请输入银行开户名')
return false
}
if ($u.test.isEmpty(form.bankAccountNumber)) {
showToast('请输入银行账号')
return false
}
if ($u.test.chinese(form.bankAccountName) === false) {
showToast('银行开户名需为中文')
return false
}
if ($u.test.chinese(form.bankDepositName) === false) {
showToast('银行开户行需为中文')
return false
}
}
return true
}
</script>
<style lang="scss" scoped>

View File

@@ -1,6 +1,11 @@
<template>
<view class="mp-iphonex-bottom content">
<u-form :model="form" ref="uForm">
<up-form
:model="form"
ref="uForm"
label-position="left"
label-width="180rpx"
>
<view class="after-sales-goods-detail-view">
<view class="header">
<view>
@@ -29,32 +34,32 @@
<!-- 上传凭证 -->
<view class="opt-view">
<view class="img-title" style="font-size: 30rpx">填写物流信息</view>
<u-form-item label="返回方式" :label-width="150">
<up-form-item label="返回方式" label-width="180rpx">
<div style="width: 100%; text-align: right;">快递至第三方卖家</div>
</u-form-item>
<u-form-item label="快递公司" :label-width="150">
</up-form-item>
<up-form-item label="快递公司" label-width="180rpx">
<div style="width: 100%; text-align: right;" @click="companySelectShow = true">
{{ form.courierCompany || '请选择快递公司' }}
</div>
</u-form-item>
<u-form-item label="快递单号" :label-width="150">
</up-form-item>
<up-form-item label="快递单号" label-width="180rpx">
<u-input input-align="right" v-model="form.logisticsNo" placeholder="请输入快递单号"/>
</u-form-item>
<u-form-item label="发货时间" :label-width="150">
</up-form-item>
<up-form-item label="发货时间" label-width="180rpx">
<div style="width: 100%; text-align: right;" @click="timeshow = true">{{
form.mDeliverTime || '请选择发货时间'
}}
</div>
</u-form-item>
</up-form-item>
</view>
</scroll-view>
<view class="submit-view">
<u-button ripple :customStyle="{'background':$lightColor,'color':'#fff' }" shape="circle" @click="onSubmit">
<u-button ripple :customStyle="{ background: lightColor, color: '#fff' }" shape="circle" @click="onSubmit">
提交申请
</u-button>
</view>
</u-form>
</up-form>
<u-select mode="single-column" :list="companyList" v-model:show="companySelectShow"
@confirm="companySelectConfirm"></u-select>
<u-calendar v-model:show="timeshow" :mode="'date'" @change="onTimeChange"></u-calendar>
@@ -62,124 +67,98 @@
</view>
</template>
<script>
import {getLogistics} from "@/api/address.js";
import {fillShipInfo} from "@/api/after-sale.js";
import storage from "@/utils/storage";
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { getLogistics } from '@/api/address.js'
import { fillShipInfo } from '@/api/after-sale.js'
import storage from '@/utils/storage'
export default {
data() {
return {
//快递公司 弹出框
companySelectShow: false,
companyList: [], //快递公司集合
timeshow: false, //发货时间
form: {
courierCompany: "", //快递公司
logisticsId: "", //快递公司ID
logisticsNo: "", //快递单号
mDeliverTime: "", //发货时间
},
serviceDetail: {}, //服务详情
sku: {}, //sku信息
};
},
onLoad(options) {
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
this.sku = storage.getAfterSaleData();
let navTitle = "服务单详情";
uni.setNavigationBarTitle({
title: navTitle, //此处写页面的title
});
this.serviceDetail.sn = options.serviceSn;
this.Logistics();
},
methods: {
/**
* 确认快递公司
*/
companySelectConfirm(e) {
this.form.logisticsId = e[0].value;
this.form.courierCompany = e[0].label;
},
const companySelectShow = ref(false)
const companyList = ref<any[]>([])
const timeshow = ref(false)
const form = reactive({
courierCompany: '',
logisticsId: '',
logisticsNo: '',
mDeliverTime: '',
})
const serviceDetail = reactive<{ sn: string }>({ sn: '' })
const sku = ref<any>({})
const uToast = ref<any>(null)
/**
* 获取快递公司
*/
Logistics() {
getLogistics().then((res) => {
if (res.data.success) {
res.data.result.forEach((item, index) => {
this.companyList[index] = {
value: item.id,
label: item.name,
};
});
}
});
},
onLoad((options) => {
sku.value = storage.getAfterSaleData()
uni.setNavigationBarTitle({ title: '服务单详情' })
serviceDetail.sn = options?.serviceSn || ''
fetchLogisticsList()
})
/**
* 更改时间
*/
onTimeChange(e) {
this.form.mDeliverTime = e.result;
},
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
/**
* 点击提交
*/
onSubmit() {
delete this.form.courierCompany;
function companySelectConfirm(e: any[]) {
form.logisticsId = e[0].value
form.courierCompany = e[0].label
}
if (this.form.logisticsId == "") {
this.$refs.uToast.show({
title: "请选择快递公司",
type: "error",
});
return;
}
if (this.form.logisticsNo == "") {
this.$refs.uToast.show({
title: "请填写快递单号",
type: "error",
});
return;
}
if (this.form.mDeliverTime == "") {
this.$refs.uToast.show({
title: "请选择发货时间",
type: "error",
});
return;
}
function fetchLogisticsList() {
getLogistics().then((res) => {
if (res.data.success) {
companyList.value = res.data.result.map((item: any) => ({
value: item.id,
label: item.name,
}))
}
})
}
uni.showLoading({
title: "加载中",
mask: true,
});
fillShipInfo(this.serviceDetail.sn, this.form).then((res) => {
if (this.$store.state.isShowToast) {
uni.hideLoading()
}
;
if (res.statusCode === 200) {
this.$refs.uToast.show({
title: "提交成功",
type: "success",
back: true,
url: "/pages/order/afterSales/afterSales",
});
}
});
},
gotoGoodsDetail(sku) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId}`,
});
},
},
};
function onTimeChange(e: { result: string }) {
form.mDeliverTime = e.result
}
function onSubmit() {
const submitForm = { ...form }
delete submitForm.courierCompany
if (form.logisticsId == '') {
uToast.value?.show({ title: '请选择快递公司', type: 'error' })
return
}
if (form.logisticsNo == '') {
uToast.value?.show({ title: '请填写快递单号', type: 'error' })
return
}
if (form.mDeliverTime == '') {
uToast.value?.show({ title: '请选择发货时间', type: 'error' })
return
}
uni.showLoading({ title: '加载中', mask: true })
fillShipInfo(serviceDetail.sn, submitForm).then((res) => {
hideLoadingIfNeeded()
if (res.statusCode === 200) {
uToast.value?.show({
title: '提交成功',
type: 'success',
back: true,
url: '/pages/order/afterSales/afterSales',
})
}
})
}
function gotoGoodsDetail(item: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.skuId}&goodsId=${item.goodsId}`,
})
}
</script>
<style lang="scss" scoped>

View File

@@ -57,59 +57,51 @@
</view>
</template>
<script>
import { getAfterSaleInfo } from "@/api/after-sale";
import storage from "@/utils/storage";
export default {
data() {
return {
sn: "",
sku: {}, //sku
applyInfo:""
};
},
onLoad(options) {
this.sn = options.sn;
this.sku = storage.getAfterSaleData();
// 查看当前商品是否支持退款退货
this.init()
},
methods: {
getGoodsName(item) {
return item.goodsName || item.name || "";
},
getGoodsImage(item) {
const image = item.image || item.goodsImage || item.thumbnail;
return this.parseGoodsImageUrl(image);
},
// 初始化数据
init() {
getAfterSaleInfo(this.sn).then((response) => {
if (response.data.success) {
this.applyInfo = response.data.result;
}
});
},
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { unitPrice, parseGoodsImageUrl } from '@/utils/filters.js'
import { getAfterSaleInfo } from '@/api/after-sale'
import storage from '@/utils/storage'
/**
* 选择退货流程
*/
onSelect(value) {
uni.redirectTo({
url: `./afterSalesDetail?sn=${this.sn}&value=${value}`,
});
},
const sn = ref('')
const sku = ref<any>({})
const applyInfo = ref<any>({})
/**
* 跳转到商品信息
*/
navigateToGoodsDetail(id) {
uni.navigateTo({
url: `/pages/product/goods?id=${id}&goodsId=${goodsId}`,
});
},
},
};
onLoad((options) => {
sn.value = options?.sn || ''
sku.value = storage.getAfterSaleData()
init()
})
function getGoodsName(item: any) {
return item.goodsName || item.name || ''
}
function getGoodsImage(item: any) {
const image = item.image || item.goodsImage || item.thumbnail
return parseGoodsImageUrl(image)
}
function init() {
getAfterSaleInfo(sn.value).then((response) => {
if (response.data.success) {
applyInfo.value = response.data.result
}
})
}
function onSelect(value: number) {
uni.redirectTo({
url: `./afterSalesDetail?sn=${sn.value}&value=${value}`,
})
}
function navigateToGoodsDetail(skuId: string) {
uni.navigateTo({
url: `/pages/product/goods?id=${skuId}&goodsId=${sku.value.goodsId}`,
})
}
</script>
<style lang="scss">

View File

@@ -25,7 +25,7 @@
</view>
<view class="goods-info">
<view class="info-box">
<view class="goods-item-view" @click="navgiateToGoodsDetail(serviceDetail)">
<view class="goods-item-view" @click="navigateToGoodsDetail(serviceDetail)">
<view class="goods-img">
<u-image
border-radius="6"
@@ -184,198 +184,184 @@
</view>
</template>
<script>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import {
unitPrice,
parseGoodsImageUrl,
serviceStatusList,
secrecyMobile,
unixToDate,
} from '@/utils/filters.js'
import {
getServiceDetail,
getStoreAfterSaleAddress,
getAfterSaleLog,
getAfterSaleReason,
} from "@/api/after-sale.js";
export default {
data() {
return {
reason: "", //申请原因
serviceTypeList: {
// 售后类型
CANCEL: "取消",
RETURN_GOODS: "退货",
EXCHANGE_GOODS: "换货",
RETURN_MONEY: "退款",
},
serviceDetail: {}, // 售后详情
logs: [], //日志
goodsList: [], //商品列表
storeAfterSaleAddress: {}, //售后地址
refundShow: false, //退款开关
accountShow: false, //账户显示
bankShow: false, //银行显示
sn: "", //订单sn
};
},
onLoad(options) {
uni.setNavigationBarTitle({
title: "服务单详情",
});
this.sn = options.sn;
this.loadDetail();
this.getAddress();
this.getLog(options.sn);
},
methods: {
statusFilter(val) {
switch (val) {
case "APPLY":
return "售后服务申请成功,等待商家审核";
case "PASS":
return "售后服务申请审核通过";
case "REFUSE":
return "售后服务申请已被商家拒绝,如有疑问请及时联系商家";
case "FULL_COURIER":
return "申请售后的商品已经寄出,等待商家收货";
case "STOCK_IN":
return "商家已将售后商品入库";
case "WAIT_FOR_MANUAL":
return "等待平台进行人工退款";
case "REFUNDING":
return "商家退款中,请您耐心等待";
case "COMPLETED":
return "售后服务已完成,感谢您的支持";
case "ERROR_EXCEPTION":
return "系统生成新订单异常,等待商家手动创建新订单";
case "CLOSED":
return "售后服务已关闭";
case "WAIT_REFUND":
return "等待平台进行退款";
default:
return "";
}
},
refundWayFilter(val) {
switch (val) {
case "OFFLINE":
return "账户退款";
case "ORIGINAL":
return "原路退回";
default:
return "";
}
},
accountTypeFilter(val) {
switch (val) {
case "WEIXINPAY":
return "微信";
case "ALIPAY":
return "支付宝";
case "BANK_TRANSFER":
return "银行卡";
default:
return "";
}
},
/**
* 点击图片放大或保存
*/
preview(urls, index) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ["保存图片"],
success: function (data) {},
fail: function (err) {},
},
});
},
} from '@/api/after-sale.js'
/**
* 获取地址信息
*/
getAddress() {
getStoreAfterSaleAddress(this.sn).then((res) => {
if (res.data.success) {
this.storeAfterSaleAddress = res.data.result;
}
});
const store = useStore()
const reason = ref('')
const serviceTypeList: Record<string, string> = {
CANCEL: '取消',
RETURN_GOODS: '退货',
EXCHANGE_GOODS: '换货',
RETURN_MONEY: '退款',
}
const serviceDetail = ref<any>(null)
const logs = ref<any[]>([])
const storeAfterSaleAddress = ref<any>({})
const refundShow = ref(false)
const accountShow = ref(false)
const bankShow = ref(false)
const sn = ref('')
onLoad((options) => {
uni.setNavigationBarTitle({ title: '服务单详情' })
sn.value = options?.sn || ''
loadDetail()
fetchAddress()
fetchLog(sn.value)
})
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function statusFilter(val: string) {
switch (val) {
case 'APPLY':
return '售后服务申请成功,等待商家审核'
case 'PASS':
return '售后服务申请审核通过'
case 'REFUSE':
return '售后服务申请已被商家拒绝,如有疑问请及时联系商家'
case 'FULL_COURIER':
return '申请售后的商品已经寄出,等待商家收货'
case 'STOCK_IN':
return '商家已将售后商品入库'
case 'WAIT_FOR_MANUAL':
return '等待平台进行人工退款'
case 'REFUNDING':
return '商家退款中,请您耐心等待'
case 'COMPLETED':
return '售后服务已完成,感谢您的支持'
case 'ERROR_EXCEPTION':
return '系统生成新订单异常,等待商家手动创建新订单'
case 'CLOSED':
return '售后服务已关闭'
case 'WAIT_REFUND':
return '等待平台进行退款'
default:
return ''
}
}
function refundWayFilter(val: string) {
switch (val) {
case 'OFFLINE':
return '账户退款'
case 'ORIGINAL':
return '原路退回'
default:
return ''
}
}
function accountTypeFilter(val: string) {
switch (val) {
case 'WEIXINPAY':
return '微信'
case 'ALIPAY':
return '支付宝'
case 'BANK_TRANSFER':
return '银行卡'
default:
return ''
}
}
function preview(urls: string[], index: number) {
uni.previewImage({
current: index,
urls,
longPressActions: {
itemList: ['保存图片'],
success: () => {},
fail: () => {},
},
})
}
/**
* 获取日志
*/
getLog(sn) {
getAfterSaleLog(sn).then((res) => {
this.logs = res.data.result;
});
},
function fetchAddress() {
getStoreAfterSaleAddress(sn.value).then((res) => {
if (res.data.success) {
storeAfterSaleAddress.value = res.data.result
}
})
}
/**
* 获取申请原因
*/
getReasonList(serviceType) {
getAfterSaleReason(serviceType).then((res) => {
if (res.data.success) {
// 1357583466371219456
this.reason = this.serviceDetail.reason;
}
});
},
function fetchLog(serviceSn: string) {
getAfterSaleLog(serviceSn).then((res) => {
logs.value = res.data.result
})
}
/**
* 初始化详情
*/
loadDetail() {
uni.showLoading({
title: "加载中",
});
getServiceDetail(this.sn).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
this.serviceDetail = res.data.result;
if (
this.serviceDetail.serviceType == "RETURN_GOODS" ||
this.serviceDetail.serviceType === "RETURN_MONEY"
) {
this.refundShow = true;
}
function fetchReasonList(serviceType: string) {
getAfterSaleReason(serviceType).then((res) => {
if (res.data.success) {
reason.value = serviceDetail.value.reason
}
})
}
this.accountShow =
(this.serviceDetail.serviceType === "RETURN_GOODS" ||
this.serviceDetail.serviceType === "ORDER_CANCEL") &&
this.serviceDetail.refundWay === "OFFLINE";
function loadDetail() {
uni.showLoading({ title: '加载中' })
getServiceDetail(sn.value).then((res) => {
hideLoadingIfNeeded()
serviceDetail.value = res.data.result
if (
serviceDetail.value.serviceType == 'RETURN_GOODS' ||
serviceDetail.value.serviceType === 'RETURN_MONEY'
) {
refundShow.value = true
}
this.bankShow =
this.serviceDetail.accountType === "BANK_TRANSFER" &&
this.serviceDetail.refundWay === "OFFLINE" &&
((this.serviceDetail.serviceType === "RETURN_GOODS") |
(this.serviceDetail.serviceType === "ORDER_CANCEL") ||
this.serviceDetail.serviceType === "RETURN_MONEY");
accountShow.value =
(serviceDetail.value.serviceType === 'RETURN_GOODS' ||
serviceDetail.value.serviceType === 'ORDER_CANCEL') &&
serviceDetail.value.refundWay === 'OFFLINE'
this.getReasonList(this.serviceDetail.serviceType);
});
},
bankShow.value =
serviceDetail.value.accountType === 'BANK_TRANSFER' &&
serviceDetail.value.refundWay === 'OFFLINE' &&
((serviceDetail.value.serviceType === 'RETURN_GOODS') |
(serviceDetail.value.serviceType === 'ORDER_CANCEL') ||
serviceDetail.value.serviceType === 'RETURN_MONEY')
/**
* 访问商品详情
*/
navgiateToGoodsDetail(item) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
});
},
fetchReasonList(serviceDetail.value.serviceType)
})
}
/**
* 进度
*/
onProgress() {
uni.navigateTo({
url: `./applyProgress?sn=${
this.serviceDetail.sn
}&createTime=${encodeURIComponent(this.serviceDetail.createTime)}
&logs=${encodeURIComponent(JSON.stringify(this.logs))}&serviceStatus=${
this.serviceDetail.serviceStatus
}`,
});
},
},
};
function navigateToGoodsDetail(item: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
})
}
function onProgress() {
uni.navigateTo({
url: `./applyProgress?sn=${
serviceDetail.value.sn
}&createTime=${encodeURIComponent(serviceDetail.value.createTime)}
&logs=${encodeURIComponent(JSON.stringify(logs.value))}&serviceStatus=${
serviceDetail.value.serviceStatus
}`,
})
}
</script>
<style lang="scss" scoped>

View File

@@ -35,54 +35,50 @@
</view>
</template>
<script>
export default {
data() {
return {
sn: "", //sn
createTime: "", //创建时间
logList: [], //日志集合
serviceStatus: "", //订单状态
};
},
onLoad(options) {
this.sn = options.sn;
this.createTime = decodeURIComponent(options.createTime);
this.serviceStatus = this.statusFilter(options.serviceStatus);
this.logList = JSON.parse(decodeURIComponent(options.logs));
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
},
methods: {
statusFilter(val) {
switch (val) {
case "APPLY":
return "售后服务申请成功,等待商家审核";
case "PASS":
return "售后服务申请审核通过";
case "REFUSE":
return "售后服务申请已被商家拒绝,如有疑问请及时联系商家";
case "FULL_COURIER":
return "申请售后的商品已经寄出,等待商家收货";
case "STOCK_IN":
return "商家已将售后商品入库";
case "WAIT_FOR_MANUAL":
return "等待平台进行人工退款";
case "REFUNDING":
return "商家退款中,请您耐心等待";
case "COMPLETED":
return "售后服务已完成,感谢您的支持";
case "ERROR_EXCEPTION":
return "系统生成新订单异常,等待商家手动创建新订单";
case "CLOSED":
return "售后服务已关闭";
case "WAIT_REFUND":
return "等待平台进行退款";
default:
return "";
}
},
},
};
const sn = ref('')
const createTime = ref('')
const logList = ref<any[]>([])
const serviceStatus = ref('')
function statusFilter(val: string) {
switch (val) {
case 'APPLY':
return '售后服务申请成功,等待商家审核'
case 'PASS':
return '售后服务申请审核通过'
case 'REFUSE':
return '售后服务申请已被商家拒绝,如有疑问请及时联系商家'
case 'FULL_COURIER':
return '申请售后的商品已经寄出,等待商家收货'
case 'STOCK_IN':
return '商家已将售后商品入库'
case 'WAIT_FOR_MANUAL':
return '等待平台进行人工退款'
case 'REFUNDING':
return '商家退款中,请您耐心等待'
case 'COMPLETED':
return '售后服务已完成,感谢您的支持'
case 'ERROR_EXCEPTION':
return '系统生成新订单异常,等待商家手动创建新订单'
case 'CLOSED':
return '售后服务已关闭'
case 'WAIT_REFUND':
return '等待平台进行退款'
default:
return ''
}
}
onLoad((options) => {
sn.value = options.sn || ''
createTime.value = decodeURIComponent(options.createTime || '')
serviceStatus.value = statusFilter(options.serviceStatus || '')
logList.value = JSON.parse(decodeURIComponent(options.logs || '[]'))
})
</script>
<style lang="scss" scoped>

View File

@@ -21,31 +21,18 @@
</view>
</template>
<script>
export default {
data() {
return {};
},
methods: {
/**
* 跳转到售后服务
*/
navigateToAfterSales() {
uni.redirectTo({
url: "/pages/order/afterSales/afterSales",
});
},
<script setup lang="ts">
function navigateToAfterSales() {
uni.redirectTo({
url: '/pages/order/afterSales/afterSales',
})
}
/**
* 跳转到首页
*/
navigateToHome() {
uni.switchTab({
url: "/pages/tabbar/home/index",
});
},
},
};
function navigateToHome() {
uni.switchTab({
url: '/pages/tabbar/home/index',
})
}
</script>
<style lang="scss" scoped>

View File

@@ -57,137 +57,112 @@
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import { getOrderDetail } from "@/api/order.js";
import { getComplainReason, addComplain } from "@/api/after-sale.js";
import { handleUploadAfterRead } from "@/utils/uploadHelper.js";
export default {
data() {
return {
storage,
uploadFileList: [],
orderStatusMap: {
//订单状态列表
UNDELIVERED: "待发货",
PARTS_DELIVERED: "部分发货",
UNPAID: "未付款",
PAID: "已付款",
DELIVERED: "已发货",
CANCELLED: "已取消",
COMPLETE: "已完成",
TAKE: "已完成",
},
complainValue: "", //投诉内容
complainShow: false, //投诉主题开关
complainTopic: "", //投诉抱怨话题
complainList: [], // 投诉列表
images: [], //投诉内容图片
order: "", //订单
orderGoodsList: "", //订单商品
orderDetail: "", //订单详情
sn: "",
skuId: "", //商品skuid
};
},
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getOrderDetail } from '@/api/order.js'
import { getComplainReason, addComplain } from '@/api/after-sale.js'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
import { unitPrice } from '@/utils/filters.js'
onLoad(option) {
this.loadData(option.sn);
this.sn = option.sn;
this.skuId = option.skuId;
this.getReasion();
},
const store = useStore()
methods: {
onUploadAfterRead(event) {
handleUploadAfterRead(event, this.uploadFileList, (urls) => {
this.images = urls;
});
},
/**
* 提交
*/
handleSubmit() {
if(!this.images.length && !this.complainValue){
uni.showToast({
title:'请上传图片凭证和投诉内容',
icon:'none'
const orderStatusMap: Record<string, string> = {
UNDELIVERED: '待发货',
PARTS_DELIVERED: '部分发货',
UNPAID: '未付款',
PAID: '已付款',
DELIVERED: '已发货',
CANCELLED: '已取消',
COMPLETE: '已完成',
TAKE: '已完成',
}
const uploadFileList = ref<any[]>([])
const complainValue = ref('')
const complainShow = ref(false)
const complainTopic = ref('')
const complainList = ref<any[]>([])
const images = ref<string[]>([])
const order = ref<Record<string, any>>({})
const orderGoodsList = ref<any[]>([])
const sn = ref('')
const skuId = ref('')
onLoad((option) => {
loadData(option.sn)
sn.value = option.sn
skuId.value = option.skuId
getReasion()
})
function onUploadAfterRead(event: any) {
handleUploadAfterRead(event, uploadFileList.value, (urls) => {
images.value = urls
})
}
function handleSubmit() {
if (!images.value.length && !complainValue.value) {
uni.showToast({
title: '请上传图片凭证和投诉内容',
icon: 'none',
})
return
}
const goods = orderGoodsList.value.filter((item) => item.skuId == skuId.value)
const data = {
complainTopic: complainTopic.value,
content: complainValue.value,
goodsId: goods[0].goodsId,
images: images.value,
orderSn: sn.value,
skuId: skuId.value,
}
addComplain(data).then((res) => {
if (res.data.success) {
uni.showToast({
title: '提交成功!',
duration: 2000,
icon: 'none',
})
setTimeout(() => {
uni.redirectTo({
url: '/pages/order/complain/complainList',
})
return
}, 1000)
}
})
}
}
// 循环出商品
let goods = this.orderGoodsList.filter((item) => {
return item.skuId == this.skuId;
});
//数据赋值
let data = {
complainTopic: this.complainTopic, //投诉主题,
content: this.complainValue, //投诉内容
goodsId: goods[0].goodsId, //商品id
images: this.images, //图片
orderSn: this.sn, //订单号
skuId: this.skuId, //skuid
};
addComplain(data).then((res) => {
if (res.data.success) {
uni.showToast({
title: "提交成功!",
duration: 2000,
icon: "none",
});
function getReasion() {
getComplainReason().then((res) => {
if (res.data.result.length >= 1) {
res.data.result.forEach((item: any) => {
complainList.value.push({
value: item.reason,
label: item.reason,
})
})
complainTopic.value = res.data.result[0].reason
}
})
}
setTimeout(() => {
uni.redirectTo({
url: "/pages/order/complain/complainList",
});
}, 1000);
}
});
},
function loadData(orderSn: string) {
uni.showLoading({ title: '加载中' })
getOrderDetail(orderSn).then((res) => {
const result = res.data.result
order.value = result.order
orderGoodsList.value = result.orderItems
if (store.state.isShowToast) uni.hideLoading()
})
}
/**
* 获取投诉原因
*/
getReasion() {
getComplainReason().then((res) => {
if (res.data.result.length >= 1) {
res.data.result.forEach((item) => {
let way = {
value: item.reason,
label: item.reason,
};
this.complainList.push(way);
});
this.complainTopic = res.data.result[0].reason;
}
});
},
/**
* 加载订单详情
*/
loadData(sn) {
uni.showLoading({
title: "加载中",
});
getOrderDetail(sn).then((res) => {
const order = res.data.result;
this.order = order.order;
this.orderGoodsList = order.orderItems;
this.orderDetail = res.data.result;
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
/**
* 确认投诉
*/
confirmComplain(e) {
this.complainTopic = e[0].label;
},
},
};
function confirmComplain(e: any[]) {
complainTopic.value = e[0].label
}
</script>
<style lang="scss" scoped>

View File

@@ -34,7 +34,7 @@
<span>{{ complaint.content }}</span>
</view>
</view>
<view class="speak-way" v-else>暂无对话</view>
<view class="speak-way" v-else>暂无对话</view>
<div v-if="complainDetail.complainStatus!='COMPLETE'">
<view class="tips">回复对话</view>
<view class="cell-item complain-content">
@@ -42,7 +42,7 @@
<u-input type="textarea" height="70rpx" auto-height v-model="complainValue" />
</view>
</view>
<view class="submit-btn" @click="handleSubmit">回复</view>
<view class="submit-btn" @click="handleSubmit">回复</view>
</div>
<view class="tips">平台仲裁</view>
<u-cell-group>
@@ -51,99 +51,91 @@
</view>
</template>
<script>
import { getComplainDetail, communication } from "@/api/after-sale";
export default {
data() {
return {
complainId: "",
complainValue: "", //回复内容
complainDetail: "", //投诉详情
statusData: {
NEW: "新投诉",
NO_APPLY: "未申请",
APPLYING: "申请中",
COMPLETE: "已完成",
EXPIRED: "已失效",
CANCEL: "已取消",
WAIT_ARBITRATION:"等待仲裁"
},
};
},
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getComplainDetail, communication } from '@/api/after-sale'
onLoad(option) {
this.complainId = option.id;
this.init(option.id);
},
methods: {
/**
* 点击图片放大或保存
*/
preview(urls, index) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ["保存图片"],
success: function (data) {},
fail: function (err) {},
},
});
},
handleSubmit() {
if (!this.complainValue) {
uni.showToast({
title: "请输入回复内容",
duration: 2000,
icon: "none",
});
return;
}
let params = {
content: this.complainValue,
complainId: this.complainId,
};
communication(params).then((res) => {
if (res.data.success) {
uni.showToast({
title: "回复成功",
duration: 2000,
icon: "none",
});
this.complainValue = '';
this.init(this.complainId);
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
const store = useStore()
const complainId = ref('')
const complainValue = ref('')
const complainDetail = ref<Record<string, any>>({})
const statusData: Record<string, string> = {
NEW: '新投诉',
NO_APPLY: '未申请',
APPLYING: '申请中',
COMPLETE: '已完成',
EXPIRED: '已失效',
CANCEL: '已取消',
WAIT_ARBITRATION: '等待仲裁',
}
onLoad((option) => {
complainId.value = option.id
init(option.id)
})
function preview(urls: string[], index: number) {
uni.previewImage({
current: index,
urls,
longPressActions: {
itemList: ['保存图片'],
success: () => {},
fail: () => {},
},
/**
* 初始化投诉详情
*/
init(id) {
uni.showLoading({
title: "加载中",
});
getComplainDetail(id).then((res) => {
if (res.data.success) {
this.complainDetail = res.data.result;
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
})
}
function handleSubmit() {
if (!complainValue.value) {
uni.showToast({
title: '请输入回复内容',
duration: 2000,
icon: 'none',
})
return
}
const params = {
content: complainValue.value,
complainId: complainId.value,
}
communication(params).then((res) => {
if (res.data.success) {
uni.showToast({
title: '回复成功',
duration: 2000,
icon: 'none',
})
complainValue.value = ''
init(complainId.value)
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}
function init(id: string) {
uni.showLoading({ title: '加载中' })
getComplainDetail(id).then((res) => {
if (res.data.success) {
complainDetail.value = res.data.result
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
if (store.state.isShowToast) uni.hideLoading()
})
}
</script>
<style lang="scss" scoped>
.row {

View File

@@ -1,181 +1,239 @@
<template>
<view>
<view class="seller-view" v-for="(item, index) in complaionData" :key="index">
<view class="seller-info u-flex u-row-between">
<view class="seller-name">
<view class="name">{{ item.storeName }}</view>
</view>
<view class="order-sn">{{ statusData[item.complainStatus] }}</view>
</view>
<u-line color="#DCDFE6"></u-line>
<view class="goods-item-view">
<view class="goods-img" @click="handleToGoods(item)">
<u-image border-radius="6" width="131rpx" height="131rpx" :src="item.goodsImage"></u-image>
</view>
<view class="goods-info" @click="handleToGoods(item)">
<view class="goods-title u-line-2">{{ item.goodsName }}</view>
<view class="goods-price">
{{unitPrice(item.goodsPrice) }}
<!-- <span>+{{ '1' }}积分</span> -->
</view>
</view>
<view class="goods-num">
<view>x{{ item.num }}</view>
</view>
</view>
<view class="complain-item-view">
<view class="complain-time"> {{ item.createTime }} </view>
<view class="complain-speak"> {{ item.complainTopic }} </view>
</view>
<view class="complain-btn">
<u-tag mode="plain" @click="handleClear(item)" class="complain-tag" text="撤销投诉" type="info"
v-if="item.complainStatus === 'APPLYING' || item.complainStatus === 'NEW'" />
<u-tag mode="plain" @click="handleInfo(item)" class="complain-tag" text="投诉详情" type="info" />
</view>
</view>
<u-empty v-if="empty" :style="{'marginTop':complaionDetail.total == 0 ? '200rpx':'0rpx'}" class="empty" style="" text="暂无投诉列表" mode="list"></u-empty>
<u-modal show-cancel-button @confirm="handleClearConfirm" v-model:show="show" :content="content"></u-modal>
</view>
</template>
<script>
import { getComplain, clearComplain } from "@/api/after-sale";
export default {
data() {
return {
statusData: {
NEW: "新投诉",
NO_APPLY: "未申请",
APPLYING: "申请中",
COMPLETE: "已完成",
EXPIRED: "已失效",
CANCEL: "已取消",
WAIT_ARBITRATION:"等待仲裁"
},
show: false,
content: "是否撤销投诉?",
params: {
pageNumber: 1,
pageSize: 20,
},
complaionDetail: "", //返回的整个response
complaionData: [], //投诉列表
empty: false,
checkComplainData: "", //存储投诉信息
};
},
mounted() {
this.init();
},
/**
* 触底加载
*/
onReachBottom() {
if (
this.complaionDetail &&
this.complaionDetail.total < this.params.pageNumber * this.params.pageSize
) {
this.params.pageNumber++;
this.init();
}
},
methods: {
// 点击跳转到商品
handleToGoods(val) {
uni.navigateTo({
url: "/pages/product/goods?id=" + val.skuId + "&goodsId=" + val.goodsId,
});
},
/**
* 点击撤销投诉
*/
handleClear(val) {
this.show = true;
this.checkComplainData = val;
},
/**
* 执行撤销
*/
handleClearConfirm() {
clearComplain(this.checkComplainData.id).then((res) => {
if (res.data.success) {
uni.showToast({
title: "撤销成功",
duration: 2000,
icon: "none",
});
this.complaionData = [];
this.params.pageNumber = 1;
this.init();
}
});
},
/**
* 查看详情
*/
handleInfo(val) {
uni.navigateTo({
url: "./complainInfo?id=" + val.id,
});
},
/**
* 初始化投诉列表
*/
init() {
uni.showLoading({
title: "加载中",
});
getComplain(this.params).then((res) => {
this.complaionDetail = res.data.result;
if (res.data.result.records.length >= 1) {
this.complaionData.push(...res.data.result.records);
} else {
this.empty = true;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
},
};
</script>
<style lang="scss" scoped>
@import "../goods.scss";
.complain-item-view {
border-bottom: 2rpx solid #f5f7fa;
border-top: 2rpx solid #f5f7fa;
padding: 20rpx 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.complain-time {
font-size: 24rpx;
color: #999;
}
::v-deep .seller-name {
width: auto !important;
}
.complain-btn {
padding: 20rpx 0;
display: flex;
align-items: center;
justify-content: flex-end;
margin-right: 30rpx;
}
.complain-tag {
margin-left: 10rpx;
}
.empty {
margin-top: 40rpx;
}
</style>
<template>
<view>
<view class="seller-view" v-for="(item, index) in complaionData" :key="index">
<view class="seller-info u-flex u-row-between">
<view class="seller-name">
<view class="name">{{ item.storeName }}</view>
</view>
<view class="order-sn">{{ statusData[item.complainStatus] }}</view>
</view>
<u-line color="#DCDFE6"></u-line>
<view class="goods-item-view">
<view class="goods-img" @click="handleToGoods(item)">
<u-image radius="6rpx" width="131rpx" height="131rpx" :src="item.goodsImage"></u-image>
</view>
<view class="goods-info" @click="handleToGoods(item)">
<view class="goods-title u-line-2">{{ item.goodsName }}</view>
<view class="goods-price">
{{unitPrice(item.goodsPrice) }}
<!-- <span>+{{ '1' }}积分</span> -->
</view>
</view>
<view class="goods-num">
<view>x{{ item.num }}</view>
</view>
</view>
<view class="complain-item-view">
<view class="complain-time"> {{ item.createTime }} </view>
<view class="complain-speak"> {{ item.complainTopic }} </view>
</view>
<view class="complain-btn">
<u-tag plain @click="handleClear(item)" class="complain-tag" text="撤销投诉" type="info"
v-if="item.complainStatus === 'APPLYING' || item.complainStatus === 'NEW'" />
<u-tag plain @click="handleInfo(item)" class="complain-tag" text="投诉详情" type="info" />
</view>
</view>
<u-empty v-if="empty" :style="{'marginTop':complaionDetail.total == 0 ? '200rpx':'0rpx'}" class="empty" style="" text="暂无投诉列表" mode="list"></u-empty>
<u-modal show-cancel-button @confirm="handleClearConfirm" v-model:show="show" :content="content"></u-modal>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { unitPrice } from '@/utils/filters.js'
import { getComplain, clearComplain } from '@/api/after-sale'
const store = useStore()
const statusData: Record<string, string> = {
NEW: '新投诉',
NO_APPLY: '未申请',
APPLYING: '申请中',
COMPLETE: '已完成',
EXPIRED: '已失效',
CANCEL: '已取消',
WAIT_ARBITRATION: '等待仲裁',
}
const show = ref(false)
const content = ref('是否撤销投诉?')
const params = ref({ pageNumber: 1, pageSize: 20 })
const complaionDetail = ref<any>(null)
const complaionData = ref<any[]>([])
const empty = ref(false)
const checkComplainData = ref<any>(null)
onLoad(() => {
init()
})
onReachBottom(() => {
if (
complaionDetail.value &&
complaionDetail.value.total > params.value.pageNumber * params.value.pageSize
) {
params.value.pageNumber++
init()
}
})
function handleToGoods(val: any) {
uni.navigateTo({
url: '/pages/product/goods?id=' + val.skuId + '&goodsId=' + val.goodsId,
})
}
function handleClear(val: any) {
show.value = true
checkComplainData.value = val
}
function handleClearConfirm() {
clearComplain(checkComplainData.value.id).then((res) => {
if (res.data.success) {
uni.showToast({
title: '撤销成功',
duration: 2000,
icon: 'none',
})
complaionData.value = []
params.value.pageNumber = 1
init()
}
})
}
function handleInfo(val: any) {
uni.navigateTo({
url: './complainInfo?id=' + val.id,
})
}
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
function init() {
uni.showLoading({
title: '加载中',
})
getComplain(params.value).then((res) => {
complaionDetail.value = res.data.result
if (res.data.result.records.length >= 1) {
complaionData.value.push(...res.data.result.records)
} else {
empty.value = true
}
hideLoadingIfNeeded()
})
}
</script>
<style lang="scss" scoped>
.seller-view {
background-color: #fff;
margin: 20rpx 0;
}
.seller-info {
height: 70rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.seller-name {
width: auto;
min-width: 0;
font-size: 33rpx;
font-weight: 600;
display: flex;
flex-direction: row;
align-items: center;
height: 90rpx;
}
.name {
margin-left: 15rpx;
margin-top: -2rpx;
font-size: 28rpx;
flex: 1;
}
.order-sn {
color: #ff0000;
font-size: 26rpx;
flex-shrink: 0;
margin-left: 20rpx;
}
.goods-item-view {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 10rpx 30rpx;
}
.goods-img {
width: 131rpx;
height: 131rpx;
flex: 0 0 131rpx;
}
.goods-info {
padding-left: 30rpx;
min-width: 0;
flex: 1;
}
.goods-title {
margin-bottom: 10rpx;
color: $font-color-dark;
}
.goods-price {
font-size: 28rpx;
margin-bottom: 10rpx;
color: #ff5a10;
}
.goods-num {
text-align: center;
flex: 0 0 60rpx;
width: 60rpx;
color: $main-color;
}
.complain-item-view {
border-bottom: 2rpx solid #f5f7fa;
border-top: 2rpx solid #f5f7fa;
padding: 20rpx 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.complain-time {
font-size: 24rpx;
color: #999;
}
.complain-btn {
padding: 20rpx 0;
display: flex;
align-items: center;
justify-content: flex-end;
margin-right: 30rpx;
}
.complain-tag {
margin-left: 10rpx;
}
.empty {
margin-top: 40rpx;
}
</style>

View File

@@ -45,34 +45,24 @@
</div>
</template>
<script>
import { getPackage } from "@/api/trade.js";
export default {
data() {
return {
order: {},
logisticsList: [],
}
},
components: {
},
computed: {
},
onLoad(option) {
let sn = option.order_sn;
this.tracesList(sn);
},
mounted() {
},
methods: {
tracesList(sn) {
getPackage(sn).then((res) => {
if(res.data.success){
this.logisticsList = res.data.result;
}
});
},
},
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getPackage } from '@/api/trade.js'
const logisticsList = ref<any[]>([])
onLoad((option) => {
const sn = option.order_sn
if (sn) fetchLogistics(sn)
})
function fetchLogistics(sn: string) {
getPackage(sn).then((res) => {
if (res.data.success) {
logisticsList.value = res.data.result
}
})
}
</script>
<style >

View File

@@ -31,7 +31,7 @@
<view class="img">
<!-- 循环出商家回复评价的图片 -->
<u-image width="140rpx" height="140rpx" v-if="comment.replyImage" v-for="(replyImg, replyIndex) in splitImg(comment.replyImage)" :src="replyImg" :key="replyIndex"
@click="preview(splitImg( comment.replyImage), index)">
@click="preview(splitImg(comment.replyImage), replyIndex)">
</u-image>
</view>
</view>
@@ -40,56 +40,46 @@
</view>
</template>
<script>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import configs from '@/config/config'
export default {
data() {
return {
configs,
userImage:configs.defaultUserPhoto,
comment: {}, //评论信息
gradeList: {
//评价grade
GOOD: "好评",
MODERATE: "中评",
WORSE: "差评",
haveImage: "有图",
},
};
},
onLoad(options) {
this.comment = JSON.parse(decodeURIComponent(options.comment));
},
methods: {
/**
* 切割图像
*/
splitImg(val) {
if (val && val.split(",")) {
return val.split(",");
} else if (val) {
return val;
} else {
return false;
}
const userImage = configs.defaultUserPhoto
const comment = ref<Record<string, any>>({})
const gradeList: Record<string, string> = {
GOOD: '好评',
MODERATE: '中评',
WORSE: '差评',
haveImage: '有图',
}
onLoad((options) => {
comment.value = JSON.parse(decodeURIComponent(options.comment))
})
function splitImg(val: string) {
if (val && val.split(',')) {
return val.split(',')
} else if (val) {
return val
}
return false
}
function preview(urls: string[] | false, index: number) {
if (!urls) return
uni.previewImage({
current: index,
urls: Array.isArray(urls) ? urls : [urls],
longPressActions: {
itemList: ['保存图片'],
success: () => {},
fail: () => {},
},
/**
* 点击图片放大或保存
*/
preview(urls, index) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ["保存图片"],
success: function (data) {},
fail: function (err) {},
},
});
},
},
};
})
}
</script>
<style lang="scss" scoped>

View File

@@ -8,8 +8,9 @@
:inactiveStyle="{ color: '#333' }"
v-model:current="current"
class="utabs"
:lineColor="$lightColor"
:activeStyle="{ color: $lightColor }"
:lineColor="lightColor"
:activeStyle="{ color: lightColor }"
:bg-color="'#ffffff'"
></u-tabs>
</view>
<swiper class="swiper-box" :current="current" @change="changeSwiper" duration="500">
@@ -38,7 +39,7 @@
<view class="btn-view u-row-between" v-if="current == 2">
<view class="description">
<view class="text title">
<u-read-more ref="uReadMore" :color="$lightColor" text-indent="0">
<u-read-more ref="uReadMore" :color="lightColor" text-indent="0">
<rich-text :nodes="'评论内容:' + order.content || ''"></rich-text>
</u-read-more>
</view>
@@ -78,209 +79,144 @@
</view>
</template>
<script>
import { getOrderList } from "@/api/order.js";
import { getComments } from "@/api/members.js";
<script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getOrderList } from '@/api/order.js'
import { getComments } from '@/api/members.js'
export default {
data() {
return {
list: [
//顶部tab
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const list = [
{ name: '全部订单' },
{ name: '待评价' },
{ name: '已评价' },
]
const gradeList: Record<string, string> = {
GOOD: '好评',
MODERATE: '中评',
WORSE: '差评',
haveImage: '有图',
}
const groupCommentStatusWay: Record<string, string> = {
NEW: '新订单,不能进行评论',
UNFINISHED: '未完成评论',
WAIT_CHASE: '待追评的评论信息',
FINISHED: '已经完成评论',
}
const current = ref(0)
const orderList = ref<any[]>([])
const params = reactive<Record<string, any>>({
pageNumber: 1,
pageSize: 10,
loadStatus: 'more',
})
onShow(() => {
orderList.value = []
params.pageNumber = 1
current.value = 0
loadData()
})
watch(current, (val) => {
params.pageNumber = 1
params.loadStatus = 'more'
orderList.value = []
if (val == 0) {
delete params.commentStatus
loadData()
} else if (val == 1) {
params.commentStatus = 'UNFINISHED'
orderList.value = []
loadData()
} else {
params.commentStatus = 'FINISHED'
orderList.value = []
loadComments()
}
})
function preview(urls: string[], index: number) {
uni.previewImage({
current: index,
urls,
longPressActions: {
itemList: ['保存图片'],
success: () => {},
fail: () => {},
},
})
}
function changeSwiper(e: any) {
current.value = e.target.current
}
function loadData() {
uni.showLoading({ title: '加载中' })
getOrderList(params).then((res) => {
if (store.state.isShowToast) uni.hideLoading()
const records = res.data.result.records
if (records.length < 10) {
params.loadStatus = 'noMore'
}
if (records.length > 0) {
orderList.value = orderList.value.concat(records)
params.pageNumber += 1
}
})
}
function talkCommont(sku: any) {
uni.navigateTo({
url: `./releaseEvaluate?sn=${sku.sn}&sku=${encodeURIComponent(JSON.stringify(sku))}`,
})
}
function loadComments() {
uni.showLoading({ title: '加载中' })
getComments(params).then((res) => {
if (store.state.isShowToast) uni.hideLoading()
const records = res.data.result.records
if (records.length < 10) {
params.loadStatus = 'noMore'
}
records.forEach((item: any) => {
item.orderItems = [
{
name: "全部订单",
image: item.goodsImage,
name: item.goodsName,
goodsId: item.goodsId,
skuId: item.skuId,
},
{
name: "待评价",
},
{
name: "已评价",
},
],
gradeList: {
//评论表
GOOD: "好评",
MODERATE: "中评",
WORSE: "差评",
haveImage: "有图",
},
groupCommentStatusWay: {
NEW: "新订单,不能进行评论",
UNFINISHED: "未完成评论",
WAIT_CHASE: "待追评的评论信息",
FINISHED: "已经完成评论",
},
current: 0, //当前tabIndex
orderList: [], //商品集合
params: {
pageNumber: 1,
pageSize: 10,
loadStatus: "more",
},
};
},
]
})
orderList.value = orderList.value.concat(records)
params.pageNumber += 1
})
}
onShow() {
this.orderList = [];
this.params.pageNumber = 1;
this.current = 0
this.loadData()
},
watch: {
/**
* 切换current
* 更改页面并重新加载数据
*/
current(val) {
this.params.pageNumber = 1;
this.params.loadStatus = "more";
this.orderList = [];
//重新读取数据
function renderData(index: number) {
if (params.loadStatus == 'noMore') return
if (index == 0) {
loadData()
} else {
loadComments()
}
}
if (val == 0) {
delete this.params.commentStatus
this.loadData();
} else if (val == 1) {
this.params.commentStatus = "UNFINISHED";
this.orderList = [];
this.loadData();
} else {
this.params.commentStatus = "FINISHED";
this.orderList = [];
return this.loadComments();
}
},
},
methods: {
/**
* 判断当前店铺是否有可评价的商品
*/
commentStatus(val) {
if (this.current == 2) {
return true;
} else {
let show;
val.orderItems &&
val.orderItems.forEach((item) => {
if (item.commentStatus == "UNFINISHED") {
show = true;
} else {
show = false;
}
});
return show;
}
},
/**
* 点击图片放大或保存
*/
preview(urls, index) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ["保存图片"],
success: function (data) {},
fail: function (err) {},
},
});
},
/**
* 点击swiper
*/
changeSwiper(e) {
this.current = e.target.current;
},
/**
* 获取订单数据
*/
loadData() {
uni.showLoading({
title: "加载中",
});
getOrderList(this.params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
const orderList = res.data.result.records;
if (orderList.length < 10) {
this.params.loadStatus = "noMore";
}
if (orderList.length > 0) {
this.orderList = this.orderList.concat(orderList);
this.params.pageNumber += 1;
}
});
},
/**
* 发表评价
*/
talkCommont(sku) {
console.log(sku);
uni.navigateTo({
url: `./releaseEvaluate?sn=${sku.sn}&sku=${encodeURIComponent(
JSON.stringify(sku)
)}`,
});
},
/**
* 加载已评价数据
*/
loadComments() {
uni.showLoading({
title: "加载中",
});
getComments(this.params).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
let orderList = res.data.result.records;
if (orderList.length < 10) {
this.params.loadStatus = "noMore";
}
orderList.forEach((item) => {
item.orderItems = [
{
image: item.goodsImage,
name: item.goodsName,
goodsId: item.goodsId,
skuId: item.skuId,
},
];
});
this.orderList = this.orderList.concat(orderList);
this.params.pageNumber += 1;
});
},
/**
* 滑到底部加载数据
*/
renderData(index) {
if (this.params.loadStatus == "noMore") return;
if (index == 0) {
this.loadData();
} else {
this.loadComments();
}
},
/**
* 评价详情
*/
onDetail(comment) {
uni.navigateTo({
url:
"./evaluateDetail?comment=" +
encodeURIComponent(JSON.stringify(comment)),
});
},
},
};
function onDetail(comment: any) {
uni.navigateTo({
url: './evaluateDetail?comment=' + encodeURIComponent(JSON.stringify(comment)),
})
}
</script>
<style lang="scss" scoped>
page {
@@ -310,6 +246,7 @@ page {
.u-tabs-box {
position: relative;
z-index: 10;
background: #ffffff;
}
.box-content {
margin: 20rpx 0;

View File

@@ -86,80 +86,69 @@
</view>
</template>
<script>
import storage from "@/utils/storage.js";
import { commentsMemberOrder } from "@/api/members.js";
import { handleUploadAfterRead } from "@/utils/uploadHelper.js";
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { commentsMemberOrder } from '@/api/members.js'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
export default {
data() {
return {
storage,
type: "textarea", //输入框状态为 textarea
border: false, //没有border
maxlength: 500, //评价最大字数为500字
placeholder:
"宝贝满足您的期待吗?说说它的优点和美中不足的地方吧。您的评价会帮助更多的人",
sku: {}, //订单信息
form: {
content: "", //评价详情
goodsId: "", //商品id
grade: "GOOD", //默认为好评
orderItemSn: "", //商品的sn
skuId: "", //商品skuId
descriptionScore: 5, //默认描述得分为5分
serviceScore: 5, //默认服务得分为5分
deliveryScore: 5, //默认物流得分为5分
},
uploadFileList: [],
};
},
onLoad(options) {
// 获取上一级传过来的数据进行解析
this.form.orderItemSn = options.sn;
this.sku = JSON.parse(decodeURIComponent(options.sku));
this.form.goodsId = this.sku.goodsId;
this.form.skuId = this.sku.skuId;
},
methods: {
/**
* 点击评价
*/
onGrade(grade) {
this.form.grade = grade;
},
const store = useStore()
/**
* 提交评价
*/
onSubmit() {
uni.showLoading({
title: "加载中",
});
commentsMemberOrder(this.form).then((res) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
if (res.data.success) {
uni.showToast({
title: "发布评价成功",
duration: 2000,
icon: "none",
success: () => {
setTimeout(() => {
uni.navigateBack();
}, 500);
},
});
}
});
},
const type = 'textarea'
const border = false
const maxlength = 500
const placeholder =
'宝贝满足您的期待吗?说说它的优点和美中不足的地方吧。您的评价会帮助更多的人'
onUploadAfterRead(event) {
handleUploadAfterRead(event, this.uploadFileList, (urls) => {
this.form.images = urls;
});
},
},
};
const sku = ref<Record<string, any>>({})
const form = reactive<Record<string, any>>({
content: '',
goodsId: '',
grade: 'GOOD',
orderItemSn: '',
skuId: '',
descriptionScore: 5,
serviceScore: 5,
deliveryScore: 5,
})
const uploadFileList = ref<any[]>([])
onLoad((options) => {
form.orderItemSn = options.sn
sku.value = JSON.parse(decodeURIComponent(options.sku))
form.goodsId = sku.value.goodsId
form.skuId = sku.value.skuId
})
function onGrade(grade: string) {
form.grade = grade
}
function onSubmit() {
uni.showLoading({ title: '加载中' })
commentsMemberOrder(form).then((res) => {
if (store.state.isShowToast) uni.hideLoading()
if (res.data.success) {
uni.showToast({
title: '发布评价成功',
duration: 2000,
icon: 'none',
success: () => {
setTimeout(() => {
uni.navigateBack()
}, 500)
},
})
}
})
}
function onUploadAfterRead(event: any) {
handleUploadAfterRead(event, uploadFileList.value, (urls) => {
form.images = urls
})
}
</script>
<style lang="scss" scoped>

View File

@@ -384,471 +384,426 @@
</div>
</div>
</template>
<script>
import * as API_Address from "@/api/address";
import * as API_Order from "@/api/order";
import * as API_Trade from "@/api/trade";
import configs from "@/config/config";
import LiLiWXPay from "@/js_sdk/lili-pay/wx-pay.js";
import invoices from "@/pages/order/invoice/setInvoice";
import { mapState } from "vuex";
export default {
onLoad: function (val) {
this.routerVal = val;
},
components: {
invoices,
},
<script setup lang="ts">
import { ref, computed, watch, getCurrentInstance } from 'vue'
import { onLoad, onShow, onBackPress } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import * as API_Address from '@/api/address'
import * as API_Order from '@/api/order'
import * as API_Trade from '@/api/trade'
import configs from '@/config/config'
import LiLiWXPay from '@/js_sdk/lili-pay/wx-pay.js'
import invoices from '@/pages/order/invoice/setInvoice'
import {
unitPrice,
goodsFormatPrice,
secrecyMobile,
isLogin,
} from '@/utils/filters.js'
data() {
return {
configs,
userImage: configs.defaultUserPhoto,
invoiceFlag: false, //开票开关
shippingText: "LOGISTICS",
shippingFlag: false,
shippingMethod: [],
shippingWay: [
{
value: "LOGISTICS",
label: "物流",
},
{
value: "SELF_PICK_UP",
label: "自提",
},
],
isAssemble: false, //是否拼团
// 判断是否填写过备注
remarkFlag: false,
selectAddressId: "",
routerVal: "",
params: {},
// 优惠劵
couponList: "",
// 已选地址
address: "",
shopAddress: "",
// 发票信息
receiptList: "",
// 店铺信息
orderMessage: "",
data: "",
// 存储备注
remarkVal: [],
remarkVal1: "",
detail: "", //返回的所有数据
endWay: "", //最后一个参团人
masterWay: "", //团长信息
pintuanFlage: true, //是开团还是拼团
notSupportFreight: [], //不支持运费
notSupportFreightNoticeText: "",
storeAddress: "",
const store = useStore()
const { proxy } = getCurrentInstance()!
const lightColor = computed(() => store.getters.lightColor)
const mainColor = computed(() => store.getters.mainColor)
const aiderLightColor = computed(() => store.getters.aiderLightColor)
const remark = computed(() => store.state.remark)
originOrderData:"", // 原始订单数据
};
interface ShippingOption {
value: string
label: string
}
const shippingWay: ShippingOption[] = [
{ value: 'LOGISTICS', label: '物流' },
{ value: 'SELF_PICK_UP', label: '自提' },
]
const userImage = configs.defaultUserPhoto
const invoiceFlag = ref(false)
const shippingText = ref('LOGISTICS')
const shippingFlag = ref(false)
const shippingMethod = ref<ShippingOption[]>([])
const isAssemble = ref(false)
const remarkFlag = ref(false)
const selectAddressId = ref('')
const routerVal = ref<Record<string, any>>({})
const params = ref<Record<string, any>>({})
const couponList = ref('')
const address = ref<any>('')
const shopAddress = ref('')
const receiptList = ref<any>('')
const orderMessage = ref<any>('')
const data = ref('')
const remarkVal = ref<any[]>([])
const remarkVal1 = ref('')
const detail = ref('')
const endWay = ref<any>('')
const masterWay = ref<any>('')
const pintuanFlage = ref(true)
const notSupportFreight = ref<any[]>([])
const notSupportFreightNoticeText = ref('')
const storeAddress = ref<any>('')
const originOrderData = ref<any>('')
watch(
remarkVal,
(val) => {
store.commit('setRemark', val)
},
watch: {
// 监听备注 并在 vuex 中存储
remarkVal: {
handler(val) {
this.$store.commit("setRemark", val);
},
immediate: true,
deep: true,
},
},
computed: {
...mapState(["remark"]),
},
/**
* 监听返回
*/
onBackPress(e) {
if (e.from == "backbutton") {
const curRoute = getCurrentPages().slice(-1)[0]?.options || {};
if (curRoute.addId) {
uni.reLaunch({
url: "/pages/tabbar/cart/cartList",
});
} else if (this.routerVal?.way === "CART") {
uni.switchTab({
url: "/pages/tabbar/cart/cartList",
});
} else {
uni.navigateBack();
}
return true;
{ immediate: true, deep: true }
)
onLoad((val) => {
routerVal.value = val || {}
})
onBackPress((e) => {
if (e.from == 'backbutton') {
const curRoute = getCurrentPages().slice(-1)[0]?.options || {}
if (curRoute.addId) {
uni.reLaunch({
url: '/pages/tabbar/cart/cartList',
})
} else if (routerVal.value?.way === 'CART') {
uni.switchTab({
url: '/pages/tabbar/cart/cartList',
})
} else {
uni.navigateBack()
}
},
return true
}
return false
})
async onShow() {
// 判断是否存在写过备注信息的商品
if (this.remark && this.remark.length > 0) {
this.remarkFlag = true;
onShow(async () => {
if (remark.value && remark.value.length > 0) {
remarkFlag.value = true
}
uni.showLoading({
mask: true,
})
try {
await getOrderList()
await getDistribution()
if (routerVal.value.way == 'PINTUAN') {
isAssemble.value = true
routerVal.value.parentOrder = JSON.parse(
decodeURIComponent(routerVal.value.parentOrder)
)
pintuanWay()
}
uni.showLoading({
mask: true,
});
try {
await this.getOrderList();
await this.getDistribution();
if (this.routerVal.way == "PINTUAN") {
this.isAssemble = true;
this.routerVal.parentOrder = JSON.parse(
decodeURIComponent(this.routerVal.parentOrder)
);
this.pintuanWay();
}
} finally {
uni.hideLoading();
} finally {
uni.hideLoading()
}
})
function getShippingLabel() {
const item =
shippingMethod.value.find((e) => e.value === shippingText.value) ||
shippingWay.find((e) => e.value === shippingText.value)
return item ? item.label : ''
}
async function callbackInvoice(val: any) {
invoiceFlag.value = false
receiptList.value = val
if (val) {
const submit = {
way: routerVal.value.way,
...receiptList.value,
}
},
mounted() {},
const receipt = await API_Order.getReceipt(submit)
if (receipt.data.success) {
shippingFlag.value = false
getOrderList()
}
}
}
methods: {
getShippingLabel() {
const item =
this.shippingMethod.find((e) => e.value === this.shippingText) ||
this.shippingWay.find((e) => e.value === this.shippingText);
return item ? item.label : "";
},
function navigateToStore(val: any) {
uni.navigateTo({
url: '/pages/product/shopPage?id=' + val.storeId,
})
}
//发票回调 选择发票之后刷新购物车
async callbackInvoice(val) {
this.invoiceFlag = false;
this.receiptList = val;
if (val) {
let submit = {
way: this.routerVal.way,
...this.receiptList,
};
let receipt = await API_Order.getReceipt(submit);
if (receipt.data.success) {
this.shippingFlag = false;
this.getOrderList();
}
function clickToAddress() {
navigateTo(
`/pages/mine/address/address?from=cart&way=${
routerVal.value.way
}&parentOrder=${encodeURIComponent(
JSON.stringify(routerVal.value.parentOrder)
)}`
)
}
function clickToStoreAddress() {
navigateTo(
`/pages/mine/address/storeAddress?from=cart&way=${routerVal.value.way}&storeId=${remarkVal.value[0].storeId}`
)
}
function pintuanWay() {
const { memberId } = routerVal.value.parentOrder
const userInfo = isLogin()
if (memberId) {
endWay.value = userInfo
masterWay.value = routerVal.value.parentOrder
pintuanFlage.value = false
} else {
pintuanFlage.value = true
masterWay.value = userInfo
}
}
function invoice() {
invoiceFlag.value = true
}
function GET_Discount() {
let storeIds: any[] = []
let skus: any[] = []
const selectedCoupon: any[] = []
if (orderMessage.value.platformCoupon) {
selectedCoupon.push(orderMessage.value.platformCoupon.memberCoupon.id)
}
if (
orderMessage.value.storeCoupons &&
Object.keys(orderMessage.value.storeCoupons)[0]
) {
const storeMemberCouponsId = Object.keys(
orderMessage.value.storeCoupons
)[0]
const storeCouponId =
orderMessage.value.storeCoupons[storeMemberCouponsId].memberCoupon.id
selectedCoupon.push(storeCouponId)
}
orderMessage.value.cartList.forEach((item: any) => {
item.skuList.forEach((sku: any) => {
storeIds.push(sku.storeId)
skus.push(sku.goodsSku.id)
})
})
storeIds = Array.from(new Set(storeIds))
skus = Array.from(new Set(skus))
uni.setStorage({
key: 'totalPrice',
data: orderMessage.value.priceDetailDTO.goodsPrice,
})
navigateTo(
`/pages/cart/coupon/index?way=${routerVal.value.way}&storeId=${storeIds}&skuId=${skus}&selectedCoupon=${selectedCoupon}`
)
}
function navigateTo(url: string) {
uni.navigateTo({
url,
})
}
function createTradeFun() {
proxy!.$u.throttle(() => {
if (shippingText.value === 'SELF_PICK_UP') {
if (!storeAddress.value.id) {
uni.showToast({
title: '请选择提货点',
duration: 2000,
icon: 'none',
})
return false
}
},
// 跳转到店铺
navigateToStore(val) {
uni.navigateTo({
url: "/pages/product/shopPage?id=" + val.storeId,
});
},
// 点击跳转地址
clickToAddress() {
this.navigateTo(
`/pages/mine/address/address?from=cart&way=${
this.routerVal.way
}&parentOrder=${encodeURIComponent(
JSON.stringify(this.routerVal.parentOrder)
)}`
);
},
clickToStoreAddress() {
this.navigateTo(
`/pages/mine/address/storeAddress?from=cart&way=${this.routerVal.way}&storeId=${this.remarkVal[0].storeId}`
);
},
// 判断团长以及团员信息
pintuanWay() {
const { memberId } = this.routerVal.parentOrder;
const userInfo = this.isLogin();
if (memberId) {
this.endWay = userInfo;
this.masterWay = this.routerVal.parentOrder;
this.pintuanFlage = false;
} else {
this.pintuanFlage = true;
this.masterWay = userInfo;
} else if (
shippingText.value === 'LOGISTICS' &&
orderMessage.value.cartTypeEnum !== 'VIRTUAL'
) {
if (!address.value.id) {
uni.showToast({
title: '请选择地址',
duration: 2000,
icon: 'none',
})
return false
}
},
// 判断发票
invoice() {
this.invoiceFlag = true;
},
}
// 领取优惠券
GET_Discount() {
// 循环店铺id,商品id获取优惠券
let store = [];
let skus = [];
let selectedCoupon = [];
if (this.orderMessage.platformCoupon)
selectedCoupon.push(this.orderMessage.platformCoupon.memberCoupon.id);
if (
this.orderMessage.storeCoupons &&
Object.keys(this.orderMessage.storeCoupons)[0]
) {
let storeMemberCouponsId = Object.keys(
this.orderMessage.storeCoupons
)[0];
let storeCouponId =
this.orderMessage.storeCoupons[storeMemberCouponsId].memberCoupon.id;
selectedCoupon.push(storeCouponId);
}
this.orderMessage.cartList.forEach((item) => {
item.skuList.forEach((sku) => {
store.push(sku.storeId);
skus.push(sku.goodsSku.id);
});
});
store = Array.from(new Set(store));
skus = Array.from(new Set(skus));
uni.setStorage({
key: "totalPrice",
data: this.orderMessage.priceDetailDTO.goodsPrice,
});
this.navigateTo(
`/pages/cart/coupon/index?way=${this.routerVal.way}&storeId=${store}&skuId=${skus}&selectedCoupon=${selectedCoupon}`
);
},
let client
// #ifdef H5
client = 'H5'
// #endif
// #ifdef MP-WEIXIN
client = 'WECHAT_MP'
// #endif
// #ifdef APP-PLUS
client = 'APP'
// #endif
/**
* 跳转
*/
navigateTo(url) {
uni.navigateTo({
url,
});
},
const submit: Record<string, any> = {
client,
way: routerVal.value.way,
remark: remarkVal.value,
parentOrderSn: '',
}
if (routerVal.value.parentOrder && routerVal.value.parentOrder.orderSn) {
submit.parentOrderSn = routerVal.value.parentOrder.orderSn
} else {
delete submit.parentOrderSn
}
/**
* 提交订单准备支付
*/
// 创建订单
createTradeFun() {
// 防抖
this.$u.throttle(() => {
if (this.shippingText === "SELF_PICK_UP") {
if (!this.storeAddress.id) {
uni.showToast({
title: "请选择提货点",
duration: 2000,
icon: "none",
});
return false;
}
} else if (this.shippingText === "LOGISTICS" && this.orderMessage.cartTypeEnum !== 'VIRTUAL') {
if (!this.address.id) {
uni.showToast({
title: "请选择地址",
duration: 2000,
icon: "none",
});
return false;
}
}
// 创建订单
let client;
// #ifdef H5
client = "H5";
// #endif
// #ifdef MP-WEIXIN
client = "WECHAT_MP";
// #endif
// #ifdef APP-PLUS
client = "APP";
// #endif
let submit = {
client,
way: this.routerVal.way,
remark: this.remarkVal,
parentOrderSn: "",
};
// 如果是拼团并且当前用户不是团长
this.routerVal.parentOrder && this.routerVal.parentOrder.orderSn
? (submit.parentOrderSn = this.routerVal.parentOrder.orderSn)
: delete submit.parentOrderSn;
/**
* 创建订单
*/
API_Trade.createTrade(submit).then((res) => {
if (res.data.success) {
uni.showToast({
title: "创建订单成功!",
duration: 2000,
icon: "none",
});
// 如果当前价格为0跳转到订单列表
if (this.orderMessage.priceDetailDTO.billPrice == 0) {
uni.navigateTo({
url: "/pages/order/myOrder?status=0",
});
} else {
// #ifdef MP-WEIXIN
// 微信小程序中点击创建订单直接开始支付
this.pay(res.data.result.sn);
// #endif
// #ifndef MP-WEIXIN
this.navigateTo(
`/pages/cart/payment/payOrder?trade_sn=${res.data.result.sn}`
);
// #endif
}
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
}
});
}, 3000);
},
/**
* 微信小程序中直接支付
*/
async pay(sn) {
new LiLiWXPay({
sn: sn,
price: this.orderMessage.priceDetailDTO.billPrice,
}).pay();
},
/**
* 获取用户地址
*/
getUserAddress() {
// 如果没有商品选择地址的话 则选择 默认地址
API_Address.getAddressDefault().then((res) => {
if (res.data.result) {
res.data.result.consigneeAddressPath =
res.data.result.consigneeAddressPath.split(",");
this.address = res.data.result;
}
});
},
// 获取配送列表
async getDistribution() {
let shopRes = await API_Trade.shippingMethodList({
way: this.routerVal.way,
});
let shopList;
if (shopRes.data.success) {
shopList = shopRes.data.result;
let way = [];
console.log(shopList);
this.shippingWay.forEach((item) => {
shopList.forEach((child) => {
if (item.value == child) {
way.push(item);
}
});
});
this.shippingMethod = way;
if (way.length && !way.some((item) => item.value === this.shippingText)) {
this.shippingText = way[0].value;
}
}
},
// 选择配送
async confirmDistribution(val) {
const selected = val?.value?.[0] || val?.[0];
if (!selected?.value) {
return;
}
let res = await API_Trade.setShipMethod({
shippingMethod: selected.value,
way: this.routerVal.way,
});
this.shippingText = selected.value;
API_Trade.createTrade(submit).then((res) => {
if (res.data.success) {
this.getOrderList();
}
},
// 获取结算参数
getOrderList() {
this.notSupportFreight = [];
this.notSupportFreightNoticeText = "";
return API_Trade.getCheckoutParams(this.routerVal.way).then((res) => {
// 获取结算参数 进行首次判断
this.originOrderData = this.orderMessage
? JSON.parse(JSON.stringify(this.orderMessage))
: null;
if (
!res.data.result.checkedSkuList ||
res.data.result.checkedSkuList.length === 0
) {
if (!this.originOrderData?.checkedSkuList?.length) {
uni.switchTab({
url: "/pages/tabbar/cart/cartList",
});
}
}
if (res.data.result.skuList.length <= 0) {
if (!this.originOrderData?.skuList?.length) {
uni.navigateTo({
url: "/pages/order/myOrder?status=0",
});
}
}
let repeatData;
res.data.result.cartList.forEach((item, index) => {
// 如果已经写过备注信息的话赋值
repeatData = {
remark: this.remarkFlag
? this.remark[index].storeId == item.storeId
? this.remark[index].remark
: item.remark
: item.remark,
storeId: item.storeId,
};
this.remarkVal[index] = repeatData;
});
this.orderMessage = res.data.result;
/**
* 为了避免路径传值在h5中超出限制问题
* 这块将可用的优惠券以及不可用的优惠券放入到vuex里面进行存储
*/
this.$store.state.canUseCoupons = res.data.result.canUseCoupons;
this.$store.state.cantUseCoupons = res.data.result.cantUseCoupons;
if (!res.data.result.memberAddress) {
// 获取会员默认地址
this.getUserAddress();
uni.showToast({
title: '创建订单成功!',
duration: 2000,
icon: 'none',
})
if (orderMessage.value.priceDetailDTO.billPrice == 0) {
uni.navigateTo({
url: '/pages/order/myOrder?status=0',
})
} else {
this.address = res.data.result.memberAddress;
res.data.result.memberAddress.consigneeAddressPath =
res.data.result.memberAddress.consigneeAddressPath.split(",");
}
if (res.data.result.storeAddress) {
this.storeAddress = res.data.result.storeAddress;
console.log("storeAddress", this.storeAddress);
}
if (
res.data.result.notSupportFreight &&
res.data.result.notSupportFreight.length != 0
) {
this.notSupportFreight = res.data.result.notSupportFreight;
this.notSupportFreightNoticeText = "以下商品超出配送范围:";
res.data.result.notSupportFreight.forEach((item) => {
this.notSupportFreightNoticeText += item.goodsSku.goodsName;
});
}
});
},
// #ifdef MP-WEIXIN
pay(res.data.result.sn)
// #endif
//
},
};
// #ifndef MP-WEIXIN
navigateTo(
`/pages/cart/payment/payOrder?trade_sn=${res.data.result.sn}`
)
// #endif
}
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
}
})
}, 3000)
}
async function pay(sn: string) {
new LiLiWXPay({
sn,
price: orderMessage.value.priceDetailDTO.billPrice,
}).pay()
}
function getUserAddress() {
API_Address.getAddressDefault().then((res) => {
if (res.data.result) {
res.data.result.consigneeAddressPath =
res.data.result.consigneeAddressPath.split(',')
address.value = res.data.result
}
})
}
async function getDistribution() {
const shopRes = await API_Trade.shippingMethodList({
way: routerVal.value.way,
})
if (shopRes.data.success) {
const shopList = shopRes.data.result
const way: ShippingOption[] = []
console.log(shopList)
shippingWay.forEach((item) => {
shopList.forEach((child: string) => {
if (item.value == child) {
way.push(item)
}
})
})
shippingMethod.value = way
if (way.length && !way.some((item) => item.value === shippingText.value)) {
shippingText.value = way[0].value
}
}
}
async function confirmDistribution(val: any) {
const selected = val?.value?.[0] || val?.[0]
if (!selected?.value) {
return
}
const res = await API_Trade.setShipMethod({
shippingMethod: selected.value,
way: routerVal.value.way,
})
shippingText.value = selected.value
if (res.data.success) {
getOrderList()
}
}
function getOrderList() {
notSupportFreight.value = []
notSupportFreightNoticeText.value = ''
return API_Trade.getCheckoutParams(routerVal.value.way).then((res) => {
originOrderData.value = orderMessage.value
? JSON.parse(JSON.stringify(orderMessage.value))
: null
if (
!res.data.result.checkedSkuList ||
res.data.result.checkedSkuList.length === 0
) {
if (!originOrderData.value?.checkedSkuList?.length) {
uni.switchTab({
url: '/pages/tabbar/cart/cartList',
})
}
}
if (res.data.result.skuList.length <= 0) {
if (!originOrderData.value?.skuList?.length) {
uni.navigateTo({
url: '/pages/order/myOrder?status=0',
})
}
}
let repeatData
res.data.result.cartList.forEach((item: any, index: number) => {
repeatData = {
remark: remarkFlag.value
? remark.value[index].storeId == item.storeId
? remark.value[index].remark
: item.remark
: item.remark,
storeId: item.storeId,
}
remarkVal.value[index] = repeatData
})
orderMessage.value = res.data.result
;(store.state as any).canUseCoupons = res.data.result.canUseCoupons
;(store.state as any).cantUseCoupons = res.data.result.cantUseCoupons
if (!res.data.result.memberAddress) {
getUserAddress()
} else {
address.value = res.data.result.memberAddress
res.data.result.memberAddress.consigneeAddressPath =
res.data.result.memberAddress.consigneeAddressPath.split(',')
}
if (res.data.result.storeAddress) {
storeAddress.value = res.data.result.storeAddress
console.log('storeAddress', storeAddress.value)
}
if (
res.data.result.notSupportFreight &&
res.data.result.notSupportFreight.length != 0
) {
notSupportFreight.value = res.data.result.notSupportFreight
notSupportFreightNoticeText.value = '以下商品超出配送范围:'
res.data.result.notSupportFreight.forEach((item: any) => {
notSupportFreightNoticeText.value += item.goodsSku.goodsName
})
}
})
}
</script>
<style scoped>
page {

View File

@@ -74,185 +74,128 @@
</view>
</template>
<script>
import { getReceiptDetail } from "@/api/order.js";
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getReceiptDetail } from '@/api/order.js'
import { unitPrice } from '@/utils/filters.js'
export default {
data() {
return {
order: {},
title_type: "",
showInvoicePopup: false,
};
},
onLoad(options) {
this.loadData(options.id);
},
methods: {
loadData(id) {
getReceiptDetail(id).then((res) => {
let order = res.data.result;
this.order = order;
this.title_type = order.companyName || order.taxpayerId ? "单位" : "个人";
});
},
getTitleNameValue() {
return this.title_type === "单位"
? this.order.companyName || "-"
: this.order.personalName || "-";
},
viewInvoice() {
if (!this.order.invoiceAddress) {
const order = ref<Record<string, any>>({})
const title_type = ref('')
const showInvoicePopup = ref(false)
onLoad((options) => {
loadData(options.id)
})
function loadData(id: string) {
getReceiptDetail(id).then((res) => {
const result = res.data.result
order.value = result
title_type.value = result.companyName || result.taxpayerId ? '单位' : '个人'
})
}
function getTitleNameValue() {
return title_type.value === '单位'
? order.value.companyName || '-'
: order.value.personalName || '-'
}
function viewInvoice() {
if (!order.value.invoiceAddress) {
uni.showToast({
title: '暂无发票地址',
duration: 2000,
icon: 'none',
})
return
}
if (isImageInvoice()) {
showInvoicePopup.value = true
return
}
// #ifdef APP-PLUS
plus.runtime.openURL(order.value.invoiceAddress)
// #endif
// #ifndef APP-PLUS
uni.navigateTo({
url: '/pages/tabbar/home/web-view?src=' + encodeURIComponent(order.value.invoiceAddress),
})
// #endif
}
function isImageInvoice() {
const url = (order.value.invoiceAddress || '').split('?')[0].toLowerCase()
return /\.(png|jpe?g|gif|bmp|webp)$/.test(url)
}
function previewImageInvoice() {
if (!order.value.invoiceAddress) return
uni.previewImage({
current: 0,
urls: [order.value.invoiceAddress],
})
}
function downloadImageInvoice() {
if (!order.value.invoiceAddress) {
uni.showToast({
title: '暂无发票可下载',
duration: 2000,
icon: 'none',
})
return
}
uni.downloadFile({
url: order.value.invoiceAddress,
success: (res) => {
if (res.statusCode !== 200) {
uni.showToast({
title: "暂无发票地址",
title: '下载失败',
duration: 2000,
icon: "none",
});
return;
icon: 'none',
})
return
}
if (this.isImageInvoice()) {
this.showInvoicePopup = true;
return;
}
// #ifdef APP-PLUS
plus.runtime.openURL(this.order.invoiceAddress);
const tempFilePath = res.tempFilePath
// #ifdef H5
const link = document.createElement('a')
link.href = tempFilePath || order.value.invoiceAddress
link.download = 'invoice'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// #endif
// #ifndef APP-PLUS
uni.navigateTo({
url:
"/pages/tabbar/home/web-view?src=" +
encodeURIComponent(this.order.invoiceAddress),
});
// #endif
},
isImageInvoice() {
const url = (this.order.invoiceAddress || "").split("?")[0].toLowerCase();
return /\.(png|jpe?g|gif|bmp|webp)$/.test(url);
},
previewImageInvoice() {
if (!this.order.invoiceAddress) {
return;
}
uni.previewImage({
current: 0,
urls: [this.order.invoiceAddress],
});
},
downloadImageInvoice() {
if (!this.order.invoiceAddress) {
uni.showToast({
title: "暂无发票可下载",
duration: 2000,
icon: "none",
});
return;
}
uni.downloadFile({
url: this.order.invoiceAddress,
success: (res) => {
if (res.statusCode !== 200) {
uni.showToast({
title: "下载失败",
duration: 2000,
icon: "none",
});
return;
}
const tempFilePath = res.tempFilePath;
// #ifdef H5
const link = document.createElement("a");
link.href = tempFilePath || this.order.invoiceAddress;
link.download = "invoice";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// #endif
// #ifndef H5
uni.saveImageToPhotosAlbum({
filePath: tempFilePath,
success: () => {
uni.showToast({
title: "发票已保存到相册",
duration: 2000,
icon: "none",
});
},
fail: () => {
uni.showToast({
title: "保存失败",
duration: 2000,
icon: "none",
});
},
});
// #endif
// #ifndef H5
uni.saveImageToPhotosAlbum({
filePath: tempFilePath,
success: () => {
uni.showToast({
title: '发票已保存到相册',
duration: 2000,
icon: 'none',
})
},
fail: () => {
uni.showToast({
title: "下载失败",
title: '保存失败',
duration: 2000,
icon: "none",
});
icon: 'none',
})
},
});
})
// #endif
},
/**
* 点击图片放大或保存
*/
preview() {
//预览发票
if (this.order.elec_file_list.length) {
uni.previewImage({
current: 0,
urls: this.order.elec_file_list,
longPressActions: {
itemList: ["发送给朋友", "保存图片", "收藏"],
success: function (data) {},
fail: function (err) {},
},
});
} else {
uni.showToast({
title: "暂无发票可预览",
duration: 2000,
icon: "none",
});
}
fail: () => {
uni.showToast({
title: '下载失败',
duration: 2000,
icon: 'none',
})
},
download() {
//下载发票
let _this = this;
if (this.order.elec_file_list.length) {
this.order.elec_file_list.forEach((item) => {
uni.downloadFile({
url: item,
success: (res) => {
if (res.statusCode === 200) {
let tempFilePath = res.tempFilePath;
uni.saveFile({
tempFilePath: tempFilePath,
success: function (res) {
uni.showToast({
title: "发票已下载到" + res.savedFilePath,
duration: 2000,
icon: "none",
});
},
});
}
},
});
});
} else {
uni.showToast({
title: "暂无发票可下载",
duration: 2000,
icon: "none",
});
}
},
},
};
})
}
</script>
<style lang="scss" scoped>

View File

@@ -92,340 +92,355 @@
</div>
</u-popup>
</template>
<script>
export default {
props: ["res"],
computed: {
isSpecialInvoice() {
return this.getActiveTitle(this.invoiceType) === "增值税专用发票";
},
titleName: {
get() {
return this.isUnitTitle()
? this.submitData.companyName
: this.submitData.personalName;
},
set(value) {
if (this.isUnitTitle()) {
this.submitData.companyName = value;
} else {
this.submitData.personalName = value;
}
this.syncReceiptTitle();
},
},
<script setup lang="ts">
import { ref, reactive, computed, watch, onMounted, getCurrentInstance } from 'vue'
interface InvoiceOption {
title: string
active: boolean
disabled?: boolean
}
interface SubmitData {
receiptTitle: string
receiptType: string
personalName: string
companyName: string
taxpayerId: string
receiptContent: string
companyAddress: string
companyPhone: string
bankName: string
bankAccount: string
receiptPhone: string
receiptEmail: string
}
const props = defineProps<{
res?: Record<string, any>
}>()
const emit = defineEmits<{
callbackInvoice: [val: SubmitData | boolean]
}>()
const { proxy } = getCurrentInstance()!
const shouldClearOnTypeChange = ref(false)
const taxpayerFlag = ref(false)
const submitData = reactive<SubmitData>({
receiptTitle: '',
receiptType: '1',
personalName: '',
companyName: '',
taxpayerId: '',
receiptContent: '',
companyAddress: '',
companyPhone: '',
bankName: '',
bankAccount: '',
receiptPhone: '',
receiptEmail: '',
})
const show = ref(true)
const title = ref('')
const tips =
'电子发票即电子增值税发票,是税局认可的有效凭证,其法律效力、基本用途及使用规定同纸质发票。'
const invoiceType = reactive<InvoiceOption[]>([
{ title: '电子普通发票', active: true },
{ title: '增值税专用发票', active: false },
])
const invoiceHeader = reactive<InvoiceOption[]>([
{ title: '个人', active: false },
{ title: '单位', active: false },
])
const goodsType = reactive<InvoiceOption[]>([
{ title: '商品明细', active: false },
{ title: '商品类别', active: false },
])
const isSpecialInvoice = computed(
() => getActiveTitle(invoiceType) === '增值税专用发票'
)
const titleName = computed({
get() {
return isUnitTitle() ? submitData.companyName : submitData.personalName
},
watch: {
invoiceType: {
handler(val) {
const currentType = this.getActiveTitle(val);
const nextReceiptType =
currentType === "增值税专用发票" ? "2" : "1";
const previousReceiptType = this.submitData.receiptType;
this.submitData.receiptType = nextReceiptType;
if (
this.shouldClearOnTypeChange &&
previousReceiptType &&
previousReceiptType !== nextReceiptType
) {
this.clearInvoiceInfo();
}
if (currentType === "增值税专用发票") {
this.setActiveByTitle(this.invoiceHeader, "单位");
this.setActiveByTitle(this.goodsType, "商品明细");
this.title = "单位";
this.taxpayerFlag = true;
this.submitData.receiptContent = "商品明细";
this.syncReceiptTitle();
} else {
this.setActiveByTitle(this.invoiceHeader, "个人");
this.setActiveByTitle(this.goodsType, "商品明细");
this.title = "个人";
this.taxpayerFlag = false;
this.submitData.receiptContent = "商品明细";
this.syncReceiptTitle();
}
this.shouldClearOnTypeChange = false;
},
deep: true,
},
invoiceHeader: {
handler(val) {
if (this.isSpecialInvoice) {
this.title = "单位";
this.taxpayerFlag = true;
return;
}
this.title = this.getActiveTitle(val) || "个人";
this.taxpayerFlag = this.title == "单位";
if (!this.taxpayerFlag) {
this.submitData.taxpayerId = "";
}
this.syncReceiptTitle();
},
deep: true,
},
goodsType: {
handler(val) {
this.submitData.receiptContent = val.filter((item) => {
return item.active == true;
})[0].title;
},
deep: true,
},
},
data() {
return {
shouldClearOnTypeChange: false,
taxpayerFlag: false,
submitData: {
receiptTitle: "", //发票抬头
receiptType: "1", // 发票类型
personalName: "",
companyName: "",
taxpayerId: "", //纳税人
receiptContent: "",
companyAddress: "", //单位地址
companyPhone: "", //单位电话
bankName: "", //开户银行
bankAccount: "", //银行账号
receiptPhone: "", //收票人手机
receiptEmail: "", //收票人邮箱
},
show: true,
title: "",
tips:
"电子发票即电子增值税发票,是税局认可的有效凭证,其法律效力、基本用途及使用规定同纸质发票。",
// 发票类型
invoiceType: [
{
title: "电子普通发票",
active: true,
},
{
title: "增值税专用发票",
active: false,
},
],
// 发票抬头
invoiceHeader: [
{
title: "个人",
active: false,
},
{
title: "单位",
active: false,
},
],
// 商品类型
goodsType: [
{
title: "商品明细",
active: false,
},
{
title: "商品类别",
active: false,
},
],
};
},
mounted() {
if (this.res) {
this.submitData.receiptType = this.normalizeReceiptType(this.res.receiptType);
this.submitData.personalName =
this.res.personalName ||
(!this.res.companyName && !this.res.taxpayerId ? this.res.receiptTitle || "" : "");
this.submitData.companyName =
this.res.companyName ||
(this.res.taxpayerId ? this.res.receiptTitle || "" : "");
this.submitData.taxpayerId = this.res.taxpayerId; //纳税人
this.submitData.receiptContent = this.res.receiptContent;
this.submitData.companyAddress = this.res.companyAddress || "";
this.submitData.companyPhone = this.res.companyPhone || "";
this.submitData.bankName = this.res.bankName || "";
this.submitData.bankAccount = this.res.bankAccount || "";
this.submitData.receiptPhone = this.res.receiptPhone || "";
this.submitData.receiptEmail = this.res.receiptEmail || "";
if (this.submitData.receiptType === "2") {
this.setActiveByTitle(this.invoiceType, "增值税专用发票");
this.setActiveByTitle(this.invoiceHeader, "单位");
this.setActiveByTitle(this.goodsType, "商品明细");
} else {
this.setActiveByTitle(this.invoiceType, "电子普通发票");
this.res.receiptContent == "商品类别"
? this.setActiveByTitle(this.goodsType, "商品类别")
: this.setActiveByTitle(this.goodsType, "商品明细");
this.res.taxpayerId
? this.setActiveByTitle(this.invoiceHeader, "单位")
: this.setActiveByTitle(this.invoiceHeader, "个人");
}
this.syncReceiptTitle();
set(value: string) {
if (isUnitTitle()) {
submitData.companyName = value
} else {
this.setActiveByTitle(this.invoiceType, "电子普通发票");
this.setActiveByTitle(this.invoiceHeader, "个人");
this.setActiveByTitle(this.goodsType, "商品明细");
this.syncReceiptTitle();
submitData.personalName = value
}
syncReceiptTitle()
},
})
watch(
invoiceType,
(val) => {
const currentType = getActiveTitle(val)
const nextReceiptType = currentType === '增值税专用发票' ? '2' : '1'
const previousReceiptType = submitData.receiptType
submitData.receiptType = nextReceiptType
if (
shouldClearOnTypeChange.value &&
previousReceiptType &&
previousReceiptType !== nextReceiptType
) {
clearInvoiceInfo()
}
if (currentType === '增值税专用发票') {
setActiveByTitle(invoiceHeader, '单位')
setActiveByTitle(goodsType, '商品明细')
title.value = '单位'
taxpayerFlag.value = true
submitData.receiptContent = '商品明细'
syncReceiptTitle()
} else {
setActiveByTitle(invoiceHeader, '个人')
setActiveByTitle(goodsType, '商品明细')
title.value = '个人'
taxpayerFlag.value = false
submitData.receiptContent = '商品明细'
syncReceiptTitle()
}
shouldClearOnTypeChange.value = false
},
methods: {
normalizeReceiptType(type) {
return type === "2" || type === "VATOSPECIAL" ? "2" : "1";
},
getActiveTitle(list) {
const current = list.find((item) => item.active);
return current ? current.title : "";
},
setActiveByTitle(list, title) {
list.forEach((item) => {
item.active = item.title === title;
});
},
isUnitTitle() {
return this.isSpecialInvoice || this.title === "单位";
},
syncReceiptTitle() {
this.submitData.receiptTitle = this.isUnitTitle()
? this.submitData.companyName
: this.submitData.personalName;
},
clearInvoiceInfo() {
this.submitData.receiptTitle = "";
this.submitData.personalName = "";
this.submitData.companyName = "";
this.submitData.taxpayerId = "";
this.submitData.receiptContent = "";
this.submitData.companyAddress = "";
this.submitData.companyPhone = "";
this.submitData.bankName = "";
this.submitData.bankAccount = "";
this.submitData.receiptPhone = "";
this.submitData.receiptEmail = "";
},
handleClickHeader(val, index, arr) {
if (val.disabled) {
return;
}
const previousTitle = this.getActiveTitle(arr);
if (arr === this.invoiceType && previousTitle !== val.title) {
this.shouldClearOnTypeChange = true;
}
arr.forEach((item) => {
item.active = false;
});
val.active = true;
},
/**
* 监听关闭
*/
close(val) {
this.$emit("callbackInvoice", val);
},
submitInvoice() {
/**
* 验证
*/
const {
receiptTitle,
taxpayerId,
companyAddress,
companyPhone,
bankName,
bankAccount,
receiptPhone,
receiptEmail,
} = this.submitData;
this.syncReceiptTitle();
{ deep: true }
)
if (this.$u.test.isEmpty(receiptTitle)) {
uni.showToast({
title: "请您填写发票抬头!",
duration: 2000,
icon: "none",
});
return false;
}
if (
!this.$u.test.isEmpty(receiptTitle) &&
this.$u.test.isEmpty(taxpayerId) &&
this.invoiceHeader[1].active == true
) {
uni.showToast({
title: "请您填写纳税人识别号!",
duration: 2000,
icon: "none",
});
watch(
invoiceHeader,
(val) => {
if (isSpecialInvoice.value) {
title.value = '单位'
taxpayerFlag.value = true
return
}
return false;
}
if (this.isSpecialInvoice && this.$u.test.isEmpty(companyAddress)) {
uni.showToast({
title: "请您填写单位地址!",
duration: 2000,
icon: "none",
});
return false;
}
if (this.isSpecialInvoice && this.$u.test.isEmpty(companyPhone)) {
uni.showToast({
title: "请您填写单位电话!",
duration: 2000,
icon: "none",
});
return false;
}
if (this.isSpecialInvoice && this.$u.test.isEmpty(bankName)) {
uni.showToast({
title: "请您填写开户银行!",
duration: 2000,
icon: "none",
});
return false;
}
if (this.isSpecialInvoice && this.$u.test.isEmpty(bankAccount)) {
uni.showToast({
title: "请您填写银行账号!",
duration: 2000,
icon: "none",
});
return false;
}
if (this.$u.test.isEmpty(receiptPhone)) {
uni.showToast({
title: "请您填写收票人手机!",
duration: 2000,
icon: "none",
});
return false;
}
if (!this.$u.test.mobile(receiptPhone)) {
uni.showToast({
title: "请输入正确的收票人手机号!",
duration: 2000,
icon: "none",
});
return false;
}
if (!this.$u.test.isEmpty(receiptEmail) && !this.$u.test.email(receiptEmail)) {
uni.showToast({
title: "请输入正确的收票人邮箱!",
duration: 2000,
icon: "none",
});
return false;
}
this.show = false;
this.close(this.submitData);
},
title.value = getActiveTitle(val) || '个人'
taxpayerFlag.value = title.value == '单位'
if (!taxpayerFlag.value) {
submitData.taxpayerId = ''
}
syncReceiptTitle()
},
};
{ deep: true }
)
watch(
goodsType,
(val) => {
submitData.receiptContent = val.filter((item) => item.active == true)[0].title
},
{ deep: true }
)
onMounted(() => {
if (props.res) {
submitData.receiptType = normalizeReceiptType(props.res.receiptType)
submitData.personalName =
props.res.personalName ||
(!props.res.companyName && !props.res.taxpayerId
? props.res.receiptTitle || ''
: '')
submitData.companyName =
props.res.companyName ||
(props.res.taxpayerId ? props.res.receiptTitle || '' : '')
submitData.taxpayerId = props.res.taxpayerId
submitData.receiptContent = props.res.receiptContent
submitData.companyAddress = props.res.companyAddress || ''
submitData.companyPhone = props.res.companyPhone || ''
submitData.bankName = props.res.bankName || ''
submitData.bankAccount = props.res.bankAccount || ''
submitData.receiptPhone = props.res.receiptPhone || ''
submitData.receiptEmail = props.res.receiptEmail || ''
if (submitData.receiptType === '2') {
setActiveByTitle(invoiceType, '增值税专用发票')
setActiveByTitle(invoiceHeader, '单位')
setActiveByTitle(goodsType, '商品明细')
} else {
setActiveByTitle(invoiceType, '电子普通发票')
props.res.receiptContent == '商品类别'
? setActiveByTitle(goodsType, '商品类别')
: setActiveByTitle(goodsType, '商品明细')
props.res.taxpayerId
? setActiveByTitle(invoiceHeader, '单位')
: setActiveByTitle(invoiceHeader, '个人')
}
syncReceiptTitle()
} else {
setActiveByTitle(invoiceType, '电子普通发票')
setActiveByTitle(invoiceHeader, '个人')
setActiveByTitle(goodsType, '商品明细')
syncReceiptTitle()
}
})
function normalizeReceiptType(type: string) {
return type === '2' || type === 'VATOSPECIAL' ? '2' : '1'
}
function getActiveTitle(list: InvoiceOption[]) {
const current = list.find((item) => item.active)
return current ? current.title : ''
}
function setActiveByTitle(list: InvoiceOption[], activeTitle: string) {
list.forEach((item) => {
item.active = item.title === activeTitle
})
}
function isUnitTitle() {
return isSpecialInvoice.value || title.value === '单位'
}
function syncReceiptTitle() {
submitData.receiptTitle = isUnitTitle()
? submitData.companyName
: submitData.personalName
}
function clearInvoiceInfo() {
submitData.receiptTitle = ''
submitData.personalName = ''
submitData.companyName = ''
submitData.taxpayerId = ''
submitData.receiptContent = ''
submitData.companyAddress = ''
submitData.companyPhone = ''
submitData.bankName = ''
submitData.bankAccount = ''
submitData.receiptPhone = ''
submitData.receiptEmail = ''
}
function handleClickHeader(
val: InvoiceOption,
_index: number,
arr: InvoiceOption[]
) {
if (val.disabled) {
return
}
const previousTitle = getActiveTitle(arr)
if (arr === invoiceType && previousTitle !== val.title) {
shouldClearOnTypeChange.value = true
}
arr.forEach((item) => {
item.active = false
})
val.active = true
}
function close(val: SubmitData | boolean) {
emit('callbackInvoice', val)
}
function submitInvoice() {
const {
receiptTitle,
taxpayerId,
companyAddress,
companyPhone,
bankName,
bankAccount,
receiptPhone,
receiptEmail,
} = submitData
syncReceiptTitle()
if (proxy.$u.test.isEmpty(receiptTitle)) {
uni.showToast({
title: '请您填写发票抬头!',
duration: 2000,
icon: 'none',
})
return false
}
if (
!proxy.$u.test.isEmpty(receiptTitle) &&
proxy.$u.test.isEmpty(taxpayerId) &&
invoiceHeader[1].active == true
) {
uni.showToast({
title: '请您填写纳税人识别号!',
duration: 2000,
icon: 'none',
})
return false
}
if (isSpecialInvoice.value && proxy.$u.test.isEmpty(companyAddress)) {
uni.showToast({
title: '请您填写单位地址!',
duration: 2000,
icon: 'none',
})
return false
}
if (isSpecialInvoice.value && proxy.$u.test.isEmpty(companyPhone)) {
uni.showToast({
title: '请您填写单位电话!',
duration: 2000,
icon: 'none',
})
return false
}
if (isSpecialInvoice.value && proxy.$u.test.isEmpty(bankName)) {
uni.showToast({
title: '请您填写开户银行!',
duration: 2000,
icon: 'none',
})
return false
}
if (isSpecialInvoice.value && proxy.$u.test.isEmpty(bankAccount)) {
uni.showToast({
title: '请您填写银行账号!',
duration: 2000,
icon: 'none',
})
return false
}
if (proxy.$u.test.isEmpty(receiptPhone)) {
uni.showToast({
title: '请您填写收票人手机!',
duration: 2000,
icon: 'none',
})
return false
}
if (!proxy.$u.test.mobile(receiptPhone)) {
uni.showToast({
title: '请输入正确的收票人手机号!',
duration: 2000,
icon: 'none',
})
return false
}
if (
!proxy.$u.test.isEmpty(receiptEmail) &&
!proxy.$u.test.email(receiptEmail)
) {
uni.showToast({
title: '请输入正确的收票人邮箱!',
duration: 2000,
icon: 'none',
})
return false
}
show.value = false
close(submitData)
}
</script>
<style scoped lang="scss">
.form-item {

View File

@@ -11,7 +11,7 @@
<u-empty text="暂无订单" mode="list"
v-if="tabItem.loaded === true && tabItem.orderList.length === 0"></u-empty>
<!-- 订单列表 -->
<view class="seller-view" :key="oderIndex" v-for="(order, oderIndex) in tabItem.orderList">
<view class="seller-view" :key="orderIndex" v-for="(order, orderIndex) in tabItem.orderList">
<!-- 店铺名称 -->
<view class="seller-info u-flex u-row-between">
<view class="seller-name wes" @click="navigateToStore(order)">
@@ -108,458 +108,339 @@
</view>
</template>
<script>
import uniLoadMore from "@/components/uni-load-more/uni-load-more.vue";
import {
getOrderList,
cancelOrder,
confirmReceipt
} from "@/api/order.js";
import {
getClearReason
} from "@/api/after-sale.js";
import LiLiWXPay from "@/js_sdk/lili-pay/wx-pay.js";
export default {
components: {
uniLoadMore,
},
data() {
return {
lightColor: this.$lightColor,
tabCurrentIndex: 0, //导航栏索引
navList: [
//导航栏list
{
state: 0,
text: "全部",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 1,
text: "待付款",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 2,
text: "待发货",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 3,
text: "待收货",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 4,
text: "已完成",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
{
state: 5,
text: "已取消",
loadStatus: "more",
orderList: [],
pageNumber: 1,
},
],
status: "", //接收导航栏状态
params: {
pageNumber: 1,
pageSize: 10,
tag: "ALL",
},
orderStatus: [
//订单状态
{
orderStatus: "ALL", //全部
},
{
orderStatus: "WAIT_PAY", //代付款
},
{
orderStatus: "WAIT_SHIP",
},
{
orderStatus: "WAIT_ROG", //待收货
},
{
orderStatus: "COMPLETE", //已完成
},
{
orderStatus: "CANCELLED", //已取消
},
{
orderStatus: "STAY_PICKED_UP", //待自提
},
],
cancelShow: false, //是否显示取消
orderSn: "", //ordersn
reason: "", //取消原因
cancelList: [], //取消列表
rogShow: false, //显示是否收货
};
},
<script setup lang="ts">
import { ref, reactive, computed, watch, getCurrentInstance } from 'vue'
import { onLoad, onShow, onPullDownRefresh, onBackPress } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue'
import { getOrderList, cancelOrder, confirmReceipt } from '@/api/order.js'
import { getClearReason } from '@/api/after-sale.js'
import LiLiWXPay from '@/js_sdk/lili-pay/wx-pay.js'
import {
unitPrice,
parseGoodsImageUrl,
tipsToLogin,
orderStatusList,
} from '@/utils/filters.js'
/**
* 跳转到个人中心
*/
onBackPress(e) {
if (e.from == "backbutton") {
uni.switchTab({
url: "/pages/tabbar/user/my",
});
return true; //阻止默认返回行为
}
},
onPullDownRefresh() {
if (this.tabCurrentIndex) {
this.initData(this.tabCurrentIndex);
} else {
this.initData(0);
}
// this.loadData(this.status);
},
onShow() {
if (this.tipsToLogin()) {
if (!this.tabCurrentIndex) {
this.initData(0);
}
}
// this.loadData(this.status);
},
const store = useStore()
const { proxy } = getCurrentInstance()!
const lightColor = computed(() => store.getters.lightColor)
const mainColor = computed(() => store.getters.mainColor)
onLoad(options) {
/**
* 修复app端点击除全部订单外的按钮进入时不加载数据的问题
* 替换onLoad下代码即可
*/
let status = Number(options.status);
this.status = status;
interface NavTabItem {
state: number
text: string
loadStatus: string
orderList: any[]
pageNumber: number
loaded?: boolean
}
this.tabCurrentIndex = status;
// if (status == 0) {
// this.loadData(status);
// }
},
const tabCurrentIndex = ref(0)
const navList = reactive<NavTabItem[]>([
{ state: 0, text: '全部', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 1, text: '待付款', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 2, text: '待发货', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 3, text: '待收货', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 4, text: '已完成', loadStatus: 'more', orderList: [], pageNumber: 1 },
{ state: 5, text: '已取消', loadStatus: 'more', orderList: [], pageNumber: 1 },
])
watch: {
/**监听更改请求数据 */
tabCurrentIndex(val) {
this.params.tag = this.orderStatus[val].orderStatus;
//切换标签页将所有的页数都重置为1
this.navList.forEach((res) => {
res.pageNumber = 1;
res.loadStatus = "more";
res.orderList = [];
});
this.loadData(val);
},
},
methods: {
// 售后
applyService(order) {
uni.navigateTo({
url: `/pages/order/afterSales/afterSales?orderSn=${order.sn}`,
});
},
const status = ref<number>(0)
const params = reactive({
pageNumber: 1,
pageSize: 10,
tag: 'ALL',
})
// 店铺详情
navigateToStore(val) {
uni.navigateTo({
url: "/pages/product/shopPage?id=" + val.storeId,
});
},
renderOrderTag(orderPromotionType) {
switch (orderPromotionType) {
case "NORMAL":
return "";
case "PINTUAN":
return "拼团订单";
break;
case "GIFT":
return "赠品订单";
break;
case "POINTS":
return "积分订单";
break;
case "KANJIA":
return "砍价订单";
default:
return "";
}
},
renderOrderTagType(orderPromotionType) {
switch (orderPromotionType) {
case "PINTUAN":
return "error";
case "GIFT":
return "primary";
case "POINTS":
return "info";
case "KANJIA":
return "warning";
default:
return "info";
}
},
getOrderItemImage(goods, order, index) {
let image = goods.image;
if (!image && order.groupImages) {
const images = String(order.groupImages).split(",");
image = images[index] || images[0];
}
return this.parseGoodsImageUrl(image);
},
/**
* 取消订单
*/
onCancel(sn) {
this.orderSn = sn;
this.cancelShow = true;
uni.showLoading({
title: "加载中",
});
getClearReason().then((res) => {
if (res.data.result.length >= 1) {
this.cancelList = res.data.result;
}
if (this.$store.state.isShowToast) {
uni.hideLoading()
};
});
},
const orderStatus = [
{ orderStatus: 'ALL' },
{ orderStatus: 'WAIT_PAY' },
{ orderStatus: 'WAIT_SHIP' },
{ orderStatus: 'WAIT_ROG' },
{ orderStatus: 'COMPLETE' },
{ orderStatus: 'CANCELLED' },
{ orderStatus: 'STAY_PICKED_UP' },
]
/**
* 初始化数据
*/
initData(index) {
this.navList[index].pageNumber = 1;
this.navList[index].loadStatus = "more";
this.navList[index].orderList = [];
this.loadData(index);
},
const cancelShow = ref(false)
const orderSn = ref('')
const reason = ref('')
const cancelList = ref<any[]>([])
const rogShow = ref(false)
/**
* 等待支付
*/
waitPay(val) {
this.$u.debounce(this.pay(val), 3000);
},
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
/**
* 支付
*/
pay(val) {
if (val.sn) {
// #ifdef MP-WEIXIN
new LiLiWXPay({
sn: val.sn,
price: val.flowPrice,
orderType: "ORDER",
}).pay();
// #endif
// #ifndef MP-WEIXIN
uni.navigateTo({
url: "/pages/cart/payment/payOrder?order_sn=" + val.sn,
});
// #endif
}
},
onBackPress((e) => {
if (e.from == 'backbutton') {
uni.switchTab({ url: '/pages/tabbar/user/my' })
return true
}
return false
})
/**
* 获取订单列表
*/
loadData(index) {
this.params.pageNumber = this.navList[index].pageNumber;
// this.params.tag = this.orderStatus[index].orderStatus;
getOrderList(this.params).then((res) => {
uni.stopPullDownRefresh();
if (!res.data.success) {
this.navList[index].loadStatus = "noMore";
return false;
}
let orderList = res.data.result.records;
if (orderList.length == 0) {
this.navList[index].loadStatus = "noMore";
} else if (orderList.length < 10) {
this.navList[index].loadStatus = "noMore";
}
if (orderList.length > 0) {
this.navList[index].orderList =
this.navList[index].orderList.concat(orderList);
this.navList[index].pageNumber += 1;
}
});
},
//swiper 切换监听
changeTab(e) {
this.tabCurrentIndex = e.target.current;
},
//顶部tab点击
tabClick(index) {
this.tabCurrentIndex = index;
},
//删除订单
deleteOrder(index) {
uni.showLoading({
title: "请稍后",
});
setTimeout(() => {
this.navList[this.tabCurrentIndex].orderList.splice(index, 1);
if (this.$store.state.isShowToast) {
uni.hideLoading()
};
}, 600);
},
//取消订单
cancelOrder(item) {
uni.showLoading({
title: "请稍后",
});
setTimeout(() => {
let {
stateTip,
stateTipColor
} = this.orderStateExp(9);
item = Object.assign(item, {
state: 9,
stateTip,
stateTipColor,
});
onPullDownRefresh(() => {
if (tabCurrentIndex.value) {
initData(tabCurrentIndex.value)
} else {
initData(0)
}
})
//取消订单后删除待付款中该项
let list = this.navList[1].orderList;
let index = list.findIndex((val) => val.id === item.id);
index !== -1 && list.splice(index, 1);
if (this.$store.state.isShowToast) {
uni.hideLoading()
};
}, 600);
},
onShow(() => {
if (tipsToLogin()) {
if (!tabCurrentIndex.value) {
initData(0)
}
}
})
//订单状态文字和颜色
orderStateExp(state) {
let stateTip = "",
stateTipColor = this.$lightColor;
switch (+state) {
case 1:
stateTip = "待付款";
break;
case 2:
stateTip = "待发货";
break;
case 9:
stateTip = "订单已关闭";
stateTipColor = "#909399";
break;
onLoad((options) => {
const statusNum = Number(options?.status)
status.value = statusNum
tabCurrentIndex.value = statusNum
})
//更多自定义
}
return {
stateTip,
stateTipColor,
};
},
watch(tabCurrentIndex, (val) => {
params.tag = orderStatus[val].orderStatus
navList.forEach((res) => {
res.pageNumber = 1
res.loadStatus = 'more'
res.orderList = []
})
loadData(val)
})
/**
* 跳转到订单详情
*/
navigateToOrderDetail(sn) {
uni.navigateTo({
url: "./orderDetail?sn=" + sn,
});
},
function applyService(order: any) {
uni.navigateTo({
url: `/pages/order/afterSales/afterSales?orderSn=${order.sn}`,
})
}
/**
* 选择取消原因
*/
reasonChange(reason) {
this.reason = reason;
},
function navigateToStore(val: any) {
uni.navigateTo({
url: '/pages/product/shopPage?id=' + val.storeId,
})
}
/**
* 提交取消订单(未付款)
*/
submitCancel() {
cancelOrder(this.orderSn, {
reason: this.reason
}).then((res) => {
if (res.data.success) {
uni.showToast({
title: "订单已取消",
duration: 2000,
icon: "none",
});
this.initData(this.tabCurrentIndex);
function renderOrderTag(orderPromotionType: string) {
switch (orderPromotionType) {
case 'NORMAL':
return ''
case 'PINTUAN':
return '拼团订单'
case 'GIFT':
return '赠品订单'
case 'POINTS':
return '积分订单'
case 'KANJIA':
return '砍价订单'
default:
return ''
}
}
this.cancelShow = false;
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
this.cancelShow = false;
}
});
},
function renderOrderTagType(orderPromotionType: string) {
switch (orderPromotionType) {
case 'PINTUAN':
return 'error'
case 'GIFT':
return 'primary'
case 'POINTS':
return 'info'
case 'KANJIA':
return 'warning'
default:
return 'info'
}
}
/**
* 确认收货显示
*/
onRog(sn) {
this.orderSn = sn;
this.rogShow = true;
},
function getOrderItemImage(goods: any, order: any, index: number) {
let image = goods.image
if (!image && order.groupImages) {
const images = String(order.groupImages).split(',')
image = images[index] || images[0]
}
return parseGoodsImageUrl(image)
}
/**
* 点击确认收货
*/
confirmRog() {
confirmReceipt(this.orderSn).then((res) => {
if (res.data.code == 200) {
uni.showToast({
title: "已确认收货",
duration: 2000,
icon: "none",
});
this.initData(this.tabCurrentIndex);
this.rogShow = false;
}
});
},
function onCancel(sn: string) {
orderSn.value = sn
cancelShow.value = true
uni.showLoading({ title: '加载中' })
getClearReason().then((res) => {
if (res.data.result.length >= 1) {
cancelList.value = res.data.result
}
hideLoadingIfNeeded()
})
}
/**
* 评价商品
*/
onComment(sn) {
uni.navigateTo({
url: "./evaluate/myEvaluate",
});
},
function initData(index: number) {
navList[index].pageNumber = 1
navList[index].loadStatus = 'more'
navList[index].orderList = []
loadData(index)
}
/**
* 重新购买
*/
reBuy(order) {
console.log(order);
return;
uni.navigateTo({
url: "/pages/product/goods?id=" + order.id + "&goodsId=" + order.goodsId,
});
},
},
};
function waitPay(val: any) {
proxy?.$u?.debounce?.(() => pay(val), 3000, true)?.()
}
function pay(val: any) {
if (val.sn) {
// #ifdef MP-WEIXIN
new LiLiWXPay({
sn: val.sn,
price: val.flowPrice,
orderType: 'ORDER',
}).pay()
// #endif
// #ifndef MP-WEIXIN
uni.navigateTo({
url: '/pages/cart/payment/payOrder?order_sn=' + val.sn,
})
// #endif
}
}
function loadData(index: number) {
params.pageNumber = navList[index].pageNumber
getOrderList(params).then((res) => {
uni.stopPullDownRefresh()
if (!res.data.success) {
navList[index].loadStatus = 'noMore'
return false
}
const records = res.data.result.records
if (records.length == 0) {
navList[index].loadStatus = 'noMore'
} else if (records.length < 10) {
navList[index].loadStatus = 'noMore'
}
if (records.length > 0) {
navList[index].orderList = navList[index].orderList.concat(records)
navList[index].pageNumber += 1
}
})
}
function changeTab(e: any) {
tabCurrentIndex.value = e.target.current
}
function tabClick(index: number) {
tabCurrentIndex.value = index
}
function deleteOrder(index: number) {
uni.showLoading({ title: '请稍后' })
setTimeout(() => {
navList[tabCurrentIndex.value].orderList.splice(index, 1)
hideLoadingIfNeeded()
}, 600)
}
function cancelOrderLocal(item: any) {
uni.showLoading({ title: '请稍后' })
setTimeout(() => {
const { stateTip, stateTipColor } = orderStateExp(9)
Object.assign(item, {
state: 9,
stateTip,
stateTipColor,
})
const list = navList[1].orderList
const idx = list.findIndex((val) => val.id === item.id)
if (idx !== -1) list.splice(idx, 1)
hideLoadingIfNeeded()
}, 600)
}
function orderStateExp(state: number) {
let stateTip = ''
let stateTipColor = lightColor.value
switch (+state) {
case 1:
stateTip = '待付款'
break
case 2:
stateTip = '待发货'
break
case 9:
stateTip = '订单已关闭'
stateTipColor = '#909399'
break
}
return { stateTip, stateTipColor }
}
function navigateToOrderDetail(sn: string) {
uni.navigateTo({
url: './orderDetail?sn=' + sn,
})
}
function reasonChange(val: string) {
reason.value = val
}
function submitCancel() {
cancelOrder(orderSn.value, { reason: reason.value }).then((res) => {
if (res.data.success) {
uni.showToast({
title: '订单已取消',
duration: 2000,
icon: 'none',
})
initData(tabCurrentIndex.value)
cancelShow.value = false
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
cancelShow.value = false
}
})
}
function onRog(sn: string) {
orderSn.value = sn
rogShow.value = true
}
function confirmRog() {
confirmReceipt(orderSn.value).then((res) => {
if (res.data.code == 200) {
uni.showToast({
title: '已确认收货',
duration: 2000,
icon: 'none',
})
initData(tabCurrentIndex.value)
rogShow.value = false
}
})
}
function onComment(_sn: string) {
uni.navigateTo({
url: './evaluate/myEvaluate',
})
}
function reBuy(order: any) {
const goods = order.orderItems?.[0]
if (!goods) return
uni.navigateTo({
url: '/pages/product/goods?id=' + goods.id + '&goodsId=' + goods.goodsId,
})
}
</script>
<style lang="scss" scoped>

View File

@@ -51,7 +51,7 @@
<view class="title">自提点地址:</view>
<view class="value address-line-height">{{ order.storeAddressPath }}</view>
</view>
<view class="order-info-view" @click="callPhone" >
<view class="order-info-view" @click="handleCallPhone" >
<view class="title">联系方式:</view>
<view class="value">{{ order.storeAddressMobile }}<u-icon name='phone-fill' ></u-icon></view>
</view>
@@ -245,287 +245,258 @@
</view>
</template>
<script>
import { getExpress, getPackage } from "@/api/trade.js";
import { cancelOrder, confirmReceipt, getOrderDetail } from "@/api/order.js";
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { getExpress, getPackage } from '@/api/trade.js'
import { cancelOrder, confirmReceipt, getOrderDetail } from '@/api/order.js'
import shares from '@/components/m-share/index'
import { getClearReason } from '@/api/after-sale.js'
import {
unitPrice,
secrecyMobile,
setClipboard,
talkIm,
callPhone,
} from '@/utils/filters.js'
import shares from "@/components/m-share/index"; //分享
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const mainColor = computed(() => store.getters.mainColor)
import { getClearReason } from "@/api/after-sale.js";
const orderStatusMap: Record<string, { title: string; value?: string }> = {
UNPAID: { title: '未付款', value: '商品暂未付款' },
PAID: { title: '已付款', value: '买家已付款' },
UNDELIVERED: { title: '待发货', value: '商品等待发货中' },
PARTS_DELIVERED: { title: '部分发货', value: '商品已部分发货。' },
DELIVERED: { title: '已发货', value: '商品已发货,请您耐心等待' },
CANCELLED: { title: '已取消', value: '订单已取消' },
COMPLETED: { title: '已完成', value: '订单已完成,祝您生活愉快' },
STAY_PICKED_UP: { title: '待自提', value: '商品正在等待提取' },
TAKE: { title: '待核验' },
}
export default {
components: {
shares,
},
data() {
return {
lightColor: this.$lightColor,
logisticsList: "", //物流信息
shareFlag: false, //拼团分享开关
orderStatusMap: {
UNPAID: {
title: "未付款",
value: "商品暂未付款",
},
PAID: {
title: "已付款",
value: "买家已付款",
},
UNDELIVERED: {
title: "待发货",
value: "商品等待发货中",
},
PARTS_DELIVERED: {
title: "部分发货",
value: "商品已部分发货。",
},
DELIVERED: {
title: "已发货",
value: "商品已发货,请您耐心等待",
},
CANCELLED: {
title: "已取消",
value: "订单已取消",
},
COMPLETED: {
title: "已完成",
value: "订单已完成,祝您生活愉快",
},
STAY_PICKED_UP: {
title: "待自提",
value: "商品正在等待提取",
},
TAKE: {
title: "待核验",
},
},
order: {},
cancelShow: false, //取消订单
orderSn: "",
orderGoodsList: "", //订单中商品集合
orderDetail: "", //订单详情信息
sn: "",
cancelList: "",
rogShow: false,
reason: "",
orderPackage:"",
};
},
onLoad(options) {
this.loadData(options.sn);
this.sn = options.sn;
},
methods: {
//获取包裹
async getOrderPackage() {
getPackage(this.order.sn).then(res => {
if (res.data.success) {
this.orderPackage = res.data.result
}
})
},
handleClickDeliver(){
uni.navigateTo({
url: `/pages/order/deliverDetail?order_sn=${this.order.sn}`,
});
},
// 退款状态枚举
refundPriceList(status) {
switch (status) {
case 'ALL_REFUND':
return "全部退款";
case 'PART_REFUND':
return "部分退款";
case 'NO_REFUND':
return "未退款";
case 'REFUNDING':
return "退款中";
default:
return "";
const logisticsList = ref<any>('')
const shareFlag = ref(false)
const order = ref<Record<string, any>>({})
const cancelShow = ref(false)
const orderSn = ref('')
const orderGoodsList = ref<any[]>([])
const orderDetail = ref<Record<string, any>>({})
const sn = ref('')
const cancelList = ref<any[]>([])
const rogShow = ref(false)
const reason = ref('')
const orderPackage = ref<any>('')
function hideLoadingIfNeeded() {
if (store.state.isShowToast) uni.hideLoading()
}
onLoad((options) => {
const orderSnParam = options?.sn || ''
sn.value = orderSnParam
loadData(orderSnParam)
})
function getOrderPackage() {
getPackage(order.value.sn).then((res) => {
if (res.data.success) {
orderPackage.value = res.data.result
}
},
callPhone(){
this.callPhone(this.order.storeAddressMobile )
},
//联系客服
contact(storeId){
this.talkIm(storeId)
},
goToShopPage(val) {
uni.navigateTo({
url: "/pages/product/shopPage?id=" + val.storeId,
});
},
// 获取物流信息
loadLogistics(sn) {
getExpress(sn).then((res) => {
this.logisticsList = res.data.result;
});
},
})
}
// 分享当前拼团信息
inviteGroup() {
this.shareFlag = true;
},
// #TODO 这块需要写一下 目前没有拼团的详细信息
ByUserMessage(order) {
uni.navigateTo({
url:
"/pages/cart/payment/shareOrderGoods?sn=" +
order.sn +
"&sku=" +
this.orderGoodsList[0].skuId +
"&goodsId=" +
this.orderGoodsList[0].goodsId,
});
},
async loadData(sn) {
uni.showLoading({
title: "加载中",
});
getOrderDetail(sn).then((res) => {
const order = res.data.result;
this.order = order.order;
this.orderGoodsList = order.orderItems;
this.orderDetail = res.data.result;
if (this.order.deliveryMethod === 'LOGISTICS') {
this.loadLogistics(sn);
this.getOrderPackage();
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
function handleClickDeliver() {
uni.navigateTo({
url: `/pages/order/deliverDetail?order_sn=${order.value.sn}`,
})
}
},
onReceipt(val) {
uni.navigateTo({
url: "/pages/order/invoice/invoiceDetail?id=" + val.id,
});
},
gotoGoodsDetail(sku) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId}`,
});
},
onCopy(sn) {
setClipboard(sn)
},
function refundPriceList(status: string) {
switch (status) {
case 'ALL_REFUND':
return '全部退款'
case 'PART_REFUND':
return '部分退款'
case 'NO_REFUND':
return '未退款'
case 'REFUNDING':
return '退款中'
default:
return ''
}
}
//售后按钮
onAfterSales(sn, sku) {
uni.navigateTo({
url: `./afterSales/afterSalesSelect?sn=${sn}&sku=${encodeURIComponent(
JSON.stringify(sku)
)}`,
});
},
// 去支付
toPay(val) {
val.sn
? uni.navigateTo({
url: "/pages/cart/payment/payOrder?order_sn=" + val.sn,
})
: false;
}, //删除订单
deleteOrder(index) {
uni.showLoading({
title: "请稍后",
});
setTimeout(() => {
this.navList[this.tabCurrentIndex].orderList.splice(index, 1);
if (this.$store.state.isShowToast){ uni.hideLoading() };
}, 600);
},
//取消订单
onCancel(sn) {
this.orderSn = sn;
function handleCallPhone() {
if (order.value.storeAddressMobile) {
callPhone(order.value.storeAddressMobile)
}
}
uni.showLoading({
title: "加载中",
});
getClearReason().then((res) => {
if (res.data.result.length >= 1) {
this.cancelList = res.data.result;
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
function contact(storeId: string) {
talkIm(storeId)
}
this.cancelShow = true;
},
function goToShopPage(val: any) {
uni.navigateTo({
url: '/pages/product/shopPage?id=' + val.storeId,
})
}
//提交取消订单(未付款)
submitCancel() {
cancelOrder(this.orderSn, { reason: this.reason }).then((res) => {
if (res.data.success) {
uni.showToast({
title: "已取消",
duration: 2000,
icon: "none",
});
this.cancelShow = false;
setTimeout(() => {
uni.reLaunch({
url: "/pages/order/myOrder?status=0",
});
}, 500);
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: "none",
});
this.cancelShow = false;
}
});
},
function loadLogistics(orderSnParam: string) {
getExpress(orderSnParam).then((res) => {
logisticsList.value = res.data.result
})
}
//确认收货
onRog(sn) {
this.orderSn = sn;
this.rogShow = true;
},
confirmRog() {
confirmReceipt(this.orderSn).then((res) => {
if (res.data.success) {
uni.showToast({
title: "已确认收货",
duration: 2000,
icon: "none",
});
this.rogShow = false;
this.loadData(this.sn);
}
});
},
//评价商品
onComment(sn) {
uni.navigateTo({
url: "./evaluate/myEvaluate",
});
}, //查看物流
onLogistics(order) {
uni.navigateTo({
url:
"/pages/mine/msgTips/packageMsg/logisticsDetail?logi_id=" +
order.logi_id +
"&ship_no=" +
order.ship_no +
"&order_sn=" +
order.sn,
});
},
function inviteGroup() {
shareFlag.value = true
}
//选择取消原因
reasonChange(reason) {
this.reason = reason;
},
reBuy(order) {
uni.navigateTo({
url:
"/pages/product/goods?id=" + order.id + "&goodsId=" + order.goodsId,
});
},
},
};
function ByUserMessage(orderItem: any) {
uni.navigateTo({
url:
'/pages/cart/payment/shareOrderGoods?sn=' +
orderItem.sn +
'&sku=' +
orderGoodsList.value[0].skuId +
'&goodsId=' +
orderGoodsList.value[0].goodsId,
})
}
function loadData(orderSnParam: string) {
uni.showLoading({ title: '加载中' })
getOrderDetail(orderSnParam).then((res) => {
const result = res.data.result
order.value = result.order
orderGoodsList.value = result.orderItems
orderDetail.value = result
if (order.value.deliveryMethod === 'LOGISTICS') {
loadLogistics(orderSnParam)
getOrderPackage()
}
hideLoadingIfNeeded()
})
}
function onReceipt(val: any) {
uni.navigateTo({
url: '/pages/order/invoice/invoiceDetail?id=' + val.id,
})
}
function gotoGoodsDetail(sku: any) {
uni.navigateTo({
url: `/pages/product/goods?id=${sku.skuId}&goodsId=${sku.goodsId}`,
})
}
function onCopy(orderSnText: string) {
setClipboard(orderSnText)
}
function onAfterSales(orderSnText: string, sku: any) {
uni.navigateTo({
url: `./afterSales/afterSalesSelect?sn=${orderSnText}&sku=${encodeURIComponent(
JSON.stringify(sku)
)}`,
})
}
function toPay(val: any) {
if (val.sn) {
uni.navigateTo({
url: '/pages/cart/payment/payOrder?order_sn=' + val.sn,
})
}
}
function onCancel(orderSnText: string) {
orderSn.value = orderSnText
uni.showLoading({ title: '加载中' })
getClearReason().then((res) => {
if (res.data.result.length >= 1) {
cancelList.value = res.data.result
}
hideLoadingIfNeeded()
})
cancelShow.value = true
}
function submitCancel() {
cancelOrder(orderSn.value, { reason: reason.value }).then((res) => {
if (res.data.success) {
uni.showToast({
title: '已取消',
duration: 2000,
icon: 'none',
})
cancelShow.value = false
setTimeout(() => {
uni.reLaunch({
url: '/pages/order/myOrder?status=0',
})
}, 500)
} else {
uni.showToast({
title: res.data.message,
duration: 2000,
icon: 'none',
})
cancelShow.value = false
}
})
}
function onRog(orderSnText: string) {
orderSn.value = orderSnText
rogShow.value = true
}
function confirmRog() {
confirmReceipt(orderSn.value).then((res) => {
if (res.data.success) {
uni.showToast({
title: '已确认收货',
duration: 2000,
icon: 'none',
})
rogShow.value = false
loadData(sn.value)
}
})
}
function onComment(_orderSnText: string) {
uni.navigateTo({
url: './evaluate/myEvaluate',
})
}
function onLogistics(orderItem: any) {
uni.navigateTo({
url:
'/pages/mine/msgTips/packageMsg/logisticsDetail?logi_id=' +
orderItem.logi_id +
'&ship_no=' +
orderItem.ship_no +
'&order_sn=' +
orderItem.sn,
})
}
function reasonChange(val: string) {
reason.value = val
}
function reBuy(orderItem: any) {
uni.navigateTo({
url: '/pages/product/goods?id=' + orderItem.id + '&goodsId=' + orderItem.goodsId,
})
}
</script>
<style lang="scss">

View File

@@ -10,31 +10,23 @@
</view>
</template>
<script>
import { getArticleDetail } from "@/api/article.js";
export default {
data() {
return {
// 用于接收上一级通过路径传输的数据
routers: "",
// 请求文章接口后存储文章信息
articleData: "",
};
},
onLoad(val) {
this.routers = val;
getArticleDetail(val.id).then((res) => {
if (res.data.result) {
// 将请求的文章数据赋值
this.articleData = res.data.result.content;
}
// 修改当前NavigationBar(标题头)为文章头部
uni.setNavigationBarTitle({
title: val.title,
});
});
},
};
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getArticleDetail } from '@/api/article.js'
const articleData = ref('')
onLoad((val) => {
getArticleDetail(val.id).then((res) => {
if (res.data.result) {
articleData.value = res.data.result.content
}
uni.setNavigationBarTitle({
title: val.title,
})
})
})
</script>
<style lang="scss" scoped>
page {

View File

@@ -1,5 +1,5 @@
<template>
<div>
<div class="seller-control" :style="themeStyle">
<u-navbar
:border="false"
:fixed="true"
@@ -18,56 +18,71 @@
</div>
</template>
<script>
import { getCompanyDetail } from "@/api/entry";
import step1 from "./step1";
import step2 from "./step2";
import step3 from "./step3";
export default {
data() {
return {
companyData: "",
current: 1,
};
},
components: {
step1,
step2,
step3,
},
mounted() {
this.init();
},
methods: {
back() {
if (this.current > 1) {
this.current--;
return;
}
uni.navigateBack({
delta: 1,
fail: () => {
uni.switchTab({ url: "/pages/tabbar/home/index" });
},
});
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useStore } from '@/store'
import { getCompanyDetail } from '@/api/entry'
import step1 from './step1.vue'
import step2 from './step2.vue'
import step3 from './step3.vue'
import { getThemeStyle } from '@/utils/theme'
const store = useStore()
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const companyData = ref<any>('')
const current = ref(1)
onMounted(() => {
init()
})
function back() {
if (current.value > 1) {
current.value--
return
}
uni.navigateBack({
delta: 1,
fail: () => {
uni.switchTab({ url: '/pages/tabbar/home/index' })
},
async init(next) {
const res = await getCompanyDetail();
if (res.data.success) {
this.companyData = res.data.result;
next ? this.current++ : "";
}
},
next() {
this.init("next");
},
finished() {
uni.navigateTo({
url: "/pages/passport/entry/seller/index",
});
},
},
};
})
}
async function init(next?: string) {
const res = await getCompanyDetail()
if (res.data.success) {
companyData.value = res.data.result
if (next) {
current.value++
}
}
}
function next() {
init('next')
}
function finished() {
uni.navigateTo({
url: '/pages/passport/entry/seller/index',
})
}
</script>
<style lang="scss" scoped></style>
<style lang="scss">
page {
background: #f7f7f7;
}
</style>
<style lang="scss" scoped>
@import "./entry.scss";
.seller-control {
min-height: 100vh;
background: #f7f7f7;
}
</style>

View File

@@ -0,0 +1,92 @@
@mixin seller-entry-form {
:deep(.u-form-item) {
padding: 0;
margin-top: 24rpx;
}
:deep(.u-form-item__body) {
flex-direction: column !important;
align-items: stretch !important;
padding: 0;
}
:deep(.u-form-item__body__left) {
width: 100% !important;
margin-bottom: 16rpx;
}
:deep(.u-form-item__body__left__content__label) {
display: block;
font-size: 30rpx;
font-weight: 600;
color: #333;
line-height: 1.4;
white-space: normal;
}
:deep(.u-form-item__body__right),
:deep(.u-form-item__body__right__content),
:deep(.u-form-item__body__right__content__slot) {
width: 100%;
flex: none;
min-width: 0;
}
:deep(.u-form-item__body__right__content__slot) {
display: block;
}
.field-input {
width: 100%;
margin-top: 0;
}
.field-row {
display: flex;
align-items: center;
gap: 16rpx;
width: 100%;
}
.field-row .field-input {
flex: 1;
min-width: 0;
}
.field-textarea {
width: 100%;
:deep(.u-textarea) {
background: #fafafa !important;
border-radius: 12rpx;
padding: 20rpx;
box-sizing: border-box;
}
:deep(.u-textarea__field) {
width: 100%;
font-size: 28rpx;
line-height: 1.6;
color: #333;
}
}
}
@mixin seller-entry-submit {
text-align: center;
background: var(--theme-light, #ff6b35);
height: 88rpx;
line-height: 88rpx;
color: #fff;
width: 92%;
margin: 40rpx auto 60rpx;
border-radius: 100px;
font-size: 30rpx;
}
.picker-action {
flex-shrink: 0;
color: var(--theme-light, #ff6b35);
font-size: 28rpx;
white-space: nowrap;
}

View File

@@ -1,18 +1,28 @@
.flag-title {
font-size: 42rpx;
font-weight: bold;
font-size: 30rpx;
font-weight: 600;
color: #333;
line-height: 1.4;
margin-bottom: 8rpx;
}
.submit,
.notice {
font-weight: bold;
font-size: 28rpx;
height: 92rpx;
text-align: center;
letter-spacing: 4rpx;
line-height: 92rpx;
.wrapper {
box-sizing: border-box;
min-height: 100vh;
padding: 20rpx 24rpx 40rpx;
background: #f8f8f8;
}
.column {
padding: 32rpx;
margin-bottom: 20rpx;
background: #fff;
border-radius: 20rpx;
}
.wrapper {
padding:16rpx;
}
.tips {
color: #999;
font-size: 24rpx;
line-height: 1.4;
margin-top: 12rpx;
}

View File

@@ -1,12 +1,12 @@
<template>
<div class="wrapper">
<u-navbar
:border="false"
:fixed="true"
:placeholder="true"
:auto-back="true"
></u-navbar>
<div>
<div class="wrapper" :style="themeStyle">
<u-navbar
:border="false"
:fixed="true"
:placeholder="true"
:auto-back="true"
></u-navbar>
<div class="entry-content">
<div class="title">店铺入驻</div>
<div class="step-list">
<div
@@ -24,144 +24,164 @@
</div>
</template>
<script>
import { getCompanyDetail } from "@/api/entry";
export default {
components: {},
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { tipsToLogin } from '@/utils/filters.js'
import { getCompanyDetail } from '@/api/entry'
import { getThemeStyle } from '@/utils/theme'
data() {
return {
current: 999,
entrySteps: [
{
title: "填写资质信息",
value: "APPLY",
},
{
title: "提交审核",
value: "APPLYING",
},
],
const store = useStore()
storeStatusWay: [
{
title: "申请已通过,请联系管理员",
value: "OPEN",
},
{
title: "店铺已关闭,重申请联系管理员",
value: "CLOSED",
},
{
title: "审核未通过,请修改资质信息",
value: "REFUSED",
},
],
const themeStyle = computed(() => getThemeStyle(store.state.theme))
companyData: "", // 公司信息
};
const current = ref(999)
const companyData = ref<any>('')
const entrySteps = ref([
{
title: '填写资质信息',
value: 'APPLY',
},
onShow() {
if(this.tipsToLogin()){
this.init();
}
{
title: '提交审核',
value: 'APPLYING',
},
])
mounted() {},
const storeStatusWay = [
{
title: '申请已通过,请联系管理员',
value: 'OPEN',
},
{
title: '店铺已关闭,重申请联系管理员',
value: 'CLOSED',
},
{
title: '审核未通过,请修改资质信息',
value: 'REFUSED',
},
]
onLoad(options) {},
methods: {
getEntryNotice() {
uni.navigateTo({
url: "/pages/mine/help/tips?type=STORE_REGISTER",
});
onShow(() => {
if (tipsToLogin()) {
init()
}
})
onLoad(() => {})
function getEntryNotice() {
uni.navigateTo({
url: '/pages/mine/help/tips?type=STORE_REGISTER',
})
}
function keepOn() {
if (companyData.value && companyData.value.storeDisable == 'OPEN') {
uni.showToast({
title: '审核已通过',
icon: 'none',
})
} else {
uni.navigateTo({
url: '/pages/passport/entry/seller/control',
})
}
}
async function init() {
entrySteps.value = [
{
title: '填写资质信息',
value: 'APPLY',
},
keepOn() {
if (this.companyData && this.companyData.storeDisable == "OPEN") {
uni.showToast({
title:"审核已通过",
icon:"none"
})
} else {
uni.navigateTo({
url: "/pages/passport/entry/seller/control",
});
}
{
title: '提交审核',
value: 'APPLYING',
},
async init() {
this.entrySteps = [
{
title: "填写资质信息",
value: "APPLY",
},
{
title: "提交审核",
value: "APPLYING",
},
];
const res = await getCompanyDetail();
if (res.data.success) {
this.companyData = res.data.result;
]
const res = await getCompanyDetail()
if (res.data.success) {
companyData.value = res.data.result
if (this.companyData) {
this.storeStatusWay.forEach((item) => {
if (item.value == this.companyData.storeDisable) {
this.entrySteps.push(item);
}
});
this.current =
this.entrySteps.findIndex(
(item) => item.value == this.companyData.storeDisable
) || 0;
} else {
this.current = 0;
if (companyData.value) {
storeStatusWay.forEach((item) => {
if (item.value == companyData.value.storeDisable) {
entrySteps.value.push(item)
}
}
},
},
};
})
current.value =
entrySteps.value.findIndex(
(item) => item.value == companyData.value.storeDisable
) || 0
} else {
current.value = 0
}
}
}
</script>
<style>
<style lang="scss">
page {
background: #fff;
}
</style>
<style lang="scss" scoped>
@import url("./entry.scss");
.wrapper {
padding: 0 80rpx;
min-height: 100vh;
background: #fff;
box-sizing: border-box;
}
.entry-content {
padding: 32rpx 80rpx calc(40rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.title {
padding-top: calc(104rpx);
font-style: normal;
line-height: 1;
line-height: 1.2;
font-weight: 500;
font-size: 56rpx;
color: #333;
padding-left: 20rpx;
}
.step-list {
margin: 80rpx 0;
}
.step-item {
padding: 30rpx 20rpx;
font-size: 40rpx;
font-weight: bold;
color: #666;
}
.step-list {
margin: 80rpx 0;
overflow: hidden;
}
.active {
color: $light-color;
background: rgba($color: $light-color, $alpha: 0.1);
.step-item.active {
color: var(--theme-light, #ff6b35);
background: var(--theme-light-10, rgba(255, 107, 53, 0.1));
border-radius: 20rpx;
}
.submit,
.notice {
font-weight: bold;
font-size: 28rpx;
height: 92rpx;
text-align: center;
letter-spacing: 4rpx;
line-height: 92rpx;
border-radius: 20rpx;
}
.submit {
color: #fff;
margin-top: 120rpx;
background: rgba($light-color, 0.8);
background: var(--theme-light, #ff6b35);
}
.notice {
margin-top: 40rpx;
color: #333;

View File

@@ -1,113 +1,118 @@
<template>
<div class="wrapper">
<u-form label-width="200" :model="form" ref="uForm">
<div class="wrapper" :style="themeStyle">
<up-form label-position="top" :model="form" ref="uForm">
<div class="column">
<div class="flag-title light-color">基础信息</div>
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="companyName"
label="公司名称"
><u-input
><u-input border="none" class="field-input"
v-model="form.companyName"
:custom-style="defaultInputStyle"
/></u-form-item>
<u-form-item
:custom-style="fieldInputStyle"
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="companyAddressPath"
label="公司所在地"
>
<div @click="showPicker()" style="margin-right: 30rpx;">选择</div>
<u-input
disabled
:custom-style="defaultInputStyle"
v-model="form.companyAddressPath"
/></u-form-item>
<u-form-item
<view class="field-row">
<u-input
disabled
border="none"
class="field-input"
:custom-style="fieldInputStyle"
v-model="form.companyAddressPath"
/>
<view class="picker-action" @click="showPicker()">选择</view>
</view>
</up-form-item>
<up-form-item
required
:border-bottom="false"
prop="companyAddress"
label="公司详细地址"
><u-input
:custom-style="defaultInputStyle"
><u-input border="none" class="field-input"
:custom-style="fieldInputStyle"
v-model="form.companyAddress"
/></u-form-item>
<u-form-item
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="employeeNum"
label="员工人数"
><u-input
><u-input border="none" class="field-input"
type="number"
:custom-style="defaultInputStyle"
:custom-style="fieldInputStyle"
v-model="form.employeeNum"
/></u-form-item>
/></up-form-item>
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="companyPhone"
label="公司电话"
><u-input
><u-input border="none" class="field-input"
type="number"
:custom-style="defaultInputStyle"
:custom-style="fieldInputStyle"
v-model="form.companyPhone"
/></u-form-item>
<u-form-item
/></up-form-item>
<up-form-item
:border-bottom="false"
prop="registeredCapital"
label="注册资金"
required
><u-input
><u-input border="none" class="field-input"
type="number"
:custom-style="defaultInputStyle"
:custom-style="fieldInputStyle"
v-model="form.registeredCapital"
/></u-form-item>
<u-form-item
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="linkName"
label="联系人姓名"
><u-input :custom-style="defaultInputStyle" v-model="form.linkName"
/></u-form-item>
<u-form-item
><u-input border="none" class="field-input" :custom-style="fieldInputStyle" v-model="form.linkName"
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="linkPhone"
label="联系人电话"
><u-input
><u-input border="none" class="field-input"
type="number"
:custom-style="defaultInputStyle"
:custom-style="fieldInputStyle"
v-model="form.linkPhone"
/></u-form-item>
<u-form-item
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="companyEmail"
label="电子邮箱"
><u-input
:custom-style="defaultInputStyle"
><u-input border="none" class="field-input"
:custom-style="fieldInputStyle"
v-model="form.companyEmail"
/></u-form-item>
/></up-form-item>
</div>
<div class="column">
<div class="flag-title light-color">营业执照信息</div>
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="licenseNum"
label="营业执照号"
><u-input :custom-style="defaultInputStyle" v-model="form.licenseNum"
/></u-form-item>
<u-form-item
><u-input border="none" class="field-input" :custom-style="fieldInputStyle" v-model="form.licenseNum"
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="scope"
label="法定经营范围"
><u-input :custom-style="defaultInputStyle" v-model="form.scope"
/></u-form-item>
<u-form-item
><u-input border="none" class="field-input" :custom-style="fieldInputStyle" v-model="form.scope"
/></up-form-item>
<up-form-item
:border-bottom="false"
prop="licencePhoto"
label="营业执照电子版"
@@ -126,25 +131,25 @@
请压缩图片在2M以内确保文字清晰以免上传或审核失败
</div>
</div>
</u-form-item>
</up-form-item>
</div>
<div class="column">
<div class="flag-title light-color">法人信息</div>
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="legalName"
label="法人姓名"
><u-input :custom-style="defaultInputStyle" v-model="form.legalName"
/></u-form-item>
<u-form-item
><u-input border="none" class="field-input" :custom-style="fieldInputStyle" v-model="form.legalName"
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="legalId"
label="法人证件号"
><u-input :custom-style="defaultInputStyle" v-model="form.legalId"
/></u-form-item>
<u-form-item
><u-input border="none" class="field-input" :custom-style="fieldInputStyle" v-model="form.legalId"
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="legalPhoto"
@@ -166,11 +171,10 @@
:max-count="1"
></u-upload>
</div>
</u-form-item>
{{form}}
</up-form-item>
</div>
</u-form>
<div class="submit" @click="validatorStep1Form">提交/下一步</div>
</up-form>
<view class="submit" @click="validatorStep1Form">提交/下一步</view>
<m-city
:provinceData="list"
headTitle="区域选择"
@@ -182,234 +186,222 @@
</div>
</template>
<script>
import { applyFirst } from "@/api/entry";
import city from "@/components/m-city/m-city.vue";
import storage from "@/utils/storage.js";
import { handleUploadAfterRead, getUploadedUrls } from "@/utils/uploadHelper.js";
import * as RegExp from "@/utils/RegExp.js";
export default {
components: { "m-city": city },
data() {
return {
storage,
defaultInputStyle: {
background: "#f7f7f7",
padding: "0 20rpx",
"border-radius": "10rpx",
},
legalPhotoFileList: [],
legalPhotoBackFileList: [],
licencePhotoFileList: [],
form: {
companyName: "",
companyAddressIdPath: [],
companyAddressPath: "",
companyAddress: "",
employeeNum: "",
registeredCapital: "",
linkName: "",
linkPhone: "",
companyPhone: "",
companyEmail: "",
licenseNum: "",
scope: "",
legalPhoto: "",
licencePhoto: "",
legalName: "",
legalId: "",
},
list: [
{
id: "",
localName: "请选择",
children: [],
},
],
rules: {
// 验证规则
companyName: [{ required: true, message: "请填写公司信息" }],
companyAddressPath: [{ required: true, message: "请选择公司所在地" }],
companyAddress: [{ required: true, message: "请填写公司详细地址" }],
employeeNum: [{ required: true, message: "请填写公司员工总数" }],
registeredCapital: [{ required: true, message: "请填写公司注册资金" }],
linkName: [{ required: true, message: "请填写联系人姓名" }],
linkPhone: [
{ required: true, message: "请填写联系人电话" },
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说返回true表示校验通过返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return this.$u.test.mobile(value);
},
message: "手机号码不正确",
// 触发器可以同时用blur和change
trigger: ["change", "blur"],
},
],
companyPhone: [
{ required: true, message: "请填写公司电话" },
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说返回true表示校验通过返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return this.$u.test.mobile(value);
},
message: "请填写正确的号码",
// 触发器可以同时用blur和change
trigger: ["change", "blur"],
},
],
companyEmail: [
{ required: true, message: "请填写电子邮箱" },
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说返回true表示校验通过返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return this.$u.test.email(value);
},
message: "请填写正确的电子邮箱",
// 触发器可以同时用blur和change
trigger: ["change", "blur"],
},
],
licenseNum: [
{ required: true, message: "请填写营业执照号" },
{ pattern: RegExp.licenseNum, message: "请输入正确的营业执照号" },
],
scope: [{ required: true, message: "请填写营业执照所示经营范围" }],
legalPhoto: [{ required: true, message: "请上传法人身份证照片" },
{
// 自定义验证函数,见上说明
validator: (rule, value, callback) => {
// 上面有说返回true表示校验通过返回false表示不通过
// this.$u.test.mobile()就是返回true或者false的
return value.length === 2;
},
message: "请上传法人身份证正反照片",
// 触发器可以同时用blur和change
trigger: ["change", "blur"],
}],
licencePhoto: [{ required: true, message: "请上传营业执照" }],
legalName: [{ required: true, message: "请输入法人姓名" }],
legalId: [
{ required: true, message: "请输入法人证件号" },
{ pattern: RegExp.IDCard, message: "请输入正确的证件号" },
],
},
};
<script setup lang="ts">
import { ref, reactive, computed, watch, getCurrentInstance } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { applyFirst } from '@/api/entry'
import MCity from '@/components/m-city/m-city.vue'
import { handleUploadAfterRead, getUploadedUrls } from '@/utils/uploadHelper.js'
import * as RegExp from '@/utils/RegExp.js'
import { getThemeStyle } from '@/utils/theme'
import { fieldInputStyle } from '@/utils/form-style.js'
const props = defineProps<{ companyData?: any }>()
const emit = defineEmits<{ callback: [] }>()
const store = useStore()
const { proxy } = getCurrentInstance()!
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const uForm = ref<any>(null)
const cityPicker = ref<any>(null)
const legalPhotoFileList = ref<any[]>([])
const legalPhotoBackFileList = ref<any[]>([])
const licencePhotoFileList = ref<any[]>([])
const form = reactive<Record<string, any>>({
companyName: '',
companyAddressIdPath: [],
companyAddressPath: '',
companyAddress: '',
employeeNum: '',
registeredCapital: '',
linkName: '',
linkPhone: '',
companyPhone: '',
companyEmail: '',
licenseNum: '',
scope: '',
legalPhoto: '',
licencePhoto: '',
legalName: '',
legalId: '',
})
const list = ref([
{
id: '',
localName: '请选择',
children: [],
},
mounted() {
this.$refs.uForm.setRules(this.rules);
},
props: ["companyData"],
watch: {
companyData: {
handler(val) {
if (val) {
this["form"] = val;
// 给图片赋值
const judgeDeepPhoto = ["legalPhoto", "licencePhoto"];
judgeDeepPhoto.forEach((key) => {
if (this.form[key]) {
this.form[key].split(",").forEach((item) => {
this[`${key}FileList`].push({ url: item });
});
}
});
}
},
deep: true,
])
const rules = {
companyName: [{ required: true, message: '请填写公司信息' }],
companyAddressPath: [{ required: true, message: '请选择公司所在地' }],
companyAddress: [{ required: true, message: '请填写公司详细地址' }],
employeeNum: [{ required: true, message: '请填写公司员工总数' }],
registeredCapital: [{ required: true, message: '请填写公司注册资金' }],
linkName: [{ required: true, message: '请填写联系人姓名' }],
linkPhone: [
{ required: true, message: '请填写联系人电话' },
{
validator: (_rule: unknown, value: string) => proxy.$u.test.mobile(value),
message: '手机号码不正确',
trigger: ['change', 'blur'],
},
},
methods: {
onUploadAfterRead(event, key, fileListKey) {
if (!Array.isArray(this.form[key])) {
this.form[key] = [];
],
companyPhone: [
{ required: true, message: '请填写公司电话' },
{
validator: (_rule: unknown, value: string) => proxy.$u.test.mobile(value),
message: '请填写正确的号码',
trigger: ['change', 'blur'],
},
],
companyEmail: [
{ required: true, message: '请填写电子邮箱' },
{
validator: (_rule: unknown, value: string) => proxy.$u.test.email(value),
message: '请填写正确的电子邮箱',
trigger: ['change', 'blur'],
},
],
licenseNum: [
{ required: true, message: '请填写营业执照号' },
{ pattern: RegExp.licenseNum, message: '请输入正确的营业执照号' },
],
scope: [{ required: true, message: '请填写营业执照所示经营范围' }],
legalPhoto: [
{ required: true, message: '请上传法人身份证照片' },
{
validator: (_rule: unknown, value: string | string[]) =>
Array.isArray(value) ? value.length === 2 : false,
message: '请上传法人身份证正反照片',
trigger: ['change', 'blur'],
},
],
licencePhoto: [{ required: true, message: '请上传营业执照' }],
legalName: [{ required: true, message: '请输入法人姓名' }],
legalId: [
{ required: true, message: '请输入法人证件号' },
{ pattern: RegExp.IDCard, message: '请输入正确的证件号' },
],
}
watch(
() => props.companyData,
(val) => {
if (val) {
Object.assign(form, val)
const judgeDeepPhoto = ['legalPhoto', 'licencePhoto'] as const
const fileListMap = {
legalPhoto: legalPhotoFileList,
licencePhoto: licencePhotoFileList,
}
handleUploadAfterRead(event, this[fileListKey], () => {
if (key === "legalPhoto") {
this.form[key] = [
...getUploadedUrls(this.legalPhotoFileList),
...getUploadedUrls(this.legalPhotoBackFileList),
];
} else {
this.form[key] = getUploadedUrls(this[fileListKey]);
judgeDeepPhoto.forEach((key) => {
if (form[key]) {
form[key].split(',').forEach((item: string) => {
fileListMap[key].value.push({ url: item })
})
}
});
},
getPickerParentValue(e) {
this.form.companyAddressIdPath = [];
let name = "";
e.forEach((item, index) => {
if (item.id) {
// 遍历数据
this.form.companyAddressIdPath.push(item.id);
if (index == e.length - 1) {
name += item.localName;
} else {
name += item.localName + ",";
}
this.form.companyAddressPath = name;
}
});
},
// 显示三级地址联动
showPicker() {
console.log(this.$refs)
this.$refs.cityPicker.show();
},
validatorStep1Form() {
this.$refs.uForm.validate(async (valid) => {
console.log(valid);
if (valid) {
const params = { ...this.form };
params.legalPhoto = params.legalPhoto.toString();
params.licencePhoto = params.licencePhoto.toString();
params.companyAddressIdPath = params.companyAddressIdPath.toString();
delete params.complexAddress;
const res = await applyFirst(params);
if (res.data.success) {
uni.showToast({
title: "提交成功!",
icon: "none",
});
this.$emit("callback");
}
}
});
},
})
}
},
};
{ deep: true }
)
onReady(() => {
uForm.value?.setRules(rules)
})
function onUploadAfterRead(
event: any,
key: string,
fileListKey: 'licencePhotoFileList' | 'legalPhotoFileList' | 'legalPhotoBackFileList'
) {
const fileListMap = {
licencePhotoFileList,
legalPhotoFileList,
legalPhotoBackFileList,
}
if (!Array.isArray(form[key])) {
form[key] = []
}
handleUploadAfterRead(event, fileListMap[fileListKey].value, () => {
if (key === 'legalPhoto') {
form[key] = [
...getUploadedUrls(legalPhotoFileList.value),
...getUploadedUrls(legalPhotoBackFileList.value),
]
} else {
form[key] = getUploadedUrls(fileListMap[fileListKey].value)
}
})
}
function getPickerParentValue(e: any[]) {
form.companyAddressIdPath = []
let name = ''
e.forEach((item, index) => {
if (item.id) {
form.companyAddressIdPath.push(item.id)
if (index == e.length - 1) {
name += item.localName
} else {
name += item.localName + ','
}
form.companyAddressPath = name
}
})
}
function showPicker() {
cityPicker.value?.show()
}
function validatorStep1Form() {
uForm.value?.validate(async (valid: boolean) => {
if (valid) {
const params = { ...form }
params.legalPhoto = params.legalPhoto.toString()
params.licencePhoto = params.licencePhoto.toString()
params.companyAddressIdPath = params.companyAddressIdPath.toString()
delete params.complexAddress
const res = await applyFirst(params)
if (res.data.success) {
uni.showToast({
title: '提交成功!',
icon: 'none',
})
emit('callback')
}
}
})
}
</script>
<style>
/* page {
background: #fff;
} */
<style lang="scss">
page {
background: #f8f8f8;
}
</style>
<style lang="scss" scoped>
@import url("./entry.scss");
@import "./entry.scss";
@import "./entry-form.scss";
.column {
padding: 32rpx;
margin-bottom: 20rpx;
background: #fff;
.wrapper {
@include seller-entry-form;
}
.submit {
color: #fff;
margin-top: 120rpx;
background: rgba($light-color, 0.8);
}
.tips {
color: #999;
font-size: 24rpx;
line-height: 1.2;
margin-top: 10rpx;
@include seller-entry-submit;
}
</style>

View File

@@ -1,144 +1,135 @@
<template>
<div class="wrapper">
<u-form label-width="250" :model="form" ref="uForm">
<div class="wrapper" :style="themeStyle">
<up-form label-position="top" :model="form" ref="uForm">
<div class="column">
<div class="flag-title light-color">基础信息</div>
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="settlementBankAccountName"
label="银行开户名"
><u-input
><u-input border="none" class="field-input"
v-model="form.settlementBankAccountName"
:custom-style="defaultInputStyle"
/></u-form-item>
:custom-style="fieldInputStyle"
/></up-form-item>
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="settlementBankAccountNum"
label="银行账号"
><u-input
:custom-style="defaultInputStyle"
><u-input border="none" class="field-input"
:custom-style="fieldInputStyle"
v-model="form.settlementBankAccountNum"
/></u-form-item>
<u-form-item
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="settlementBankBranchName"
label="开户银行支行名称"
><u-input
:custom-style="defaultInputStyle"
><u-input border="none" class="field-input"
:custom-style="fieldInputStyle"
v-model="form.settlementBankBranchName"
/></u-form-item>
/></up-form-item>
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="settlementBankJointName"
label="支行联行号"
><u-input
:custom-style="defaultInputStyle"
><u-input border="none" class="field-input"
:custom-style="fieldInputStyle"
v-model="form.settlementBankJointName"
/></u-form-item>
/></up-form-item>
</div>
</u-form>
<div class="submit" @click="validatorStep2Form">提交/下一步</div>
</up-form>
<view class="submit" @click="validatorStep2Form">提交/下一步</view>
</div>
</template>
<script>
import { applySecond } from "@/api/entry";
<script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { applySecond } from '@/api/entry'
import { getThemeStyle } from '@/utils/theme'
import { fieldInputStyle } from '@/utils/form-style.js'
export default {
data() {
return {
defaultInputStyle: {
background: "#f7f7f7",
padding: "0 20rpx",
"border-radius": "10rpx",
},
form: {
settlementBankAccountName: "",
settlementBankAccountNum: "",
settlementBankBranchName: "",
settlementBankJointName: "",
},
const props = defineProps<{ companyData?: any }>()
const emit = defineEmits<{ callback: [] }>()
rules: {
// 验证规则
settlementBankAccountName: [
{ required: true, message: "请填写银行开户名称" },
],
settlementBankAccountNum: [
{ required: true, message: "请填写银行账号" },
],
settlementBankBranchName: [
{ required: true, message: "请填写开户银行支行名称" },
],
settlementBankJointName: [
{ required: true, message: "请填写支行联行号" },
],
},
};
const store = useStore()
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const uForm = ref<any>(null)
const form = reactive({
settlementBankAccountName: '',
settlementBankAccountNum: '',
settlementBankBranchName: '',
settlementBankJointName: '',
})
const rules = {
settlementBankAccountName: [
{ required: true, message: '请填写银行开户名称' },
],
settlementBankAccountNum: [
{ required: true, message: '请填写银行账号' },
],
settlementBankBranchName: [
{ required: true, message: '请填写开户银行支行名称' },
],
settlementBankJointName: [
{ required: true, message: '请填写支行联行号' },
],
}
watch(
() => props.companyData,
(val) => {
if (val) {
Object.assign(form, val)
}
},
mounted() {
this.$refs.uForm.setRules(this.rules);
},
props: ["companyData"],
watch: {
companyData: {
handler(val) {
this["form"] = val;
console.log(this.form)
},
deep: true,
immediate:true
},
},
methods: {
validatorStep2Form() {
this.$refs.uForm.validate(async (valid) => {
if (valid) {
const params = { ...this.form };
const res = await applySecond(params);
if (res.data.success) {
uni.showToast({
title: "提交成功!",
icon: "none",
});
this.$emit("callback");
}
}
});
},
},
};
{ deep: true, immediate: true }
)
onReady(() => {
uForm.value?.setRules(rules)
})
function validatorStep2Form() {
uForm.value?.validate(async (valid: boolean) => {
if (valid) {
const params = { ...form }
const res = await applySecond(params)
if (res.data.success) {
uni.showToast({
title: '提交成功!',
icon: 'none',
})
emit('callback')
}
}
})
}
</script>
<style>
/* page {
background: #fff;
} */
<style lang="scss">
page {
background: #f8f8f8;
}
</style>
<style lang="scss" scoped>
@import url("./entry.scss");
@import "./entry.scss";
@import "./entry-form.scss";
.wrapper {
// padding: 50rpx 32rpx 16rpx 32rpx;
}
.column {
padding: 32rpx;
margin-bottom: 20rpx;
background: #fff;
@include seller-entry-form;
}
.submit {
color: #fff;
margin-top: 120rpx;
background: rgba($light-color, 0.8);
}
.tips {
color: #999;
font-size: 24rpx;
line-height: 1.2;
margin-top: 10rpx;
@include seller-entry-submit;
}
</style>

View File

@@ -1,16 +1,16 @@
<template>
<div class="wrapper">
<u-form label-width="200" :model="form" ref="uForm">
<div class="wrapper" :style="themeStyle">
<up-form label-position="top" :model="form" ref="uForm">
<div class="column">
<div class="flag-title light-color">基础信息</div>
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="storeName"
label="店铺名称"
><u-input v-model="form.storeName" :custom-style="defaultInputStyle"
/></u-form-item>
<u-form-item
><u-input border="none" class="field-input" v-model="form.storeName" :custom-style="fieldInputStyle"
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="storeLogo"
@@ -25,39 +25,45 @@
:max-count="1"
></u-upload>
</div>
</u-form-item>
<u-form-item
</up-form-item>
<up-form-item
required
:border-bottom="false"
prop="goodsManagementCategory"
label="店铺经营类目"
>
<div @click="showCategory()" style="margin-right: 30rpx;">选择</div>
<u-input
:custom-style="defaultInputStyle"
v-model="goodsManagementCategory"
disabled
@click="showCategory()"
/></u-form-item>
>
<view class="field-row">
<u-input
border="none"
class="field-input"
:custom-style="fieldInputStyle"
v-model="goodsManagementCategory"
disabled
@click="showCategory()"
/>
<view class="picker-action" @click="showCategory()">选择</view>
</view>
</up-form-item>
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="storeAddressPath"
label="店铺所在地"
>
<div @click="showPicker()" style="margin-right: 30rpx;">选择</div>
<u-input
:custom-style="defaultInputStyle"
v-model="form.storeAddressPath"
disabled
/>
</u-form-item>
<view class="field-row">
<u-input
border="none"
class="field-input"
:custom-style="fieldInputStyle"
v-model="form.storeAddressPath"
disabled
/>
<view class="picker-action" @click="showPicker()">选择</view>
</view>
</up-form-item>
<!-- <u-form-item
<!-- <up-form-item
required
:border-bottom="false"
prop="storeAddressPath"
@@ -65,30 +71,35 @@
>
<div class="get-center" @click="clickUniMap()">开始定位</div>
<div class="tips-success" v-if="form.storeCenter">已成功定位</div>
</u-form-item> -->
</up-form-item> -->
<u-form-item
<up-form-item
required
:border-bottom="false"
prop="storeAddressDetail"
label="店铺详细地址"
><u-input
:custom-style="defaultInputStyle"
><u-input border="none" class="field-input"
:custom-style="fieldInputStyle"
v-model="form.storeAddressDetail"
/></u-form-item>
<u-form-item
/></up-form-item>
<up-form-item
required
:border-bottom="false"
prop="storeDesc"
label="店铺简介"
><u-input
type="textarea"
:custom-style="defaultInputStyle"
>
<u-textarea
class="field-textarea"
border="none"
height="240"
maxlength="500"
v-model="form.storeDesc"
/></u-form-item>
placeholder="请输入店铺简介"
/>
</up-form-item>
</div>
</u-form>
<div class="submit" @click="validatorStep1Form">提交平台审核</div>
</up-form>
<view class="submit" @click="validatorStep3Form">提交平台审核</view>
<m-city
:provinceData="list"
headTitle="区域选择"
@@ -108,261 +119,256 @@
</div>
</template>
<script>
import { applyThird } from "@/api/entry";
import { getCategoryList } from "@/api/goods";
import city from "@/components/m-city/m-city.vue";
import storage from "@/utils/storage.js";
import { handleUploadAfterRead } from "@/utils/uploadHelper.js";
import uniMap from "@/components/uniMap";
import permision from "@/js_sdk/wa-permission/permission.js";
export default {
components: { "m-city": city, uniMap },
data() {
return {
storage,
mapFlag: false,
defaultInputStyle: {
background: "#f7f7f7",
padding: "0 20rpx",
"border-radius": "10rpx",
},
goodsManagementCategory: "",
storeLogoFileList: [],
categoryList: [],
form: {
storeName: "",
storeLogo: "",
goodsManagementCategory: "",
storeAddressPath: "",
storeAddressDetail: "",
storeDesc: "",
},
list: [
{
id: "",
localName: "请选择",
children: [],
},
],
rules: {
// 验证规则
goodsManagementCategory: [
{ required: true, message: "请选择店铺经营类目" },
],
storeName: [{ required: true, message: "请填写店铺名称" }],
storeLogo: [{ required: true, message: "请上传店铺logo" }],
storeDesc: [{ required: true, message: "请填写店铺简介" }],
storeCenter: [{ required: true, message: "请选择店铺位置" }],
storeAddressIdPath: [{ required: true, message: "请选择店铺位置" }],
storeAddressDetail: [{ required: true, message: "请输入店铺详细地址" }],
},
enableCategory: false,
};
<script setup lang="ts">
import { ref, reactive, computed, watch, onMounted } from 'vue'
import { onReady } from '@dcloudio/uni-app'
import { useStore } from '@/store'
import { applyThird } from '@/api/entry'
import { getCategoryList } from '@/api/goods'
import MCity from '@/components/m-city/m-city.vue'
import uniMap from '@/components/uniMap'
import permision from '@/js_sdk/wa-permission/permission.js'
import { handleUploadAfterRead } from '@/utils/uploadHelper.js'
import { getThemeStyle } from '@/utils/theme'
import { fieldInputStyle } from '@/utils/form-style.js'
const props = defineProps<{ companyData?: any }>()
const emit = defineEmits<{ callback: [] }>()
const store = useStore()
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const uForm = ref<any>(null)
const cityPicker = ref<any>(null)
const mapFlag = ref(false)
const enableCategory = ref(false)
const goodsManagementCategory = ref('')
const storeLogoFileList = ref<any[]>([])
const categoryList = ref<any[]>([])
const form = reactive<Record<string, any>>({
storeName: '',
storeLogo: '',
goodsManagementCategory: '',
storeAddressPath: '',
storeAddressDetail: '',
storeDesc: '',
})
const list = ref([
{
id: '',
localName: '请选择',
children: [],
},
mounted() {
this.$refs.uForm.setRules(this.rules);
this.fetchCategoryList();
])
const rules = {
goodsManagementCategory: [
{ required: true, message: '请选择店铺经营类目' },
],
storeName: [{ required: true, message: '请填写店铺名称' }],
storeLogo: [{ required: true, message: '请上传店铺logo' }],
storeDesc: [{ required: true, message: '请填写店铺简介' }],
storeCenter: [{ required: true, message: '请选择店铺位置' }],
storeAddressIdPath: [{ required: true, message: '请选择店铺位置' }],
storeAddressDetail: [{ required: true, message: '请输入店铺详细地址' }],
}
watch(
() => props.companyData,
(val) => {
if (val) {
Object.assign(form, val)
const judgeDeepPhoto = ['storeLogo']
judgeDeepPhoto.forEach((key) => {
if (form[key]) {
storeLogoFileList.value = []
form[key].split(',').forEach((item: string) => {
storeLogoFileList.value.push({ url: item })
})
}
})
}
},
props: ["companyData"],
watch: {
companyData: {
handler(val) {
this["form"] = val;
// 给图片赋值
const judgeDeepPhoto = ["storeLogo"];
{ deep: true, immediate: true }
)
judgeDeepPhoto.forEach((key) => {
if (this.form[key]) {
this.form[key].split(",").forEach((item) => {
this[`${key}FileList`].push({ url: item });
});
}
});
},
deep: true,
immediate: true,
},
},
onReady(() => {
uForm.value?.setRules(rules)
})
methods: {
callBackAddress(val) {
console.log(val);
this.form.storeAddressDetail = val.address;
this.form.storeCenter = `${val.longitude},${val.latitude}`;
},
// 关闭地图
closeMap() {
this.mapFlag = false;
},
// 打开地图并访问权限
clickUniMap() {
console.log("click");
// #ifdef APP-PLUS
if (plus.os.name == "iOS") {
// ios系统
permision.judgeIosPermission("location")
? (this.mapFlag = true)
: this.refuseMap();
} else {
// 安卓
this.requestAndroidPermission(
"android.permission.ACCESS_FINE_LOCATION"
);
}
// #endif
onMounted(() => {
fetchCategoryList()
})
// #ifndef APP-PLUS
this.mapFlag = true;
// #endif
},
function callBackAddress(val: any) {
form.storeAddressDetail = val.address
form.storeCenter = `${val.longitude},${val.latitude}`
}
// 如果拒绝权限 提示区设置
refuseMap() {
uni.showModal({
title: "温馨提示",
content: "您已拒绝定位,请开启",
confirmText: "去设置",
success(res) {
if (res.confirm) {
//打开授权设置
// #ifndef MP-WEIXIN
uni.getSystemInfo({
success(res) {
if (res.platform == "ios") {
//IOS
plus.runtime.openURL("app-settings://");
} else if (res.platform == "android") {
//安卓
let main = plus.android.runtimeMainActivity();
let Intent = plus.android.importClass(
"android.content.Intent"
);
let mIntent = new Intent("android.settings.ACTION_SETTINGS");
main.startActivity(mIntent);
}
},
});
// #endif
}
},
});
},
// 获取安卓是否拥有地址权限
async requestAndroidPermission(permisionID) {
var result = await permision.requestAndroidPermission(permisionID);
function closeMap() {
mapFlag.value = false
}
if (result == 1) {
this.mapFlag = true;
} else {
this.refuseMap();
function clickUniMap() {
// #ifdef APP-PLUS
if (plus.os.name == 'iOS') {
permision.judgeIosPermission('location')
? (mapFlag.value = true)
: refuseMap()
} else {
requestAndroidPermission('android.permission.ACCESS_FINE_LOCATION')
}
// #endif
// #ifndef APP-PLUS
mapFlag.value = true
// #endif
}
function refuseMap() {
uni.showModal({
title: '温馨提示',
content: '您已拒绝定位,请开启',
confirmText: '去设置',
success(res) {
if (res.confirm) {
// #ifndef MP-WEIXIN
uni.getSystemInfo({
success(sysRes) {
if (sysRes.platform == 'ios') {
plus.runtime.openURL('app-settings://')
} else if (sysRes.platform == 'android') {
const main = plus.android.runtimeMainActivity()
const Intent = plus.android.importClass('android.content.Intent')
const mIntent = new Intent('android.settings.ACTION_SETTINGS')
main.startActivity(mIntent)
}
},
})
// #endif
}
},
})
}
confirmCategory(val) {
this.form.goodsManagementCategory = val[0].value;
this.goodsManagementCategory = val[0].label;
},
async fetchCategoryList() {
const res = await getCategoryList(0);
async function requestAndroidPermission(permisionID: string) {
const result = await permision.requestAndroidPermission(permisionID)
if (result == 1) {
mapFlag.value = true
} else {
refuseMap()
}
}
function confirmCategory(val: any[]) {
form.goodsManagementCategory = val[0].value
goodsManagementCategory.value = val[0].label
}
async function fetchCategoryList() {
const res = await getCategoryList(0)
if (res.data.success) {
if (res.data.result.length) {
categoryList.value = res.data.result.map((item: any) => {
return { label: item.name, value: item.id }
})
if (form.goodsManagementCategory) {
goodsManagementCategory.value = categoryList.value.find(
(item) => form.goodsManagementCategory == item.value
)?.label || ''
}
}
}
}
function onUploadAfterRead(
event: any,
key: string,
fileListKey: 'storeLogoFileList'
) {
handleUploadAfterRead(event, storeLogoFileList.value, (urls) => {
form[key] = urls
})
}
function getPickerParentValue(e: any[]) {
form.storeAddressIdPath = []
let name = ''
e.forEach((item, index) => {
if (item.id) {
form.storeAddressIdPath.push(item.id)
if (index == e.length - 1) {
name += item.localName
} else {
name += item.localName + ','
}
form.storeAddressPath = name
}
})
form.storeCenter = e[e.length - 1].center
}
function showPicker() {
cityPicker.value?.show()
}
function showCategory() {
enableCategory.value = true
}
function validatorStep3Form() {
uForm.value?.validate(async (valid: boolean) => {
if (valid) {
const params = { ...form }
params.storeLogo = params.storeLogo.toString()
params.storeAddressIdPath = params.storeAddressIdPath.toString()
const res = await applyThird(params)
if (res.data.success) {
if (res.data.result.length) {
this.categoryList = res.data.result.map((item) => {
return { label: item.name, value: item.id };
});
if (this.form.goodsManagementCategory) {
this.goodsManagementCategory = this.categoryList.find(
(item) => this.form.goodsManagementCategory == item.value
).label;
}
}
uni.showToast({
title: '提交成功!',
icon: 'none',
})
emit('callback')
}
},
onUploadAfterRead(event, key, fileListKey) {
handleUploadAfterRead(event, this[fileListKey], (urls) => {
this.form[key] = urls;
});
},
getPickerParentValue(e) {
this.form.storeAddressIdPath = [];
console.log(e)
let name = "";
e.forEach((item, index) => {
if (item.id) {
// 遍历数据
this.form.storeAddressIdPath.push(item.id);
if (index == e.length - 1) {
name += item.localName;
} else {
name += item.localName + ",";
}
this.form['storeAddressPath'] = name
}
});
this.form.storeCenter = e[e.length-1].center
},
// 显示三级地址联动
showPicker() {
this.$refs.cityPicker.show();
},
showCategory() {
this.enableCategory = true;
},
validatorStep1Form() {
this.$refs.uForm.validate(async (valid) => {
console.log(valid);
if (valid) {
const params = { ...this.form };
params.storeLogo = params.storeLogo.toString();
params.storeAddressIdPath = params.storeAddressIdPath.toString();
const res = await applyThird(params);
if (res.data.success) {
uni.showToast({
title: "提交成功!",
icon: "none",
});
this.$emit("callback");
}
}
});
},
},
};
}
})
}
</script>
<style>
/* page {
background: #fff;
} */
<style lang="scss">
page {
background: #f8f8f8;
}
</style>
<style lang="scss" scoped>
@import url("./entry.scss");
@import "./entry.scss";
@import "./entry-form.scss";
.wrapper {
@include seller-entry-form;
}
.get-center {
padding: 12rpx 30rpx;
background: $light-color;
background: var(--theme-light, #ff6b35);
border-radius: 10rpx;
font-size: 24rpx;
color: #fff;
font-weight: bold;
display: inline;
}
.column {
padding: 32rpx;
margin-bottom: 20rpx;
background: #fff;
display: inline-block;
}
.submit {
color: #fff;
margin-top: 120rpx;
background: rgba($light-color, 0.8);
}
.tips {
color: #999;
font-size: 24rpx;
line-height: 1.2;
margin-top: 10rpx;
@include seller-entry-submit;
}
.tips-success {
color: $weChat-color;
font-size: 24rpx;

File diff suppressed because it is too large Load Diff

View File

@@ -31,79 +31,78 @@
</view>
</template>
<script>
import { scannerCodeLogin, scannerCodeLoginConfirm } from "@/api/login";
export default {
data() {
return {
errorMsg: "",
token: "",
};
},
watch: {
errorMsg(val) {
if (val) {
uni.showToast({
title: val,
icon: "none",
});
// uni.navigateBack()
<script setup lang="ts">
import { ref, watch } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { forceLogin } from '@/utils/filters.js'
import { scannerCodeLogin, scannerCodeLoginConfirm } from '@/api/login'
const errorMsg = ref('')
const token = ref('')
watch(errorMsg, (val) => {
if (val) {
uni.showToast({
title: val,
icon: 'none',
})
}
})
onLoad((params) => {
token.value = params.token || ''
if (!token.value) {
errorMsg.value = '信息异常'
}
})
onShow(() => {
forceLogin()
scannerCodeLogin({ token: token.value }).then((res) => {
if (res.data.code == 200) {
const code = res.data.result
switch (code) {
case 0:
case 1:
errorMsg.value = ''
break
case 2:
case 3:
errorMsg.value = '请勿重复扫码'
break
case 4:
errorMsg.value = '二维码已过期,重新扫码'
break
default:
errorMsg.value = '状态异常'
}
},
},
onShow() {
this.forceLogin();
scannerCodeLogin({ token: this.token }).then((res) => {
if (res.data.code == 200) {
let code = res.data.result;
switch (code) {
case 0:
case 1:
this.errorMsg = "";
break;
case 2:
case 3:
this.errorMsg = "请勿重复扫码";
break;
case 4:
this.errorMsg = "二维码已过期,重新扫码";
break;
default:
this.errorMsg = "状态异常";
}
} else {
this.errorMsg = res.data.message;
}
});
},
onLoad(params) {
this.token = params.token;
if (this.token == undefined || this.token == "") {
this.errorMsg = "信息异常";
} else {
errorMsg.value = res.data.message
}
},
methods: {
confirmLogin() {
this.config(1);
},
cancelLogin() {
this.config(0);
},
config(code) {
scannerCodeLoginConfirm({ token: this.token, code: code }).then((res) => {
let title = res.data.success ? "操作成功" : "操作失败";
uni.showToast({
title: title,
duration: 1500,
icon: "none",
});
setTimeout(function () {
uni.navigateBack();
}, 1000);
});
},
},
};
})
})
function confirmLogin() {
submitLoginConfirm(1)
}
function cancelLogin() {
submitLoginConfirm(0)
}
function submitLoginConfirm(code: number) {
scannerCodeLoginConfirm({ token: token.value, code }).then((res) => {
const title = res.data.success ? '操作成功' : '操作失败'
uni.showToast({
title,
duration: 1500,
icon: 'none',
})
setTimeout(() => {
uni.navigateBack()
}, 1000)
})
}
</script>
<style lang="scss" scoped>
page {

View File

@@ -19,39 +19,33 @@
</view>
</template>
<script>
import { whetherNavigate } from "@/utils/Foundation"; //登录跳转
import config from "@/config/config";
import api from "@/config/api.js";
export default {
data() {
return {
// 授权信息展示,商城名称
projectName: config.name,
};
},
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { whetherNavigate } from '@/utils/Foundation'
import config from '@/config/config'
import api from '@/config/api.js'
//微信小程序进入页面先获取code否则几率出现code和后续交互数据不对应情况
mounted() {
// 小程序默认分享
uni.showShareMenu({ withShareTicket: true });
},
methods: {
back() {
whetherNavigate();
},
getUserProfile() {
let code = "WECHAT";
let buyer = api.buyer;
window.open(buyer + `/passport/connect/connect/login/web/` + code, "_self");
},
backToHome() {
uni.switchTab({
url: `/pages/tabbar/home/index`,
});
},
},
};
const projectName = ref(config.name)
onMounted(() => {
uni.showShareMenu({ withShareTicket: true })
})
function back() {
whetherNavigate()
}
function getUserProfile() {
const code = 'WECHAT'
const buyer = api.buyer
window.open(buyer + `/passport/connect/connect/login/web/` + code, '_self')
}
function backToHome() {
uni.switchTab({
url: '/pages/tabbar/home/index',
})
}
</script>
<style lang="scss" scoped>

View File

@@ -46,197 +46,127 @@
</view>
</template>
<script>
import {
mpAutoLogin
} from "@/api/connect.js";
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useStore } from '@/store'
import { mpAutoLogin } from '@/api/connect.js'
import { whetherNavigate } from '@/utils/Foundation'
import { getUserInfo } from '@/api/members'
import storage from '@/utils/storage.js'
import config from '@/config/config'
import {
whetherNavigate
} from "@/utils/Foundation"; //登录跳转
import {
getUserInfo
} from "@/api/members";
import storage from "@/utils/storage.js";
import config from '@/config/config'
export default {
data() {
return {
lightColor:this.$lightColor,
checked:false,
configs:config,
// 是否展示手机号码授权弹窗,默认第一步不展示,要先获取用户基础信息
phoneAuthPopup: false,
// 授权信息展示,商城名称
projectName: config.name,
//微信返回信息用于揭秘信息获取sessionkey
code: "",
//微信昵称
nickName: "",
logingFlag: false,
//微信头像
image: "",
};
},
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
//微信小程序进入页面先获取code否则几率出现code和后续交互数据不对应情况
mounted() {
// 小程序默认分享
uni.showShareMenu({
withShareTicket: true
});
const checked = ref(false)
const configs = config
const phoneAuthPopup = ref(false)
const projectName = ref(config.name)
const code = ref('')
const nickName = ref('')
const logingFlag = ref(false)
const image = ref('')
onMounted(() => {
uni.showShareMenu({ withShareTicket: true })
uni.login({
success: (res) => {
if (res.errMsg === 'login:ok') {
code.value = res.code
} else {
uni.showToast({
title: '系统异常,请联系管理员!',
})
}
},
})
})
//获取code
uni.login({
success: (res) => {
if(res.errMsg === "login:ok") {
this.code = res.code
} else {
uni.showToast({
title: "系统异常,请联系管理员!"
})
}
},
});
},
methods: {
/**
* TODO 此方法不一定是最优解,如果有更好的办法请在 https://gitee.com/beijing_hongye_huicheng/lilishop/issues 中提出
* 小程序返回bug
* 1.介于微信登录是在login.vue的基础上作为判断跳转来
* 所以在页面栈中会自动记录回退路径所以导致每次微信小程序点击回退就会自动返回login页面
* 当然login页面的判断就是 没有登录就会跳转到微信小程序页面 导致了无法回退到之前页面
* 2.解决方法: 尝试在回退的时候判断地址,让回退多一级这样就避免了
*/
function back() {
whetherNavigate('wx')
}
back() {
whetherNavigate("wx");
},
backToHome() {
uni.switchTab({
url: `/pages/tabbar/home/index`,
});
},
function backToHome() {
uni.switchTab({
url: '/pages/tabbar/home/index',
})
}
function completeLogin(accessToken: string, refreshToken: string) {
storage.setAccessToken(accessToken)
storage.setRefreshToken(refreshToken)
uni.showToast({
title: '登录成功!',
icon: 'none',
})
getUserInfo().then((user) => {
storage.setUserInfo(user.data.result)
storage.setHasLogin(true)
uni.navigateBack({ delta: 1 })
})
}
function getUserProfile() {
if (!checked.value) {
uni.showToast({
title: '请勾选协议',
icon: 'none',
})
return
}
logingFlag.value = true
if (!code.value) return
//获取用户信息
getUserProfile(e) {
if(!this.checked){
uni.showToast({
title:"请勾选协议",
icon:'none'
})
return
}
this.logingFlag = true;
uni.getUserProfile({
desc: '用于完善会员资料',
success: (res) => {
nickName.value = res.userInfo.nickName
image.value = res.userInfo.avatarUrl
if (this.code) {
// 推荐使用wx.getUserProfile获取用户信息开发者每次通过该接口获取用户个人信息均需用户确认
uni.getUserProfile({
desc: "用于完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
console.log("success", res)
this.nickName = res.userInfo.nickName;
this.image = res.userInfo.avatarUrl;
if (configs.enableFetchMobileLogin) {
phoneAuthPopup.value = true
return
}
/**
* 根据公有的配置设置登录方式
*/
if(this.configs.enableFetchMobileLogin){
this.phoneAuthPopup = true;
return false
}
let iv = res.iv;
let encryptedData = res.encryptedData;
mpAutoLogin({
encryptedData: res.encryptedData,
iv: res.iv,
code: code.value,
image: image.value,
nickName: nickName.value,
}).then((apiRes) => {
completeLogin(apiRes.data.result.accessToken, apiRes.data.result.refreshToken)
})
},
fail: (res) => {
console.log('fail', res)
},
})
let code = this.code;
let image = this.image;
let nickName = this.nickName;
mpAutoLogin({
encryptedData,
iv,
code,
image,
nickName,
}).then((apiRes) => {
storage.setAccessToken(apiRes.data.result.accessToken);
storage.setRefreshToken(apiRes.data.result.refreshToken);
// 登录成功
uni.showToast({
title: "登录成功!",
icon: "none",
});
//获取用户信息
getUserInfo().then((user) => {
storage.setUserInfo(user.data.result);
storage.setHasLogin(true);
logingFlag.value = false
}
uni.navigateBack({
delta: 1,
});
});
});
function getPhoneNumber(e: any) {
const { iv, encryptedData } = e.detail
if (!encryptedData) {
uni.showToast({
title: '请授予手机号码权限,手机号码会和会员系统用户绑定!',
icon: 'none',
})
return
}
},
fail: (res) => {
console.log("fail", res)
},
});
this.logingFlag = false;
}
},
//获取手机号授权
getPhoneNumber(e) {
let iv = e.detail.iv;
let encryptedData = e.detail.encryptedData;
if (!e.detail.encryptedData) {
uni.showToast({
title: "请授予手机号码权限,手机号码会和会员系统用户绑定!",
icon: "none",
});
return;
}
let code = this.code;
let image = this.image;
let nickName = this.nickName;
mpAutoLogin({
encryptedData,
iv,
code,
image,
nickName,
}).then((res) => {
storage.setAccessToken(res.data.result.accessToken);
storage.setRefreshToken(res.data.result.refreshToken);
// 登录成功
uni.showToast({
title: "登录成功!",
icon: "none",
});
//获取用户信息
getUserInfo().then((user) => {
storage.setUserInfo(user.data.result);
storage.setHasLogin(true);
uni.navigateBack({
delta: 1,
});
});
});
},
},
};
mpAutoLogin({
encryptedData,
iv,
code: code.value,
image: image.value,
nickName: nickName.value,
}).then((res) => {
completeLogin(res.data.result.accessToken, res.data.result.refreshToken)
})
}
</script>
<style lang="scss" scoped>
/*微信授权*/

View File

@@ -25,7 +25,7 @@
<u-col span="12">
<u-row :gutter="12">
<u-col :offset="1" span="4">
<u-button class="btns" @click="askValue=''">清空</u-button>
<u-button class="btns" @click="params.askValue=''">清空</u-button>
</u-col>
<u-col :offset="2" span="4">
<u-button class="btns" @click="getAskMessage()" type="success">提交</u-button>
@@ -36,75 +36,83 @@
</div>
</div>
</template>
<script>
import * as API_GOODS from "../../api/goods";
import * as API_MEM from "../../api/members";
export default {
data() {
return {
askGoods: "",
queryGoodsDetail: "",
border: true,
params: {
askValue: "",
anonymous: "YES",
},
};
},
onLoad(options) {
this.askGoods = options;
this.getGoodsData();
},
methods: {
getGoodsData() {
if (this.askGoods.goods_id) {
API_GOODS.getGoods(this.askGoods.goods_id).then((result) => {
this.queryGoodsDetail = result.data;
});
}
},
getAskMessage() {
uni.showLoading();
if (this.params.askValue == "") {
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import * as API_GOODS from '../../api/goods'
import * as API_MEM from '../../api/members'
import { useStore } from '@/store'
const store = useStore()
const askGoods = ref<any>('')
const queryGoodsDetail = ref<any>('')
const border = ref(true)
const params = reactive({
askValue: '',
anonymous: 'YES',
})
function getGoodsData() {
if (askGoods.value.goods_id) {
API_GOODS.getGoods(askGoods.value.goods_id).then((result) => {
queryGoodsDetail.value = result.data
})
}
}
function getAskMessage() {
uni.showLoading()
if (params.askValue == '') {
uni.showToast({
title: '请填写内容!',
icon: 'none',
})
if (store.state.isShowToast) {
uni.hideLoading()
}
return false
}
API_MEM.consultating(
askGoods.value.goods_id,
params.askValue,
params.anonymous
)
.then((res) => {
if (res.statusCode == 200) {
uni.showToast({
title: "请填写内容!",
icon: "none",
});
if (this.$store.state.isShowToast){ uni.hideLoading() };
return false;
}
API_MEM.consultating(
this.askGoods.goods_id,
this.params.askValue,
this.params.anonymous
)
.then((res) => {
if (res.statusCode == 200) {
uni.showToast({
title: "提交成功!",
icon: "none",
});
this.askValue = "";
}
if (this.$store.state.isShowToast){ uni.hideLoading() };
title: '提交成功!',
icon: 'none',
})
.catch((err) => {
if (this.$store.state.isShowToast){ uni.hideLoading() };
});
},
radioGroupChange(e) {
},
radioChange(e) {
if (this.anonymous == "YES") {
this.anonymous = "NO";
} else {
this.anonymous = "YES";
params.askValue = ''
}
},
},
};
if (store.state.isShowToast) {
uni.hideLoading()
}
})
.catch(() => {
if (store.state.isShowToast) {
uni.hideLoading()
}
})
}
function radioGroupChange(_e: any) {}
function radioChange(_e: any) {
if (params.anonymous == 'YES') {
params.anonymous = 'NO'
} else {
params.anonymous = 'YES'
}
}
function goodsDetail() {}
onLoad((options: any) => {
askGoods.value = options
getGoodsData()
})
</script>
<style scoped lang="scss">
.img {

View File

@@ -82,130 +82,129 @@
</view>
</template>
<script>
import * as membersApi from "@/api/members.js";
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import * as membersApi from '@/api/members.js'
import configs from '@/config/config'
export default {
data() {
return {
configs,
status: "loadmore",
userImage: configs.defaultUserPhoto,
commentDetail: "",
selectIndex: "0",
params: {
pageNumber: 1,
pageSize: 10,
grade: "",
import { noPassByName } from '@/utils/filters.js'
const status = ref('loadmore')
const userImage = configs.defaultUserPhoto
const commentDetail = ref<any>('')
const selectIndex = ref(0)
const params = reactive<any>({
pageNumber: 1,
pageSize: 10,
grade: '',
})
const gradeList: Record<string, string> = {
GOOD: '好评',
MODERATE: '中评',
WORSE: '差评',
HAVEIMAGE: '有图',
}
const commDetail = ref<any[]>([])
const opid = ref('')
function commentScore(item: any) {
return item.descriptionScore || item.deliveryScore || 0
}
function splitImg(val: any) {
if (val && val.split(',')) {
return val.split(',')
} else if (val) {
return val
} else {
return false
}
}
function getGoodsCommentsFun(id: string) {
status.value = 'loading'
membersApi.getGoodsComments(id, params).then((res) => {
if (
res.data.result.records == [] ||
res.data.result.records == '' ||
res.data.result.records == null
) {
status.value = 'noMore'
return false
}
commDetail.value = commDetail.value.concat(res.data.result.records)
status.value = 'loadmore'
})
}
function getGoodsCommentsNum(id: string) {
membersApi.getGoodsCommentsCount(id).then((res) => {
if (res.statusCode === 200) {
commentDetail.value = res.data.result
}
})
}
function select(index: number) {
Object.assign(params, {
pageNumber: 1,
pageSize: 10,
})
selectIndex.value = index
params.grade = ['', 'GOOD', 'MODERATE', 'WORSE', ''][selectIndex.value]
if (selectIndex.value === 4) {
params.haveImage = 1
}
commDetail.value = []
if (selectIndex.value === 0) {
Object.assign(params, {
pageNumber: 1,
pageSize: 10,
grade: '',
})
}
getGoodsCommentsFun(opid.value)
}
function preview(urls: string[], index: number) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ['保存图片'],
success: function () {
uni.showToast({
title: '保存成功',
duration: 2000,
icon: 'none',
})
},
gradeList: {
GOOD: "好评",
MODERATE: "中评",
WORSE: "差评",
HAVEIMAGE: "有图",
fail: function () {
uni.showToast({
title: '保存失败',
duration: 2000,
icon: 'none',
})
},
commDetail: [],
dataTotal: 0,
opid: "",
};
},
async onLoad(options) {
this.getGoodsCommentsFun(options.id);
this.getGoodsCommentsNum(options.id);
this.opid = options.id;
},
onReachBottom() {
this.params.pageNumber++;
this.getGoodsCommentsFun(this.opid);
},
methods: {
commentScore(item) {
return item.descriptionScore || item.deliveryScore || 0;
},
})
}
splitImg(val) {
if (val && val.split(",")) {
return val.split(",");
} else if (val) {
return val;
} else {
return false;
}
},
function loadmore() {
params.pageNumber++
getGoodsCommentsFun(opid.value)
}
getGoodsCommentsFun(id) {
this.status = "loading";
membersApi.getGoodsComments(id, this.params).then((res) => {
if (
res.data.result.records == [] ||
res.data.result.records == "" ||
res.data.result.records == null
) {
this.status = "noMore";
return false;
}
this.commDetail = this.commDetail.concat(res.data.result.records);
this.dataTotal = res.data.result.total;
this.status = "loadmore";
});
},
onLoad((options: any) => {
getGoodsCommentsFun(options.id)
getGoodsCommentsNum(options.id)
opid.value = options.id
})
getGoodsCommentsNum(id) {
membersApi.getGoodsCommentsCount(id).then((res) => {
if (res.statusCode === 200) {
this.commentDetail = res.data.result;
}
});
},
select(index) {
this.params = {
pageNumber: 1,
pageSize: 10,
};
this.selectIndex = index;
this.params.grade = ["", "GOOD", "MODERATE", "WORSE", ""][
this.selectIndex
];
this.selectIndex === 4 ? (this.params.haveImage = 1) : true;
this.commDetail = [];
if (this.selectIndex === 0) {
this.params = {
pageNumber: 1,
pageSize: 10,
grade: "",
};
}
this.getGoodsCommentsFun(this.opid);
},
preview(urls, index) {
uni.previewImage({
current: index,
urls: urls,
longPressActions: {
itemList: ["保存图片"],
success: function () {
uni.showToast({
title: "保存成功",
duration: 2000,
icon: "none",
});
},
fail: function () {
uni.showToast({
title: "保存失败",
duration: 2000,
icon: "none",
});
},
},
});
},
},
};
onReachBottom(() => {
params.pageNumber++
getGoodsCommentsFun(opid.value)
})
</script>
<style lang="scss" scoped>

View File

@@ -3,89 +3,81 @@
<chat></chat>
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
},
onLoad (e) {
// 腾讯云智服客服插件需在 manifest.json 的 mp-weixin.plugins 中注册后方可使用,
// 未注册时 requirePlugin 会抛出 "has not registered any plugins",此处做容错处理。
let chat
try {
chat = requirePlugin('myPlugin')
} catch (err) {
uni.showToast({
title: '客服功能暂未配置',
icon: 'none',
duration: 2000
})
return
}
const params = JSON.parse((decodeURIComponent(e.params)))
chat.init({
sign: params.mpSign, //必传,公司渠道唯一标识,腾讯云智服后台系统创建「小程序插件」渠道后,在「渠道管理」获取
token: params.token, //非必填
uid: params.uuid, //用户唯一标识,如果没有则不填写,默认为空
title: params.storageName, //非必填,如果未填写,默认获取配置标题
isRMB: '', //商品是否显示人民币¥,默认显示false不显示
data: { //参数c1,c2,c3,c4,c5用于传递用户信息参数d1,d2,d3,d4,d5,d6用于传递商品信息默认为空
c1: '',
c2: '',
c3: '',
c4: '',
c5: '',
d1: params.goodsName, //商品描述
d2: params.price, //价格
d3: '', //原价格
d4: params.goodsImg, //展示商品图片链接
d5: '', //商品跳转链接
d6: params.goodsId, //商品id
data: ''//加密串,非必填
},
viewUrl(res){ //需要跳转外部链接则需要配置一个web-view
if (res) {
wx.navigateTo({
url: '/pages/webview/index?href=' + res
})
}
},
setTitle(res){ //设置标题
if (res) {
wx.setNavigationBarTitle({
title: res
})
}
},
setBarColor(res) { //设置导航栏背景色
if (res) {
wx.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: res
})
}
},
success(res){ //初始化成功时调用
if (res.data == 'success') {
console.log('success');
}
},
fail(res){ //初始化失败时调用
if (res.data == 'initError') {
console.log(res.message);
}
},
leave(res){ //离开会话页面
if (res) {
console.log(res);
}
}
})
}
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app'
onLoad((e: any) => {
// 腾讯云智服客服插件需在 manifest.json 的 mp-weixin.plugins 中注册后方可使用,
// 未注册时 requirePlugin 会抛出 "has not registered any plugins",此处做容错处理。
let chat
try {
chat = requirePlugin('myPlugin')
} catch (err) {
uni.showToast({
title: '客服功能暂未配置',
icon: 'none',
duration: 2000
})
return
}
</script>
const params = JSON.parse(decodeURIComponent(e.params))
chat.init({
sign: params.mpSign,
token: params.token,
uid: params.uuid,
title: params.storageName,
isRMB: '',
data: {
c1: '',
c2: '',
c3: '',
c4: '',
c5: '',
d1: params.goodsName,
d2: params.price,
d3: '',
d4: params.goodsImg,
d5: '',
d6: params.goodsId,
data: ''
},
viewUrl(res: string){
if (res) {
wx.navigateTo({
url: '/pages/webview/index?href=' + res
})
}
},
setTitle(res: string){
if (res) {
wx.setNavigationBarTitle({
title: res
})
}
},
setBarColor(res: string) {
if (res) {
wx.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: res
})
}
},
success(res: any){
if (res.data == 'success') {
console.log('success');
}
},
fail(res: any){
if (res.data == 'initError') {
console.log(res.message);
}
},
leave(res: any){
if (res) {
console.log(res);
}
}
})
})
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -9,41 +9,35 @@
</div>
</template>
<script>
import { getLicencePhoto } from "@/api/store.js";
export default {
data() {
return {
storeData: {},
};
},
onLoad(options) {
this.getStoreLicencePhoto(options.id);
},
methods: {
async getStoreLicencePhoto(id) {
let res = await getLicencePhoto(id);
if (res.data.success) {
this.storeData = res.data.result;
}
},
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getLicencePhoto } from '@/api/store.js'
/**
* 点击图片放大或保存
*/
preview() {
uni.previewImage({
current: 0,
urls: [this.storeData.licencePhoto],
longPressActions: {
itemList: ["保存图片"],
success: function (data) {},
fail: function (err) {},
},
});
const storeData = ref<any>({})
async function getStoreLicencePhoto(id: string) {
const res = await getLicencePhoto(id)
if (res.data.success) {
storeData.value = res.data.result
}
}
function preview() {
uni.previewImage({
current: 0,
urls: [storeData.value.licencePhoto],
longPressActions: {
itemList: ['保存图片'],
success: function () {},
fail: function () {},
},
},
};
})
}
onLoad((options: any) => {
getStoreLicencePhoto(options.id)
})
</script>
<style lang="scss" scoped>
@@ -53,4 +47,4 @@ export default {
margin: 20rpx 0;
}
}
</style>
</style>

View File

@@ -64,88 +64,82 @@
</view>
</template>
<script>
import * as API_Members from "@/api/members.js";
export default {
data() {
return {
lightColor: this.$lightColor,
commDetail: {},
readMoreInited: {},
grade: "",
params: {
pageNumber: 1,
pageSize: 10,
grade: "",
},
};
},
props: {
goodsDetail: {
default: () => ({}),
type: Object,
},
<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue'
import * as API_Members from '@/api/members.js'
import { noPassByName } from '@/utils/filters.js'
import { useStore } from '@/store'
const props = defineProps<{
goodsDetail?: Record<string, any>
}>()
const store = useStore()
const lightColor = computed(() => store.getters.lightColor)
const commDetail = ref<any>({})
const readMoreInited = ref<Record<number, boolean>>({})
const grade = ref('')
const params = {
pageNumber: 1,
pageSize: 10,
grade: '',
}
const uReadMore = ref<any>()
watch(
() => props.goodsDetail,
(val) => {
if (val && val.goodsId) {
grade.value = val.grade
getGoodsCommentsMethods()
}
},
{ deep: true, immediate: true }
)
watch: {
goodsDetail: {
handler(val) {
if (val && val.goodsId) {
this.grade = val.grade;
this.getGoodsCommentsMethods();
}
},
deep: true,
immediate: true,
},
},
function commentImages(item: any) {
const images = item.images || item.image
if (!images) return []
return typeof images === 'string' ? images.split(',').filter(Boolean) : images
}
methods: {
commentImages(item) {
const images = item.images || item.image;
if (!images) return [];
return typeof images === "string" ? images.split(",").filter(Boolean) : images;
},
function initReadMore(index: number) {
if (readMoreInited.value[index]) return
readMoreInited.value[index] = true
nextTick(() => {
const refs = uReadMore.value
const target = Array.isArray(refs) ? refs[index] : refs
target?.init?.()
})
}
initReadMore(index) {
if (this.readMoreInited[index]) return;
this.readMoreInited[index] = true;
this.$nextTick(() => {
const refs = this.$refs.uReadMore;
const target = Array.isArray(refs) ? refs[index] : refs;
target?.init?.();
});
},
function getGoodsCommentsMethods() {
if (!props.goodsDetail?.goodsId) return
API_Members.getGoodsComments(props.goodsDetail.goodsId, params).then((res) => {
readMoreInited.value = {}
commDetail.value = res.data.result || {}
nextTick(() => {
const records = commDetail.value.records || []
records.slice(0, 2).forEach((_: any, index: number) => {
setTimeout(() => initReadMore(index), 50 * (index + 1))
})
})
})
}
getGoodsCommentsMethods() {
if (!this.goodsDetail.goodsId) return;
API_Members.getGoodsComments(this.goodsDetail.goodsId, this.params).then((res) => {
this.readMoreInited = {};
this.commDetail = res.data.result || {};
this.$nextTick(() => {
const records = this.commDetail.records || [];
records.slice(0, 2).forEach((_, index) => {
setTimeout(() => this.initReadMore(index), 50 * (index + 1));
});
});
});
},
function toComment(id: string | number, gradeVal: string | number) {
uni.navigateTo({
url: `/pages/product/comment?id=${id}&grade=${gradeVal}`,
})
}
toComment(id, grade) {
uni.navigateTo({
url: `/pages/product/comment?id=${id}&grade=${grade}`,
});
},
previewImg(urls, index) {
uni.previewImage({
urls,
indicator: "number",
current: index,
});
},
},
};
function previewImg(urls: string[], index: number) {
uni.previewImage({
urls,
indicator: 'number',
current: index,
})
}
</script>
<style lang="scss" scoped>

View File

@@ -2,12 +2,9 @@
</template>
<script>
export default {
}
<script setup lang="ts">
</script>
<style lang="scss" scoped>
</style>
</style>

View File

@@ -39,30 +39,32 @@
</view>
</template>
<script>
import { getGoodsMessage } from "@/api/goods";
export default {
data() {
return {
goodsDetail: "",
style: {
img:"display:block;width:100%"
}
};
},
props: ["res", "goodsId", "goodsParams"],
computed: {
hasDetailContent() {
return !!(this.res?.mobileIntro || (this.goodsParams && this.goodsParams.length));
},
},
async mounted() {
let res = await getGoodsMessage(this.goodsId);
if (res.data.success) {
this.goodsDetail = res.data.result;
}
},
};
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { getGoodsMessage } from '@/api/goods'
const props = defineProps<{
res?: any
goodsId?: string | number
goodsParams?: any[]
}>()
const goodsDetail = ref('')
const style = {
img: 'display:block;width:100%',
}
const hasDetailContent = computed(() => {
return !!(props.res?.mobileIntro || (props.goodsParams && props.goodsParams.length))
})
onMounted(async () => {
if (!props.goodsId) return
const res = await getGoodsMessage(props.goodsId)
if (res.data.success) {
goodsDetail.value = res.data.result
}
})
</script>
<style lang="scss" scoped>

View File

@@ -5,14 +5,12 @@
</view>
</template>
<script>
<script setup lang="ts">
import goodsList from '@/components/m-goods-list/list.vue'
export default {
props: ["res"],
components:{goodsList},
methods: {
}
};
defineProps<{
res?: any[]
}>()
</script>
<style lang="scss" scoped>

View File

@@ -27,30 +27,33 @@
<view class="swiper-dots">{{ current }}/{{ video ? res.length + 1 : res.length }}</view>
</view>
</template>
<script>
export default {
data() {
return {
current: 1,
html: ""
};
},
props: ["res", 'video'],
watch: {
video(val) {
this.html = '<video muted="muted" ref="videoPlay" style="width:100%; height:100%;" src=' + val + ' page-gesture show-mute-btn autoplay webkit-playsinline="" playsinline="" ></video>'
<script setup lang="ts">
import { ref, watch } from 'vue'
const props = defineProps<{
res?: string[]
video?: string
}>()
const current = ref(1)
const html = ref('')
watch(
() => props.video,
(val) => {
if (val) {
html.value =
'<video muted="muted" ref="videoPlay" style="width:100%; height:100%;" src=' +
val +
' page-gesture show-mute-btn autoplay webkit-playsinline="" playsinline="" ></video>'
}
},
methods: {
// 轮播图对应的dot
swiperChange(e) {
this.current = e.detail.current + 1;
},
},
mounted() {
console.log(this.video)
}
};
{ immediate: true }
)
function swiperChange(e: any) {
current.value = e.detail.current + 1
}
</script>
<style lang="scss" scoped>
.carousel {

View File

@@ -1,6 +1,13 @@
.showBack {
margin-top: calc( var(--status-bar-height) + 20px ) !important;
}
margin-top: calc(var(--status-bar-height) + 20px) !important;
}
/* 商品详情底栏改为 flex 布局后,避免全局 mp-iphonex-bottom 把高度撑乱 */
.page-bottom.mp-iphonex-bottom {
height: auto !important;
min-height: 100rpx;
box-sizing: border-box;
padding-top: 10rpx;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}

View File

@@ -26,83 +26,81 @@
<m-city :provinceData="cityList" headTitle="区域选择" ref="cityPicker" pickerSize="4"></m-city>
</u-popup>
</template>
<script>
import setup from "@/components/m-buy/popup.js";
/************请求存储***************/
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import setup from '@/components/m-buy/popup.js'
import * as API_Address from '@/api/address.js'
import { clearStrComma, isLogin } from '@/utils/filters.js'
import * as API_Address from "@/api/address.js";
export default {
data() {
return {
checked: "",
setup,
addressDetail: "",
cityList: [
{
id: "",
localName: "请选择",
children: [],
},
],
};
const props = defineProps<{
goodsId?: string | number
addressFlag?: boolean
}>()
const emit = defineEmits<{
closeAddress: [val: boolean]
deliveryData: [val: any]
}>()
const checked = ref<any>('')
const addressDetail = ref<any>('')
const cityList = ref([
{
id: '',
localName: '请选择',
children: [],
},
props: ["goodsId", "addressFlag"],
])
mounted() {
if (this.isLogin("auth")) {
this.getShippingAddress();
} else {
uni.navigateTo({
url: "/pages/passport/login",
});
}
},
onMounted(() => {
if (isLogin('auth')) {
getShippingAddress()
} else {
uni.navigateTo({
url: '/pages/passport/login',
})
}
})
methods: {
/**关闭地址 */
closeAddress() {
this.$emit("closeAddress", false);
this.$emit("deliveryData", this.checked);
},
function closeAddress() {
emit('closeAddress', false)
emit('deliveryData', checked.value)
}
getpicker() {
uni.navigateTo({
url: "/pages/mine/address/add",
});
this.closeAddress();
},
function getpicker() {
uni.navigateTo({
url: '/pages/mine/address/add',
})
closeAddress()
}
/**获取地址 */
getShippingAddress() {
if (this.isLogin("auth")) {
API_Address.getAddressList(1, 50).then((res) => {
if (res.data.success) {
this.addressDetail = res.data.result.records;
let addr = res.data.result.records.filter((item) => {
return item.isDefault == 1;
});
function getShippingAddress() {
if (isLogin('auth')) {
API_Address.getAddressList(1, 50).then((res) => {
if (res.data.success) {
addressDetail.value = res.data.result.records
const addr = res.data.result.records.filter((item: any) => {
return item.isDefault == 1
})
if (addr[0]) {
this.checked = addr[0];
this.$emit("deliveryData", this.checked);
}
}
});
if (addr[0]) {
checked.value = addr[0]
emit('deliveryData', checked.value)
}
}
},
})
}
}
/**点击地址返回父级商品状态 */
clickAddress(val) {
this.checked = val;
function clickAddress(val: any) {
checked.value = val
this.addressDetail.forEach((item) => {
item.isDefault = false;
});
val.isDefault = !val.isDefault;
this.$emit("deliveryData", this.checked);
},
},
};
addressDetail.value.forEach((item: any) => {
item.isDefault = false
})
val.isDefault = !val.isDefault
emit('deliveryData', checked.value)
}
</script>
<style lang="scss" scoped>
.light {
@@ -165,4 +163,4 @@ export default {
line-height: 90rpx;
font-size: 34rpx;
}
</style>
</style>

View File

@@ -23,66 +23,57 @@
</view>
</template>
<script>
import * as API_Promotions from "@/api/promotions";
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import * as API_Promotions from '@/api/promotions'
import configs from '@/config/config'
export default {
data() {
return {
configs,
userImage:configs.defaultUserPhoto,
import { noPassByName } from '@/utils/filters.js'
import { useStore } from '@/store'
joinBtnStyle: {
background: this.$lightColor,
color: "#fff",
},
/** 待成团订单 */
assembleOrder: "",
const props = defineProps<{
res?: any
}>()
/** 查看更多待成团订单 */
assembleOrderAll: "",
};
},
props: ["res"],
const emit = defineEmits<{
'to-assemble-buy-now': [order: any]
}>()
watch: {
res: {
handler() {
if (this.res && this.res.length != 0) {
Object.keys(this.res).forEach((item) => {
let key = item.split("-");
if (key && key[0] == "PINTUAN") {
this.getAssembleInfo(item);
}
});
const store = useStore()
const userImage = configs.defaultUserPhoto
const assembleOrder = ref<any[]>([])
const joinBtnStyle = computed(() => ({
background: store.getters.lightColor,
color: '#fff',
}))
watch(
() => props.res,
(res) => {
if (res && res.length != 0) {
Object.keys(res).forEach((item) => {
const key = item.split('-')
if (key && key[0] == 'PINTUAN') {
getAssembleInfo(item)
}
},
immediate: true,
},
// assembleOrder(val) {
// this.$emit("assembleOrder", val);
// },
})
}
},
computed: {},
mounted() {},
methods: {
// 获取此商品所有待成团的订单
getAssembleInfo(val) {
let id = this.res[val].id;
API_Promotions.getPromotionGroupMember(id).then((res) => {
if (res.data.success) {
console.warn(res.data.result);
this.assembleOrder = res.data.result;
}
});
},
{ immediate: true }
)
buy(order) {
this.$emit("to-assemble-buy-now", order);
},
},
};
function getAssembleInfo(val: string) {
const id = props.res[val].id
API_Promotions.getPromotionGroupMember(id).then((res) => {
if (res.data.success) {
assembleOrder.value = res.data.result
}
})
}
function buy(order: any) {
emit('to-assemble-buy-now', order)
}
</script>
<style lang="scss" scoped>

View File

@@ -106,119 +106,118 @@
</view>
</template>
<script>
export default {
data() {
return {
countdownData: {},
countdownTimer: null,
};
<script setup lang="ts">
import { computed, onUnmounted, ref, watch } from 'vue'
import { goodsFormatPrice } from '@/utils/filters.js'
const props = defineProps<{
res?: any
detail?: any
}>()
const countdownData = ref<Record<number, any>>({})
let countdownTimer: ReturnType<typeof setInterval> | null = null
const promotionList = computed(() => {
if (!props.res || typeof props.res !== 'object') return []
return Object.keys(props.res).map((key) => ({
...(props.res[key] || {}),
__key: key.split('-')[0],
}))
})
watch(
promotionList,
(list) => {
refreshCountdown(list)
startCountdownTimer()
},
props: {
res: {
type: null,
default: {},
},
detail: {
type: null,
default: {},
},
},
computed: {
promotionList() {
if (!this.res || typeof this.res !== "object") return [];
return Object.keys(this.res).map((key) => ({
...(this.res[key] || {}),
__key: key.split("-")[0],
}));
},
},
watch: {
promotionList: {
handler(list) {
this.refreshCountdown(list);
this.startCountdownTimer();
},
immediate: true,
},
},
beforeUnmount() {
this.clearCountdownTimer();
},
methods: {
clearCountdownTimer() {
if (this.countdownTimer) {
clearInterval(this.countdownTimer);
this.countdownTimer = null;
}
},
startCountdownTimer() {
this.clearCountdownTimer();
this.countdownTimer = setInterval(() => {
this.refreshCountdown(this.promotionList);
}, 1000);
},
refreshCountdown(list = this.promotionList) {
const next = {};
list.forEach((promotion, index) => {
next[index] = this.msToTimeData(this.getCountDownMs(promotion));
});
this.countdownData = next;
},
padTime(val) {
return String(val ?? 0).padStart(2, "0");
},
countdownDisplayHours(index) {
const data = this.countdownData[index];
if (!data) return 0;
return data.days * 24 + data.hours;
},
msToTimeData(ms) {
const DAY = 86400000;
const HOUR = 3600000;
const MINUTE = 60000;
const SECOND = 1000;
const time = Math.max(Number(ms) || 0, 0);
return {
days: Math.floor(time / DAY),
hours: Math.floor((time % DAY) / HOUR),
minutes: Math.floor((time % HOUR) / MINUTE),
seconds: Math.floor((time % MINUTE) / SECOND),
};
},
parsePromotionTime(val) {
if (val == null || val === "") return 0;
if (typeof val === "number" && !Number.isNaN(val)) {
return val < 1e12 ? val * 1000 : val;
}
const text = String(val).trim();
if (/^\d+$/.test(text)) {
const num = Number(text);
return num < 1e12 ? num * 1000 : num;
}
const parsed = Date.parse(text.replace(/-/g, "/"));
return Number.isNaN(parsed) ? 0 : parsed;
},
getCountDownMs(promotion) {
if (!promotion || !promotion.endTime) return 0;
const now = Date.now();
const startMs = this.parsePromotionTime(promotion.startTime || promotion.start_time);
const endMs = this.parsePromotionTime(promotion.endTime);
if (!endMs) return 0;
const target = startMs && now < startMs ? startMs : endMs;
const remain = target - now;
return remain > 0 ? remain : 0;
},
getIsTimer(val) {
const now = Date.now();
const startMs = this.parsePromotionTime(val.startTime || val.start_time);
if (startMs && now < startMs) {
return "距离活动开始";
}
return "距离活动结束";
},
},
};
{ immediate: true }
)
onUnmounted(() => {
clearCountdownTimer()
})
function clearCountdownTimer() {
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
}
function startCountdownTimer() {
clearCountdownTimer()
countdownTimer = setInterval(() => {
refreshCountdown(promotionList.value)
}, 1000)
}
function refreshCountdown(list = promotionList.value) {
const next: Record<number, any> = {}
list.forEach((promotion, index) => {
next[index] = msToTimeData(getCountDownMs(promotion))
})
countdownData.value = next
}
function padTime(val: number | undefined) {
return String(val ?? 0).padStart(2, '0')
}
function countdownDisplayHours(index: number) {
const data = countdownData.value[index]
if (!data) return 0
return data.days * 24 + data.hours
}
function msToTimeData(ms: number) {
const DAY = 86400000
const HOUR = 3600000
const MINUTE = 60000
const SECOND = 1000
const time = Math.max(Number(ms) || 0, 0)
return {
days: Math.floor(time / DAY),
hours: Math.floor((time % DAY) / HOUR),
minutes: Math.floor((time % HOUR) / MINUTE),
seconds: Math.floor((time % MINUTE) / SECOND),
}
}
function parsePromotionTime(val: any) {
if (val == null || val === '') return 0
if (typeof val === 'number' && !Number.isNaN(val)) {
return val < 1e12 ? val * 1000 : val
}
const text = String(val).trim()
if (/^\d+$/.test(text)) {
const num = Number(text)
return num < 1e12 ? num * 1000 : num
}
const parsed = Date.parse(text.replace(/-/g, '/'))
return Number.isNaN(parsed) ? 0 : parsed
}
function getCountDownMs(promotion: any) {
if (!promotion || !promotion.endTime) return 0
const now = Date.now()
const startMs = parsePromotionTime(promotion.startTime || promotion.start_time)
const endMs = parsePromotionTime(promotion.endTime)
if (!endMs) return 0
const target = startMs && now < startMs ? startMs : endMs
const remain = target - now
return remain > 0 ? remain : 0
}
function getIsTimer(val: any) {
const now = Date.now()
const startMs = parsePromotionTime(val.startTime || val.start_time)
if (startMs && now < startMs) {
return '距离活动开始'
}
return '距离活动结束'
}
</script>
<style scoped lang="scss">

View File

@@ -1,79 +1,95 @@
<template>
<view class="wrapper">
<div class="coupon-empty" v-if="!res">暂无优惠券</div>
<view class="coupon-List" v-for="(item, index) in couponRes" :key="index">
<view class="coupon-empty" v-if="!couponRes.length">暂无优惠券</view>
<view class="coupon-List" v-for="(item, index) in couponRes" :key="item.id || index">
<view class="coupon-item">
<view class="top">
<div class="price">
<span class="price-num" v-if="item.couponType == 'DISCOUNT'">{{ item.couponDiscount }}</span>
<span class="price-num" v-if="item.couponType == 'PRICE'">{{unitPrice(item.price) }}</span>
</div>
<view class="text">
<div class="coupon-List-title">
<view v-if="item.scopeType">
<span v-if="item.scopeType == 'ALL' && item.storeId == '0'">全平台</span>
<span v-if="item.scopeType == 'PORTION_GOODS_CATEGORY'">仅限品类</span>
<view v-else>{{
item.storeName == "platform" ? "全平台" : item.storeName + "店铺"
}}使用</view>
</view>
</div>
<div>{{unitPrice(item.consumeThreshold) }}可用</div>
<view class="price">
<text class="price-num" v-if="item.couponType == 'DISCOUNT'">{{ item.couponDiscount }}</text>
<text class="price-num" v-else-if="item.couponType == 'PRICE'">{{ unitPrice(item.price) }}</text>
</view>
<view class="lingqu-btn" @click="getCoupon(item, index)">
<div class="lingqu-text" :class="yhqFlag[index] ? 'cur' : ''">
{{ yhqFlag[index] ? "已领取或领完" : "立即领取" }}
</div>
<view class="text">
<view class="coupon-List-title">
<text v-if="item.scopeType == 'ALL' && item.storeId == '0'">全平台</text>
<text v-else-if="item.scopeType == 'PORTION_GOODS_CATEGORY'">仅限品类</text>
<text v-else-if="item.scopeType == 'PORTION_GOODS'">部分商品</text>
<text v-else>{{ item.storeName == 'platform' ? '全平台' : item.storeName + '店铺' }}使用</text>
</view>
<text>{{ unitPrice(item.consumeThreshold) }}可用</text>
</view>
<view class="lingqu-btn" @tap="getCoupon(item, index)">
<view class="lingqu-text" :class="{ cur: yhqFlag[index] }">
{{ yhqFlag[index] ? '已领取或领完' : '立即领取' }}
</view>
</view>
</view>
<view class="line"></view>
<view class="time">{{ item.startTime /unixToDate(1000) }} - {{ item.endTime /unixToDate(1000) }}</view>
<view class="time">{{ formatCouponTime(item.startTime) }} - {{ formatCouponTime(item.endTime) }}</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
yhqFlag: [], //获取优惠券判断是否点击
couponRes: [],
};
},
props: {
res: {
type: null,
default: "",
},
},
watch: {
res: {
handler() {
if (this.res && this.res.length != 0) {
Object.keys(this.res).forEach((item) => {
let key = item.split("-")[0];
if (key === "COUPON") {
this.couponRes.push(this?.res[item]);
<script setup lang="ts">
import { ref, watch } from 'vue'
import { unitPrice, unixToDate } from '@/utils/filters.js'
}
});
}
},
immediate: true,
},
},
methods: {
// 提交优惠券
getCoupon(item, index) {
this.yhqFlag[index] = true;
this.$emit("getCoupon", item);
},
},
};
const props = defineProps<{
res?: any
}>()
const emit = defineEmits<{
getCoupon: [item: any]
}>()
const yhqFlag = ref<boolean[]>([])
const couponRes = ref<any[]>([])
function formatCouponTime(val: any) {
if (val == null || val === '') return ''
const text = String(val).trim()
if (!text) return ''
if (text.includes('-') || text.includes('/')) {
return text.length > 10 ? text.slice(0, 16) : text
}
const num = Number(text)
if (!Number.isFinite(num)) return text
const seconds = num < 1e12 ? num : Math.floor(num / 1000)
return unixToDate(seconds, 'yyyy-MM-dd')
}
watch(
() => props.res,
(res) => {
const list: any[] = []
if (res && typeof res === 'object') {
Object.keys(res).forEach((item) => {
if (item.split('-')[0] === 'COUPON') {
list.push(res[item])
}
})
}
couponRes.value = list
yhqFlag.value = list.map(() => false)
},
{ immediate: true, deep: true }
)
function getCoupon(item: any, index: number) {
if (yhqFlag.value[index]) return
yhqFlag.value[index] = true
yhqFlag.value = [...yhqFlag.value]
emit('getCoupon', item)
}
</script>
<style lang="scss" scoped>
.coupon-empty {
color: #999;
font-size: 26rpx;
padding: 20rpx 0;
}
.coupon-item {
width: 100%;
height: 100%;
@@ -85,10 +101,12 @@
.coupon-List {
display: flex;
flex-direction: column;
height: 230rpx;
min-height: 230rpx;
background: #e9ebfb;
margin: 30rpx 0;
padding: 10rpx 30rpx;
border-radius: 12rpx;
box-sizing: border-box;
.line {
height: 1px;
@@ -119,15 +137,17 @@
.time {
flex: 1;
font-size: 24rpx;
align-items: center;
color: #666;
display: flex;
align-items: center;
padding: 12rpx 0 8rpx;
}
}
.top {
height: 140rpx;
min-height: 140rpx;
display: flex;
align-items: center;
.price {
width: 33%;
@@ -135,48 +155,56 @@
color: #6772e5;
font-size: 40rpx;
display: flex;
height: 100%;
align-items: center;
flex-shrink: 0;
.price-num {
font-size: 50rpx;
line-height: 1.2;
}
}
.text {
width: 33%;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
font-size: 26rpx;
color: 333;
margin-left: 40rpx;
color: #333;
margin-left: 20rpx;
min-width: 0;
.coupon-List-title {
font-size: 30rpx;
font-size: 28rpx;
font-weight: bold;
margin-bottom: 8rpx;
line-height: 1.4;
}
}
.lingqu-btn {
display: flex;
align-items: center;
margin-left: 40rpx;
flex-shrink: 0;
margin-left: 16rpx;
.lingqu-text {
width: 140rpx;
height: 40rpx;
min-width: 140rpx;
height: 56rpx;
padding: 0 16rpx;
text-align: center;
line-height: 40rpx;
line-height: 56rpx;
color: #fff;
background: #6772e5;
border-radius: 5px;
font-size: 26rpx;
border-radius: 28rpx;
font-size: 24rpx;
box-sizing: border-box;
&.cur {
background: none;
transform: rotate(45deg) translate(10rpx, -46rpx);
background: #ccc;
color: #fff;
transform: none;
}
}
}

View File

@@ -1,140 +1,134 @@
<template>
<view class="wrapper" v-if="res">
<view v-for="(prom, index) in Object.keys(res)" :key="index">
<view>
<view v-if="prom.split('-')[0] == 'FULL_DISCOUNT'">
<div class="res_prom_item" v-if="res[prom].fullMinus">
<u-tag text="满减" type="error"></u-tag>
<!-- TODO 后续将优化为可点击的商品以及优惠券显示明细 -->
<span class="pro-text"
>{{ res[prom].fullMoney }} 立减现金
<span class="price">{{ res[prom].fullMinus }}</span>
<span v-if="res[prom].couponFlag"> 赠送<span>优惠券</span></span>
<span v-if="res[prom].pointFlag"> 赠送{{ res[prom].point }}积分</span>
<span v-if="res[prom].giftFlag"> 赠送商品</span>
<span v-if="res[prom].freeFreightFlag">赠送包邮服务</span>
</span>
</div>
<div class="res_prom_item" v-if="res[prom].fullRate && res[prom].fullRateFlag">
<u-tag text="打折" type="error"></u-tag>
<span class="pro-text"
>{{ res[prom].fullMoney }}立享<span class="price"
>{{ res[prom].fullRate }}</span
>优惠</span
>
</div>
<view v-for="prom in promotionKeys" :key="prom">
<view v-if="getPromType(prom) == 'FULL_DISCOUNT'">
<view class="res_prom_item" v-if="res[prom].fullMinus">
<view class="deg_tag">满减</view>
<text class="pro-text">
{{ res[prom].fullMoney }} 立减现金
<text class="price">{{ res[prom].fullMinus }}</text>
<text v-if="res[prom].couponFlag"> 赠送优惠券</text>
<text v-if="res[prom].pointFlag"> 赠送{{ res[prom].point }}积分</text>
<text v-if="res[prom].giftFlag"> 赠送商品</text>
<text v-if="res[prom].freeFreightFlag"> 赠送包邮服务</text>
</text>
</view>
<view v-if="prom.split('-')[0] == 'PINTUAN'">
<div class="res_prom_item" v-if="res[prom].requiredNum">
<u-tag text="拼团" type="error"></u-tag>
<span class="pro-text"
>{{ res[prom].requiredNum }}人拼团 限购<span class="price"
>{{ res[prom].limitNum }}</span
></span
>
</div>
<view class="res_prom_item" v-if="res[prom].fullRate && res[prom].fullRateFlag">
<view class="deg_tag">打折</view>
<text class="pro-text">
{{ res[prom].fullMoney }}立享
<text class="price">{{ res[prom].fullRate }}</text>
优惠
</text>
</view>
</view>
<view v-if="prom.split('-')[0] == 'SECKILL'">
<div class="res_prom_item">
<u-tag text="限时抢购" type="error"></u-tag>
<span class="pro-text">限时抢购</span>
</div>
<view v-if="getPromType(prom) == 'PINTUAN'">
<view class="res_prom_item" v-if="res[prom].requiredNum">
<view class="deg_tag">拼团</view>
<text class="pro-text">
{{ res[prom].requiredNum }}人拼团 限购
<text class="price">{{ res[prom].limitNum }}</text>
</text>
</view>
</view>
<view v-if="prom.split('-')[0] == 'POINTS_GOODS'">
<div class="res_prom_item">
<u-tag text="积分活动" type="error"></u-tag>
<span class="pro-text">当前商品参与积分活动<span @click="handClickToJoinPromotion(prom)" class="href">点击此处参与活动</span></span>
</div>
<view v-if="getPromType(prom) == 'SECKILL'">
<view class="res_prom_item">
<view class="deg_tag">限时抢购</view>
<text class="pro-text">限时抢购</text>
</view>
</view>
<view v-if="prom.split('-')[0] == 'KANJIA'">
<div class="res_prom_item">
<u-tag text="砍价活动" type="error"></u-tag>
<span class="pro-text">当前商品参与砍价活动<span @click="handClickToJoinPromotion(prom)" class="href">点击此处参与活动</span></span>
</div>
<view v-if="getPromType(prom) == 'POINTS_GOODS'">
<view class="res_prom_item">
<view class="deg_tag">积分活动</view>
<text class="pro-text">
当前商品参与积分活动
<text @tap="handClickToJoinPromotion(prom)" class="href">点击此处参与活动</text>
</text>
</view>
</view>
<view v-if="getPromType(prom) == 'KANJIA'">
<view class="res_prom_item">
<view class="deg_tag">砍价活动</view>
<text class="pro-text">
当前商品参与砍价活动
<text @tap="handClickToJoinPromotion(prom)" class="href">点击此处参与活动</text>
</text>
</view>
</view>
</view>
<view v-if="!res">暂无促销活动</view>
<view v-if="!promotionKeys.length">暂无促销活动</view>
</view>
</template>
<script>
export default {
data() {
return {};
},
watch: {
res: {
handler() {
if (this.res && this.res.length != 0) {
Object.keys(this.res).forEach((item) => {
if (item != "COUPON") {
let key = item.split("-")[0];
this.res[item]._key = key;
}
});
}
},
immediate: true,
},
},
<script setup lang="ts">
import { computed } from 'vue'
props: {
// 父组件传递回来的数据
res: {
type: null,
default: "",
},
},
mounted() {},
methods: {
// 跳转到参与商品活动的详情列表中
handClickToJoinPromotion(val){
const promotion = {
"POINTS_GOODS": `/pages/promotion/point/detail?id=${this.res[val].id}`,
"KANJIA": `/pages/promotion/bargain/detail?id=${this.res[val].id}`,
}
const props = defineProps<{
res?: any
}>()
uni.navigateTo({
url:promotion[val.split('-')[0]]
})
const promotionKeys = computed(() => {
if (!props.res || typeof props.res !== 'object') return []
return Object.keys(props.res).filter((key) => getPromType(key) !== 'COUPON')
})
}
},
};
function getPromType(key: string) {
return key.split('-')[0]
}
function handClickToJoinPromotion(val: string) {
const type = getPromType(val)
const promotionMap: Record<string, string> = {
POINTS_GOODS: `/pages/promotion/point/detail?id=${props.res[val].id}`,
KANJIA: `/pages/promotion/bargain/detail?id=${props.res[val].id}`,
}
const url = promotionMap[type]
if (url) {
uni.navigateTo({ url })
}
}
</script>
<style lang="scss" scoped>
.deg_tag {
flex-shrink: 0;
color: $price-color;
padding: 4rpx 12rpx;
border: 2rpx solid $price-color;
border-radius: 6rpx;
font-size: 22rpx;
line-height: 1.4;
white-space: nowrap;
}
.pro-text {
flex: 1;
min-width: 0;
font-size: 26rpx;
font-family: PingFang SC, PingFang SC-Regular;
font-weight: 400;
text-align: left;
line-height: 1.6;
color: #333333;
margin-left: 20rpx;
word-spacing: 15rpx;
}
.wrapper {
display: block;
}
::v-deep .u-mode-light-error {
border: none;
}
.res_prom_item {
display: flex;
align-items: flex-start;
gap: 16rpx;
margin: 20rpx 0;
}
.price_image {
display: block;
.price {
color: $price-color;
}
.href{
.href {
color: $main-color;
}
</style>

View File

@@ -50,51 +50,45 @@
</view>
</view>
<view v-if="this.res != null && Object.keys(res).length == 0">暂无促销信息</view>
<view v-if="res != null && Object.keys(res).length == 0">暂无促销信息</view>
</view>
</template>
<script>
import promotion from './promotion_type';
export default {
data() {
return {
promotion,
couponList: ''
};
},
props: {
// 父组件传递回来的数据
res: {
type: null,
default: {}
}
},
watch: {
res: {
handler() {
if (this.res && this.res.length != 0 && this.res != null) {
Object.keys(this.res).forEach(item => {
let key = item.split('-')[0];
this.res[item].__key = key;
<script setup lang="ts">
import { ref, watch } from 'vue'
import promotion from './promotion_type'
if (item.split('-')[0] == 'COUPON') {
this.couponList = 'COUPON';
}
});
}
},
immediate: true
}
},
mounted() {},
methods: {
// 此方法条用父级方法
shutMask(val) {
this.$emit('shutMasks', val);
}
}
};
const props = defineProps<{
res?: any
}>()
const emit = defineEmits<{
shutMasks: [val: number]
}>()
const couponList = ref('')
watch(
() => props.res,
(res) => {
couponList.value = ''
if (res && res.length != 0 && res != null) {
Object.keys(res).forEach((item) => {
const key = item.split('-')[0]
res[item].__key = key
if (item.split('-')[0] == 'COUPON') {
couponList.value = 'COUPON'
}
})
}
},
{ immediate: true }
)
function shutMask(val: number) {
emit('shutMasks', val)
}
</script>
<style lang="scss" scoped>

Some files were not shown because too many files have changed in this diff Show More