新增营销系统、分销系统、会员功能、门店、提现功能
This commit is contained in:
183
views/account/balance/index.vue
Normal file
183
views/account/balance/index.vue
Normal file
@ -0,0 +1,183 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-24 15:17
|
||||
@description:index
|
||||
@update: 2024-01-24 15:17
|
||||
-->
|
||||
<script setup>
|
||||
import Header from '@/components/Header/index.vue'
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { useJump } from "@/hooks/useJump";
|
||||
import { useMainStore } from "@/store/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { usePaging } from "@/hooks/usePaging";
|
||||
import { pageUserBill } from "@/api/account/balance";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import { emptyOrderIcon } from "@/utils/images";
|
||||
import Empty from "@/components/Empty/index.vue";
|
||||
import moment from "moment";
|
||||
|
||||
const {scrollTop} = useScroll();
|
||||
|
||||
const {goRecharge} = useJump();
|
||||
|
||||
const mainStore = useMainStore();
|
||||
const {user} = storeToRefs(mainStore)
|
||||
|
||||
const {otherParams, list, refreshPage} = usePaging({
|
||||
request: pageUserBill,
|
||||
load: false
|
||||
});
|
||||
|
||||
onLoad(() => {
|
||||
otherParams.value.category = 'now_money'
|
||||
})
|
||||
|
||||
onShow(()=>{
|
||||
refreshPage()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff"> 我的余额
|
||||
</Header>
|
||||
<view class="balance">
|
||||
<view class="balance-detail flex flex-jc__sb flex-ai__center">
|
||||
<view class="left">
|
||||
<view class="title">账户余额</view>
|
||||
<view class="money">{{ user.nowMoney.toFixed(2) }}</view>
|
||||
</view>
|
||||
<view class="right">
|
||||
<view
|
||||
class="button"
|
||||
@click="goRecharge">充值
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="list-card">
|
||||
<view class="title">
|
||||
收支明细
|
||||
</view>
|
||||
<template v-if="list.length>0">
|
||||
<view
|
||||
class="row flex flex-ai__center flex-jc__sb"
|
||||
v-for="order in list"
|
||||
:key="order.id">
|
||||
<view class="left flex flex-ai__center">
|
||||
<image
|
||||
class="icon"
|
||||
v-if="false" />
|
||||
<view class="info">
|
||||
<view class="type-name">{{ order.title }}</view>
|
||||
<view class="time">{{ moment(order.createTime).format("YYYY-MM-DD HH:mm:ss") }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- error-->
|
||||
<view
|
||||
class="right success"
|
||||
:class="{error:order.pm === 0}">
|
||||
{{ order.pm === 0 ? '-' : '+' }}{{ order.number }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<Empty
|
||||
v-else
|
||||
:iconSrc="emptyOrderIcon"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.balance {
|
||||
@include usePadding(32, 32);
|
||||
width: 100%;
|
||||
|
||||
.balance-detail {
|
||||
@include usePadding(32, 32);
|
||||
background: #333333;
|
||||
color: #fff;
|
||||
width: 100%;
|
||||
border-radius: 15rpx;
|
||||
|
||||
.title {
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.money {
|
||||
font-size: 48rpx;
|
||||
}
|
||||
|
||||
.button {
|
||||
@include usePadding(38, 12);
|
||||
background: #fff;
|
||||
border-radius: 15rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.list-card {
|
||||
width: 100%;
|
||||
@include usePadding(32, 32);
|
||||
background: #fff;
|
||||
border-radius: 15rpx;
|
||||
margin-top: 30rpx;
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin: 24rpx 0;
|
||||
|
||||
.left {
|
||||
.icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 10rpx;
|
||||
background: #b9b9b9;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.type-name {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.right {
|
||||
@include usePadding(38, 7);
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #28C445;
|
||||
background: rgba(40, 196, 69, 0.1);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #EE6D46;
|
||||
background: #FFF7F5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
92
views/account/integral/index.scss
Normal file
92
views/account/integral/index.scss
Normal file
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @name: index
|
||||
* @author: kahu4
|
||||
* @date: 2024-01-19 14:26
|
||||
* @description:index
|
||||
* @update: 2024-01-19 14:26
|
||||
* */
|
||||
|
||||
.integral {
|
||||
width: 100%;
|
||||
@include usePadding(34, 34);
|
||||
|
||||
.num-card {
|
||||
width: 100%;
|
||||
border-radius: 15rpx;
|
||||
aspect-ratio: 682/278;
|
||||
background-size: 100% 100%;
|
||||
@include usePadding(48, 76);
|
||||
color: #663405;
|
||||
font-size: 28rpx;
|
||||
|
||||
.num {
|
||||
font-weight: bold;
|
||||
font-size: 64rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-row {
|
||||
width: 100%;
|
||||
gap: 16rpx;
|
||||
margin: 34rpx 0;
|
||||
|
||||
.tab-item {
|
||||
flex-grow: 1;
|
||||
@include usePadding(0, 16);
|
||||
background: #F6F8F8;
|
||||
transition: all .3s;
|
||||
border-radius: 8rpx;
|
||||
text-align: center;
|
||||
|
||||
&.current {
|
||||
background: #333333;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.integral-list {
|
||||
@include usePadding(32, 32);
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 15rpx;
|
||||
|
||||
.row {
|
||||
margin: 25rpx 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #333333;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.right {
|
||||
@include usePadding(38, 10);
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #28C445;
|
||||
background: rgba(40, 196, 69, 0.1);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #EE6D46;
|
||||
background: #FFF7F5;
|
||||
}
|
||||
}
|
||||
}
|
118
views/account/integral/index.vue
Normal file
118
views/account/integral/index.vue
Normal file
@ -0,0 +1,118 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-19 14:26
|
||||
@description:index
|
||||
@update: 2024-01-19 14:26
|
||||
-->
|
||||
<script setup>
|
||||
import './index.scss'
|
||||
import Header from '@/components/Header/index.vue'
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { onLoad, onPageScroll } from "@dcloudio/uni-app";
|
||||
import { accountIntegralBg, emptyOrderIcon } from "@/utils/images";
|
||||
import { ref } from "vue";
|
||||
import { pageIntegralBill } from "@/api/account/integral";
|
||||
import { usePaging } from "@/hooks/usePaging";
|
||||
import moment from "moment";
|
||||
import Empty from '@/components/Empty/index.vue'
|
||||
import { useMainStore } from "@/store/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
const {scrollTop} = useScroll();
|
||||
onPageScroll(() => {
|
||||
})
|
||||
|
||||
const mainStore = useMainStore()
|
||||
const {user} = storeToRefs(mainStore);
|
||||
|
||||
const filterTabs = [
|
||||
{label: '全部', value: 99},
|
||||
{label: '收入', value: 1},
|
||||
{label: '支出', value: 0}
|
||||
]
|
||||
|
||||
const tabCurrent = ref(99)
|
||||
|
||||
async function tabCurrentChange(item) {
|
||||
tabCurrent.value = item.value
|
||||
if (item.value === 99) {
|
||||
delete otherParams.value.pm
|
||||
} else {
|
||||
otherParams.value.pm = item.value
|
||||
}
|
||||
await refreshPage()
|
||||
}
|
||||
|
||||
// 账单列表
|
||||
const {otherParams, list, loading, refreshPage} = usePaging({
|
||||
request: pageIntegralBill,
|
||||
load: false
|
||||
})
|
||||
|
||||
onLoad(() => {
|
||||
otherParams.value = {}
|
||||
otherParams.value.category = "integral"
|
||||
refreshPage()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff"> 我的积分
|
||||
</Header>
|
||||
<view class="integral">
|
||||
<!-- 积分展示 -->
|
||||
<view
|
||||
class="num-card"
|
||||
:style="{backgroundImage:`url(${accountIntegralBg})`}">
|
||||
<view class="title">当前积分</view>
|
||||
<view class="num"> {{ user.integral || '0.00' }}</view>
|
||||
</view>
|
||||
<!-- tabs -->
|
||||
<view class="tabs-row flex flex-ai__center flex-jc__sb">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{current:tabCurrent === tab.value}"
|
||||
v-for="tab in filterTabs"
|
||||
:key="tab.value"
|
||||
@click="tabCurrentChange(tab)">
|
||||
{{ tab.label }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- list -->
|
||||
<view class="integral-list">
|
||||
<template v-if="list.length>0">
|
||||
<view
|
||||
class="row flex flex-jc__sb flex-ai__center"
|
||||
v-for="item in list">
|
||||
<view class="left">
|
||||
<view class="name">{{ item.title }}</view>
|
||||
<view class="time">{{ moment(item.createTime).format("YYYY-MM-DD HH:mm:ss") }}</view>
|
||||
</view>
|
||||
<view
|
||||
class="right"
|
||||
:class="{error:item.pm===0,success:item.pm===1}">
|
||||
{{ item.pm === 0 ? '-' : '+' }}{{ item.number }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template v-if="!loading && list.length===0">
|
||||
<Empty
|
||||
:icon-src="emptyOrderIcon"
|
||||
padding="0 0" />
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
|
||||
</style>
|
376
views/account/recharge/index.vue
Normal file
376
views/account/recharge/index.vue
Normal file
@ -0,0 +1,376 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-21 15:38
|
||||
@description:订单核销
|
||||
@update: 2024-01-21 15:38
|
||||
-->
|
||||
<script setup>
|
||||
import Header from "@/components/Header/index.vue"
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { wechatIcon } from "@/utils/images";
|
||||
import { useInterface } from "@/hooks/useInterface";
|
||||
import { computed, ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useRouter } from "@/hooks/useRouter";
|
||||
import { createRechargeOrder, getRechargeConfig } from "@/api/account/balance";
|
||||
import { doPayment, PayType } from "@/utils/paymentUtils";
|
||||
|
||||
// ========================= hooks ==========================
|
||||
const {toast, loading, hideLoading} = useInterface();
|
||||
const {scrollTop} = useScroll();
|
||||
const {getParams} = useRouter();
|
||||
|
||||
|
||||
// =========================== 充值方式 =========================
|
||||
const methodRenderList = ref([
|
||||
{
|
||||
label: '微信',
|
||||
value: 0,
|
||||
icon: wechatIcon
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
// ========================== 充值 =============================
|
||||
// 当前选中的充值配置
|
||||
const current = ref(undefined)
|
||||
// 充值配置
|
||||
const rechargeConfig = ref({
|
||||
content: '',
|
||||
customMin: 0, // 用户最小提现数
|
||||
customSwitch: 0, // 是否支持用户自定义金额,0关闭1开启
|
||||
rechargePackageBaseVOS: []
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取充值配置
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetRechargeConfig() {
|
||||
const res = await getRechargeConfig();
|
||||
// 是否开启了自定义
|
||||
if (res.customSwitch === 1) {
|
||||
res.rechargePackageBaseVOS.push({
|
||||
id: 'other',
|
||||
name: '其他',
|
||||
rechargeAmount: '',
|
||||
})
|
||||
}
|
||||
// 默认选中充值配置的第一项
|
||||
if (res.rechargePackageBaseVOS.length > 0) {
|
||||
current.value = res.rechargePackageBaseVOS[0].id
|
||||
rechargeForm.value.amount = res.rechargePackageBaseVOS[0].rechargeAmount
|
||||
rechargeForm.value.packageId = res.rechargePackageBaseVOS[0].id
|
||||
}
|
||||
rechargeConfig.value = res
|
||||
}
|
||||
|
||||
// 计算其他输入金额的位置
|
||||
const otherIndex = computed(() => rechargeConfig.value.rechargePackageBaseVOS.findIndex(i => i.id === 'other'))
|
||||
|
||||
/**
|
||||
* 改变金额tab项
|
||||
* @param item
|
||||
*/
|
||||
function currentDefaultMoneyItemChange(item) {
|
||||
current.value = item.id
|
||||
rechargeForm.value.packageId = item.id
|
||||
rechargeForm.value.amount = item.rechargeAmount
|
||||
}
|
||||
|
||||
/**
|
||||
* 其他金额输入,在此给form赋值
|
||||
* @param e
|
||||
*/
|
||||
function handleOtherInput(e) {
|
||||
rechargeForm.value.amount = parseInt(e.detail.value)
|
||||
}
|
||||
|
||||
// 是否正在请求
|
||||
const rechargeLoading = ref(false)
|
||||
// 发起充值的form表单
|
||||
const rechargeForm = ref({
|
||||
type: 0, // 0微信
|
||||
amount: 0,
|
||||
packageId: null
|
||||
})
|
||||
|
||||
/**
|
||||
* 提交充值创建充值订单
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function confirm() {
|
||||
try {
|
||||
rechargeLoading.value = true
|
||||
rechargeForm.value.amount = parseInt(rechargeForm.value.amount)
|
||||
// 如果是其他金额,校验有没有大于最低金额
|
||||
if (current.value === 'other') {
|
||||
delete rechargeForm.value.packageId
|
||||
if (rechargeForm.value.amount < rechargeConfig.value.customMin)
|
||||
return toast({title: `最低充值${ rechargeConfig.value.customMin }元`})
|
||||
}
|
||||
const orderId = await createRechargeOrder(rechargeForm.value);
|
||||
await doPayment({
|
||||
type: PayType["0"],
|
||||
payInfo: {orderId}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw new Error(e)
|
||||
} finally {
|
||||
rechargeLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onLoad(async (options) => {
|
||||
await doGetRechargeConfig()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff"> 充值
|
||||
</Header>
|
||||
<view class="verification">
|
||||
<view class="ver-card">
|
||||
<view class="title">选择充值面额</view>
|
||||
<view class="select-row">
|
||||
<view
|
||||
class="select-item"
|
||||
:class="{current:current === money.id}"
|
||||
v-for="money in rechargeConfig.rechargePackageBaseVOS"
|
||||
:key="money.value"
|
||||
@click="currentDefaultMoneyItemChange(money)">
|
||||
{{ money.id === 'other' ? money.name : money.rechargeAmount }}
|
||||
<view
|
||||
class="give"
|
||||
v-if="money.giftAmount>0">送{{ money.giftAmount }}元
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="input row"
|
||||
:class="{hidden:current !== 'other' }">
|
||||
<view class="flex flex-ai__center">
|
||||
<text class="price">¥</text>
|
||||
<input
|
||||
v-if="rechargeConfig.rechargePackageBaseVOS[otherIndex]"
|
||||
v-model="rechargeConfig.rechargePackageBaseVOS[otherIndex].rechargeAmount"
|
||||
type="number"
|
||||
placeholder="请输入充值金额"
|
||||
@input="handleOtherInput" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="ver-card">
|
||||
<view class="title">支付方式</view>
|
||||
<u-radio-group
|
||||
activeColor="#EE6D46"
|
||||
v-model="rechargeForm.type"
|
||||
placement="column"
|
||||
>
|
||||
<template
|
||||
v-for="item in methodRenderList"
|
||||
:key="item.value">
|
||||
<view
|
||||
class="flex flex-jc__sb flex-ai__center"
|
||||
@click="rechargeForm.type = item.value">
|
||||
<view class="input row method none-border none-bg">
|
||||
<view class="flex flex-ai__center">
|
||||
<image
|
||||
class="icon"
|
||||
:src="item.icon" />
|
||||
<text class="text">{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-radio
|
||||
:customStyle="{marginBottom: '8px'}"
|
||||
label=" "
|
||||
:name="item.value"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="btn-row row animation-button"
|
||||
:class="{disabled:rechargeForm.amount<=0}"
|
||||
@click="confirm">
|
||||
确认充值
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.verification {
|
||||
@include usePadding(34, 34);
|
||||
|
||||
.ver-card {
|
||||
@include usePadding(32, 32);
|
||||
margin-bottom: 24rpx;
|
||||
border-radius: 15rpx;
|
||||
background: #fff;
|
||||
|
||||
.title {
|
||||
text-align: left;
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.select-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 15rpx;
|
||||
margin: 24rpx 0;
|
||||
|
||||
.select-item {
|
||||
@include usePadding(0, 30);
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
background: #f6f8f8;
|
||||
border: 1rpx solid #f6f8f8;;
|
||||
border-radius: 10rpx;
|
||||
transition: all .3s;
|
||||
position: relative;
|
||||
|
||||
.give {
|
||||
@include usePadding(11, 5);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: #EE6D46;
|
||||
border-radius: 10rpx 0 10rpx 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.current {
|
||||
background: #fdf0ed;
|
||||
border: 1rpx solid #EE6D46;
|
||||
color: #EE6D46;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.input {
|
||||
@include useFlex(space-between, center);
|
||||
@include usePadding(21, 24);
|
||||
width: 100%;
|
||||
scale: 1;
|
||||
overflow: hidden;
|
||||
transition: all .3s;
|
||||
border-radius: 8rpx;
|
||||
background: #f6f8f8;
|
||||
transform-origin: left top;
|
||||
|
||||
.price {
|
||||
font-size: 48rpx;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
scale: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.method {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.none-border {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.none-bg {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.qr-scan {
|
||||
@include useFlex(center, center);
|
||||
margin-top: 40rpx !important;
|
||||
|
||||
image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
transition: all .3s;
|
||||
|
||||
&:active {
|
||||
scale: 1.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.btn-row {
|
||||
@include useFlex(center, center);
|
||||
height: 80rpx;
|
||||
border-radius: 15rpx;
|
||||
position: fixed;
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
bottom: 50rpx;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
background: #f3997d;
|
||||
|
||||
&:active {
|
||||
scale: 1;
|
||||
animation: disabledAnimation 200ms 15;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes disabledAnimation {
|
||||
0%, 90% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
30% {
|
||||
transform: translateX(-20rpx);
|
||||
}
|
||||
60% {
|
||||
transform: translateX(20rpx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
:deep(.u-radio) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
223
views/account/signIn/index.scss
Normal file
223
views/account/signIn/index.scss
Normal file
@ -0,0 +1,223 @@
|
||||
.sign-in {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 750rpx;
|
||||
height: 464rpx;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
&__inner {
|
||||
position: relative;
|
||||
@include usePadding(34, 0);
|
||||
|
||||
.bg-mask {
|
||||
width: 100%;
|
||||
height: 464rpx;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
.header-row {
|
||||
position: absolute;
|
||||
top: 38rpx;
|
||||
color: #fff;
|
||||
font-size: 90rpx;
|
||||
font-weight: bold;
|
||||
align-items: baseline;
|
||||
|
||||
.sub {
|
||||
font-weight: normal;
|
||||
font-size: 32rpx;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.sign-btn-row {
|
||||
position: absolute;
|
||||
top: 180rpx;
|
||||
@include useFlex(flex-start, center);
|
||||
@include usePadding(30, 0);
|
||||
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@include usePadding(14, 10);
|
||||
background: $white-color;
|
||||
color: $primary-color;
|
||||
font-size: 28rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-left: 15rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.sign-calendar {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@include usePadding(34, 25);
|
||||
|
||||
&__inner {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
overflow-x: scroll;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
column-gap: 16rpx;
|
||||
|
||||
.item {
|
||||
border-radius: 15rpx;
|
||||
font-size: 22rpx;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
background: #FFF0AF;
|
||||
|
||||
.item-box {
|
||||
@include usePadding(12, 17);
|
||||
width: 100%;
|
||||
background: #FFF0AF;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 12rpx;
|
||||
font-weight: bold;
|
||||
|
||||
image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.none {
|
||||
background: #FFFBE9;
|
||||
color: #999 !important;
|
||||
.item-box {
|
||||
@include usePadding(12, 17);
|
||||
width: 100%;
|
||||
background: #FFFBE9;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 12rpx;
|
||||
font-weight: bold;
|
||||
|
||||
image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pastDue {
|
||||
color: #999999 !important;
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sign-card {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 30rpx;
|
||||
margin-top: 15rpx;
|
||||
@include usePadding(32, 32);
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin: 24rpx 0;
|
||||
|
||||
.row-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.row-info {
|
||||
flex-grow: 1;
|
||||
|
||||
.name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sub {
|
||||
|
||||
|
||||
}
|
||||
|
||||
.btn {
|
||||
@include usePadding(32, 14);
|
||||
background: #f6f9f8;
|
||||
border-radius: 15rpx;
|
||||
|
||||
image {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
}
|
||||
|
||||
color: #333;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.disable {
|
||||
background: #D3D7DA !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
|
||||
.success-card {
|
||||
width: 80vw;
|
||||
aspect-ratio: 490/558;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
.inner {
|
||||
height: 100%;
|
||||
@include useFlex(center, center, column);
|
||||
color: #AF281D;
|
||||
font-size: 28rpx;
|
||||
|
||||
.count-row {
|
||||
padding-top: 130rpx;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: 90rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.button {
|
||||
@include usePadding(84, 21);
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
222
views/account/signIn/index.vue
Normal file
222
views/account/signIn/index.vue
Normal file
@ -0,0 +1,222 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-18 19:37
|
||||
@description:index
|
||||
@update: 2024-01-18 19:37
|
||||
-->
|
||||
<script setup>
|
||||
import './index.scss'
|
||||
import Header from '@/components/Header/index.vue'
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { onPageScroll } from "@dcloudio/uni-app";
|
||||
import { accountSignInBg, accountSignOk, accountSignStar, accountSignSuccessBg } from "@/utils/images";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { getIntegralRule, getSignInInfo, signIn } from "@/api/account/signIn";
|
||||
import { useInterface } from "@/hooks/useInterface";
|
||||
import { useMainStore } from "@/store/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
import Popup from "@/components/Popup.vue";
|
||||
import moment from "moment";
|
||||
|
||||
const {scrollTop} = useScroll();
|
||||
onPageScroll(() => {
|
||||
})
|
||||
|
||||
const mainStore = useMainStore()
|
||||
const {user} = storeToRefs(mainStore);
|
||||
const {toast} = useInterface()
|
||||
|
||||
// 签到信息
|
||||
const signInInfo = ref({
|
||||
signInDays: 0,
|
||||
todaySignIn: false,
|
||||
signInRecordList: []
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
/**
|
||||
* 获取签到信息
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetSignInInfo() {
|
||||
try {
|
||||
loading.value = true
|
||||
signInInfo.value = await getSignInInfo();
|
||||
await doGetIntegralRule()
|
||||
// 处理签到日历
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const successIntegral = ref(0)
|
||||
const successPopupRef = ref()
|
||||
|
||||
function openSuccessPopup() {
|
||||
successPopupRef.value.show()
|
||||
}
|
||||
|
||||
async function closeSuccessPopup() {
|
||||
await doGetSignInInfo()
|
||||
successIntegral.value = 0
|
||||
successPopupRef.value.close()
|
||||
}
|
||||
|
||||
/**
|
||||
* 签到
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doSignIn() {
|
||||
if (loading.value) return
|
||||
if (signInInfo.value.todaySignIn) return toast({title: '今日已签到'})
|
||||
try {
|
||||
loading.value = true
|
||||
successIntegral.value = await signIn();
|
||||
await mainStore.getUserInfo()
|
||||
openSuccessPopup()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const taskList = ref([])
|
||||
|
||||
/**
|
||||
* 获取每日任务
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetIntegralRule() {
|
||||
taskList.value = await getIntegralRule();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
doGetSignInInfo()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff"> 签到
|
||||
</Header>
|
||||
<view class="sign-in">
|
||||
<!-- 背景 -->
|
||||
<view
|
||||
class="bg"
|
||||
:style="{backgroundImage:`url(${accountSignInBg})`}"></view>
|
||||
<!-- 内容 -->
|
||||
<view class="sign-in__inner">
|
||||
<view class="bg-mask">
|
||||
<!-- 积分显示 -->
|
||||
<view class="header-row flex flex-ai__end">
|
||||
{{ user.integral || '0.00' }}
|
||||
<text class="sub">积分</text>
|
||||
</view>
|
||||
<!-- 签到显示 -->
|
||||
<view class="sign-btn-row">
|
||||
<view class="title">
|
||||
您已经连续签到
|
||||
<text class="primary-color"> {{ signInInfo.signInDays }}</text>
|
||||
天
|
||||
</view>
|
||||
<view
|
||||
@click="doSignIn"
|
||||
:class="{'disable':signInInfo.todaySignIn}"
|
||||
class="btn">
|
||||
{{ signInInfo.todaySignIn ? '已签到' : '立即签到' }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- 签到日历 -->
|
||||
<view class="sign-calendar">
|
||||
<view class="sign-calendar__inner">
|
||||
<template
|
||||
v-for="date in signInInfo.signInRecordList"
|
||||
:key="date.timestamp">
|
||||
<view
|
||||
class="item flex flex-column flex-ai__center flex-jc__center "
|
||||
:class="{none:!date.isSign}">
|
||||
<view class="item-box flex flex-column flex-ai__center flex-jc__center">
|
||||
<text>+{{ date.integral }}</text>
|
||||
<image :src="date.isSign?accountSignOk:accountSignStar" />
|
||||
</view>
|
||||
{{ moment(date.createTime).format("MM-DD") }}
|
||||
</view>
|
||||
</template>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 每日任务Card -->
|
||||
<view class="sign-card">
|
||||
<view class="title"> 每日任务</view>
|
||||
<view class="sign-list">
|
||||
<template
|
||||
v-for="task in taskList"
|
||||
:key="task.id">
|
||||
<view class="row flex flex-ai__center">
|
||||
<image
|
||||
class="row-icon"
|
||||
:src="task.iconUrl" />
|
||||
|
||||
<view class="row-info flex flex-ai__center flex-jc__sb">
|
||||
<view class="left">
|
||||
<view class="name">{{ task.typeName }}</view>
|
||||
<view class="sub flex flex-ai__center">
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn flex flex-ai__center">
|
||||
<image :src="accountSignStar" />
|
||||
+{{ task.integral }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<Popup
|
||||
ref="successPopupRef"
|
||||
mode="center"
|
||||
:show-closeable="false">
|
||||
<view
|
||||
class="success-card"
|
||||
:style="{backgroundImage:`url(${accountSignSuccessBg})`}">
|
||||
<view class="inner">
|
||||
<view
|
||||
class="count-row flex">
|
||||
<view class="count">{{ successIntegral }}</view>
|
||||
积分
|
||||
</view>
|
||||
<view class="info">
|
||||
签到成功,恭喜您获得
|
||||
</view>
|
||||
<view
|
||||
class="button animation-button"
|
||||
@click="closeSuccessPopup">
|
||||
我知道了
|
||||
</view>
|
||||
<view class="tips">
|
||||
连续签到奖励更丰富
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</Popup>
|
||||
</template>
|
||||
|
||||
|
||||
<style
|
||||
scope
|
||||
lang="scss">
|
||||
:deep(.uni-popup__wrapper) {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
</style>
|
371
views/account/withdraw/index.vue
Normal file
371
views/account/withdraw/index.vue
Normal file
@ -0,0 +1,371 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-21 15:38
|
||||
@description:订单核销
|
||||
@update: 2024-01-21 15:38
|
||||
-->
|
||||
<script setup>
|
||||
import Header from "@/components/Header/index.vue"
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { useInterface } from "@/hooks/useInterface";
|
||||
import Modal from "@/components/Modal/index.vue";
|
||||
import { ref } from "vue";
|
||||
import Popup from "@/components/Popup.vue";
|
||||
import { getWithDrawMethodList, updateWithDrawMethod, withDrawNow } from "@/api/account/integral";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useRouter } from "@/hooks/useRouter";
|
||||
import { useMainStore } from "@/store/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { aliIcon, balanceIcon } from "@/utils/images";
|
||||
import { cloneDeep } from "loadsh";
|
||||
|
||||
// ========================= hooks ==========================
|
||||
const {toast, loading, hideLoading} = useInterface();
|
||||
const {scrollTop} = useScroll();
|
||||
const {getParams, goBack} = useRouter();
|
||||
const mainStore = useMainStore();
|
||||
const {user} = storeToRefs(mainStore)
|
||||
|
||||
|
||||
// =========================== 提现方式 =========================
|
||||
const methodRenderList = ref([])
|
||||
|
||||
const updatePopupRef = ref()
|
||||
|
||||
const methodCurrent = ref()
|
||||
|
||||
|
||||
/**
|
||||
* 获取提现方式列表
|
||||
* 此处服务端使用list,方便后期扩展
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetWithDrawMethodList() {
|
||||
methodRenderList.value = await getWithDrawMethodList()
|
||||
methodCurrent.value = methodRenderList.value[0].id
|
||||
}
|
||||
|
||||
const getMethodIcon = (type) => {
|
||||
return [balanceIcon, aliIcon][type]
|
||||
}
|
||||
|
||||
const methodLoading = ref(false)
|
||||
|
||||
const methodForm = ref()
|
||||
|
||||
/**
|
||||
* 保存提现方式
|
||||
*/
|
||||
async function confirmDrawMethod() {
|
||||
if (methodLoading.value) return
|
||||
if (!methodForm.value.realName) return toast({title: '请输入真实姓名'})
|
||||
if (!methodForm.value.accountNum) return toast({title: '请输入账号'})
|
||||
await updateWithDrawMethod(methodForm.value)
|
||||
toast({title: '设置成功'})
|
||||
await doGetWithDrawMethodList()
|
||||
updatePopupRef.value.close()
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开添加支付方式
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function openAliPopup(item) {
|
||||
methodForm.value = cloneDeep(item)
|
||||
updatePopupRef.value.show()
|
||||
}
|
||||
|
||||
// ========================== 提现 =============================
|
||||
const maxMoney = ref(0) // 最大提现金额
|
||||
const modalRef = ref()
|
||||
const modalTitle = ref('')
|
||||
|
||||
function selectAll() {
|
||||
withdrawForm.value.amount = maxMoney.value
|
||||
}
|
||||
|
||||
/**
|
||||
* 提现防呆
|
||||
*/
|
||||
function openModal() {
|
||||
if (withdrawForm.value.amount <= 0) return toast({title: '提现金额必须大于0'})
|
||||
const method = methodRenderList.value.find(item => item.id === methodCurrent.value)
|
||||
modalTitle.value = `确定要提现 ${ withdrawForm.value.amount } 到 ${ method.paymentMethod } 吗?`
|
||||
modalRef.value.show()
|
||||
}
|
||||
|
||||
|
||||
const withdrawLoading = ref(false)
|
||||
const withdrawForm = ref({
|
||||
amount: 0,
|
||||
})
|
||||
|
||||
async function doWithdrawDeposit() {
|
||||
try {
|
||||
withdrawLoading.value = true
|
||||
withdrawForm.value.id = methodRenderList.value.find(item => item.id === methodCurrent.value).id
|
||||
await withDrawNow(withdrawForm.value)
|
||||
toast({title: '提现成功'})
|
||||
withdrawForm.value.id = undefined
|
||||
maxMoney.value -= withdrawForm.value.amount
|
||||
withdrawForm.value.amount = 0
|
||||
goBack({}, 2000)
|
||||
} finally {
|
||||
withdrawLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async (options) => {
|
||||
const params = getParams(options);
|
||||
await doGetWithDrawMethodList()
|
||||
maxMoney.value = params.maxMoney || 0
|
||||
if (params && Number(params.type) === 1) {
|
||||
const find = methodRenderList.value.find((item) => item.paymentMethod === "余额");
|
||||
if (!find) return
|
||||
methodCurrent.value = find.id
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff">
|
||||
提现
|
||||
</Header>
|
||||
<view>
|
||||
<view class="verification">
|
||||
<view class="ver-card">
|
||||
<view class="title">提现金额</view>
|
||||
<view class="input row">
|
||||
<view class="flex flex-ai__center">
|
||||
<text class="price">¥</text>
|
||||
<input
|
||||
v-model="withdrawForm.amount"
|
||||
:max="maxMoney"
|
||||
type="digit"
|
||||
placeholder="请输入提现金额" />
|
||||
</view>
|
||||
<text
|
||||
v-if="maxMoney>0"
|
||||
class="btn"
|
||||
@click="selectAll">全部提现
|
||||
</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
可提现金额{{ maxMoney }}元
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="ver-card">
|
||||
<view class="title">提现至</view>
|
||||
<u-radio-group
|
||||
activeColor="#EE6D46"
|
||||
v-model="methodCurrent"
|
||||
placement="column"
|
||||
>
|
||||
<template
|
||||
v-for="item in methodRenderList"
|
||||
:key="item.id">
|
||||
<view
|
||||
class="flex flex-jc__sb flex-ai__center"
|
||||
@click="methodCurrent = item.id">
|
||||
<view class="input row method">
|
||||
<view class="flex flex-ai__center">
|
||||
<image
|
||||
class="icon"
|
||||
:src="getMethodIcon(item.type)" />
|
||||
<text class="text">{{ item.paymentMethod }}</text>
|
||||
</view>
|
||||
<text
|
||||
v-if="item.type!==0"
|
||||
class="btn primary-color"
|
||||
@click.stop="openAliPopup(item)">
|
||||
更新账户
|
||||
</text>
|
||||
</view>
|
||||
<u-radio
|
||||
:customStyle="{marginBottom: '8px'}"
|
||||
label=" "
|
||||
:name="item.id"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="btn-row row animation-button"
|
||||
:class="{disabled:withdrawForm.amount<=0}"
|
||||
@click="openModal">
|
||||
确认提现
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<Modal
|
||||
ref="modalRef"
|
||||
:content="modalTitle"
|
||||
@confirm="doWithdrawDeposit" />
|
||||
|
||||
<!-- 绑定 -->
|
||||
<Popup
|
||||
ref="updatePopupRef"
|
||||
:title="`绑定${methodForm&&methodForm.paymentMethod || ''}`"
|
||||
mode="center"
|
||||
:show-closeable="false">
|
||||
<view
|
||||
class="ali-popup"
|
||||
v-if="methodForm">
|
||||
<view class="input">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="请输入真实姓名"
|
||||
v-model="methodForm.realName">
|
||||
</view>
|
||||
<view class="input">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="请输入账号"
|
||||
v-model="methodForm.accountNum">
|
||||
</view>
|
||||
<view
|
||||
class="btn animation-button"
|
||||
@click="confirmDrawMethod">
|
||||
保存
|
||||
</view>
|
||||
</view>
|
||||
</Popup>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.verification {
|
||||
@include usePadding(34, 34);
|
||||
|
||||
.ver-card {
|
||||
@include usePadding(32, 32);
|
||||
margin-bottom: 24rpx;
|
||||
border-radius: 15rpx;
|
||||
background: #fff;
|
||||
|
||||
.title {
|
||||
text-align: left;
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.input {
|
||||
@include useFlex(space-between, center);
|
||||
@include usePadding(21, 24);
|
||||
width: 100%;
|
||||
//background: #F6F8F8;
|
||||
border-radius: 8rpx;
|
||||
border-bottom: 1rpx solid #f6f8f8;
|
||||
|
||||
.price {
|
||||
font-size: 48rpx;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.method {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.none-border {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.qr-scan {
|
||||
@include useFlex(center, center);
|
||||
margin-top: 40rpx !important;
|
||||
|
||||
image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
transition: all .3s;
|
||||
|
||||
&:active {
|
||||
scale: 1.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.btn-row {
|
||||
@include useFlex(center, center);
|
||||
height: 80rpx;
|
||||
border-radius: 15rpx;
|
||||
position: fixed;
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
bottom: 50rpx;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
background: #f3997d;
|
||||
|
||||
&:active {
|
||||
scale: 1;
|
||||
animation: disabledAnimation 200ms 15;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes disabledAnimation {
|
||||
0%, 90% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
30% {
|
||||
transform: translateX(-20rpx);
|
||||
}
|
||||
60% {
|
||||
transform: translateX(20rpx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ali-popup {
|
||||
width: 80vw;
|
||||
@include usePadding(32, 32);
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
background: #f6f8f8;
|
||||
@include usePadding(21, 21);
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
|
||||
.btn {
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
text-align: center;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.u-radio) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
</style>
|
250
views/activity/afterVerification/index.vue
Normal file
250
views/activity/afterVerification/index.vue
Normal file
@ -0,0 +1,250 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-21 15:38
|
||||
@description:订单核销
|
||||
@update: 2024-01-21 15:38
|
||||
-->
|
||||
<script setup>
|
||||
import Header from "@/components/Header/index.vue"
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { afterVerificationQrScan } from "@/utils/images";
|
||||
import { ref } from 'vue'
|
||||
import { cancelAfterVerification } from "@/api/activity/afterVerification";
|
||||
import { useInterface } from "@/hooks/useInterface";
|
||||
import Modal from "@/components/Modal/index.vue";
|
||||
import { useQrCodeScan } from "@/hooks/useQrCodeScan";
|
||||
|
||||
// ========================= hooks ==========================
|
||||
const {toast, loading, hideLoading} = useInterface();
|
||||
const {scrollTop} = useScroll();
|
||||
const {qrCodeScan, cancelScan} = useQrCodeScan()
|
||||
|
||||
// ========================= 核销 ==========================
|
||||
const checkOffCode = ref('') // 核销码
|
||||
|
||||
/**
|
||||
* 核销
|
||||
*/
|
||||
async function doCancelAfterVerification() {
|
||||
if (!checkOffCode.value) return toast({title: '核销码有误'})
|
||||
try {
|
||||
loading()
|
||||
await cancelAfterVerification({writeOffCode: checkOffCode.value})
|
||||
checkOffCode.value = ''
|
||||
hideLoading()
|
||||
toast({title: '核销成功'})
|
||||
} catch (e) {
|
||||
hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
const modalRef = ref()
|
||||
|
||||
function openModal() {
|
||||
if (checkOffCode.value.length <= 0) return
|
||||
modalRef.value.show()
|
||||
}
|
||||
|
||||
function cancelModal() {
|
||||
checkOffCode.value = ''
|
||||
}
|
||||
|
||||
// ====================== 扫码相关 ==============================
|
||||
const showH5QrScan = ref(false)
|
||||
|
||||
async function doQrScan() {
|
||||
try {
|
||||
showH5QrScan.value = true
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
checkOffCode.value = await qrCodeScan();
|
||||
openModal()
|
||||
} finally {
|
||||
showH5QrScan.value = false
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
function h5CancelQrScan() {
|
||||
cancelScan()
|
||||
showH5QrScan.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff"> 订单核销
|
||||
</Header>
|
||||
<view class="verification">
|
||||
<view class="ver-card">
|
||||
<view class="title">核销券码</view>
|
||||
<view class="input row">
|
||||
<input
|
||||
v-model="checkOffCode"
|
||||
type="text"
|
||||
placeholder="请输入核销券码" />
|
||||
</view>
|
||||
<view
|
||||
class="btn-row row animation-button"
|
||||
:class="{disabled:checkOffCode.length<=0}"
|
||||
@click="openModal">
|
||||
确认核销
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="ver-card">
|
||||
<view class="title">二维码核销</view>
|
||||
<view class="qr-scan row">
|
||||
<image
|
||||
:src="afterVerificationQrScan"
|
||||
@click="doQrScan" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<Modal
|
||||
ref="modalRef"
|
||||
content="确认要核销此订单吗?"
|
||||
@confirm="doCancelAfterVerification"
|
||||
@cancel="cancelModal" />
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- h5 qr-scan UI -->
|
||||
<div
|
||||
class="qr-h5"
|
||||
:style="{
|
||||
scale:showH5QrScan?1:0
|
||||
}">
|
||||
<div
|
||||
id="reader">
|
||||
</div>
|
||||
<div
|
||||
class="cancel"
|
||||
@click="h5CancelQrScan">
|
||||
<u-icon
|
||||
name="close"
|
||||
color="#fff"
|
||||
size="28" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.qr-h5 {
|
||||
scale: 0;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #fff;
|
||||
transition: all .3s;
|
||||
|
||||
.cancel {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
background: red;
|
||||
box-shadow: 0 0 20rpx rgba(236, 236, 236, 0.47);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
right: 50%;
|
||||
bottom: 10px;
|
||||
transform-origin: center center;
|
||||
transform: translateX(50%) translateY(-100%);
|
||||
transition: all .3s;
|
||||
|
||||
&:active {
|
||||
scale: 0.9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.verification {
|
||||
@include usePadding(34, 34);
|
||||
|
||||
.ver-card {
|
||||
@include usePadding(32, 32);
|
||||
margin-bottom: 24rpx;
|
||||
border-radius: 15rpx;
|
||||
background: #fff;
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
font-size: 38rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.btn-row {
|
||||
@include useFlex(center, center);
|
||||
height: 80rpx;
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
background: #f3997d;
|
||||
|
||||
&:active {
|
||||
scale: 1;
|
||||
animation: disabledAnimation 200ms 15;
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
@include usePadding(21, 24);
|
||||
width: 100%;
|
||||
background: #F6F8F8;
|
||||
border-radius: 8rpx;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.qr-scan {
|
||||
@include useFlex(center, center);
|
||||
margin-top: 40rpx !important;
|
||||
|
||||
image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
transition: all .3s;
|
||||
|
||||
&:active {
|
||||
scale: 1.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes disabledAnimation {
|
||||
0%, 90% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
30% {
|
||||
transform: translateX(-20rpx);
|
||||
}
|
||||
60% {
|
||||
transform: translateX(20rpx);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
70
views/activity/groupBy/component/GoodsItemOptions.vue
Normal file
70
views/activity/groupBy/component/GoodsItemOptions.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<!--
|
||||
@name: GoodsItemOptions
|
||||
@author: kahu4
|
||||
@date: 2024-01-16 11:57
|
||||
@description:拼团商品底部按钮
|
||||
@update: 2024-01-16 11:57
|
||||
-->
|
||||
<script setup>
|
||||
import { toRefs } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
goods: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
const {goods} = toRefs(props)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="options flex flex-jc__sb flex-ai__end">
|
||||
<view class="left-info">
|
||||
<view class="row">
|
||||
3人团
|
||||
</view>
|
||||
<view class="price-row flex flex-ai__end">
|
||||
¥329
|
||||
<view class="old-price">
|
||||
¥666
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="button animation-button" @click.stop="">
|
||||
立即拼团
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss"
|
||||
scoped>
|
||||
.options {
|
||||
width: 100%;
|
||||
|
||||
|
||||
.left-info{
|
||||
color:$tips-color;
|
||||
font-size: 24rpx;
|
||||
.price-row{
|
||||
margin-top: 5rpx;
|
||||
color: $primary-color;
|
||||
font-size: 30rpx;
|
||||
.old-price{
|
||||
text-decoration: line-through;
|
||||
color:$tips-color;
|
||||
font-size: 20rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.button{
|
||||
min-width: 45%;
|
||||
background: $primary-color;
|
||||
color: #fff;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
608
views/activity/groupBy/detail.vue
Normal file
608
views/activity/groupBy/detail.vue
Normal file
@ -0,0 +1,608 @@
|
||||
<!--
|
||||
@name: detail
|
||||
@author: kahu4
|
||||
@date: 2024-01-16 16:35
|
||||
@description:拼团详情
|
||||
@update: 2024-01-16 16:35
|
||||
-->
|
||||
<script setup>
|
||||
import Header from "@/components/Header/index.vue";
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import Goods from "@/components/goodsComponents/Goods.vue";
|
||||
import { onLoad, onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
|
||||
import { computed, onBeforeUnmount, ref } from "vue";
|
||||
import InviteFriends from "@/components/Share/InviteFriends.vue";
|
||||
import GroupByPoster from "@/components/Poster/GroupBy.vue";
|
||||
import { SharePathKey, useShare } from "@/hooks/useShare";
|
||||
import { useRouter } from "@/hooks/useRouter";
|
||||
import { getGroupByDetailTeamworkId } from "@/api/goods";
|
||||
import { getTimeAfterNow } from "@/utils/utils";
|
||||
import { useMainStore } from "@/store/store";
|
||||
import GoodAttrSelect from "@/components/good-attr-select/good-attr-select.vue";
|
||||
import { getProductDetail } from "@/api/product";
|
||||
import { useInterface } from "@/hooks/useInterface";
|
||||
import { getCartAdd } from "@/api/cart";
|
||||
|
||||
const {getParams, push} = useRouter();
|
||||
const {scrollTop} = useScroll()
|
||||
const mainStore = useMainStore();
|
||||
|
||||
const teamworkId = ref()
|
||||
const {toast} = useInterface();
|
||||
|
||||
onLoad((option) => {
|
||||
const params = getParams(option);
|
||||
// 邀请进来的
|
||||
if (params.t && params.t === SharePathKey.GROUP_BY) {
|
||||
teamworkId.value = params.id
|
||||
} else {
|
||||
teamworkId.value = params.teamworkId
|
||||
}
|
||||
doGetGroupByDetailTeamworkId()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopTimePlay()
|
||||
})
|
||||
|
||||
// 拼团详情
|
||||
const groupDetail = ref()
|
||||
// 拼团状态 0 进行中 1 成功 2 失败
|
||||
const groupState = ref(0)
|
||||
// 拼团title
|
||||
const groupTitle = computed(() => {
|
||||
if (!groupDetail.value) return
|
||||
if (groupState.value === 0) {
|
||||
return `再邀${ residuePersonNum.value }位即可拼团成功`
|
||||
}
|
||||
if (groupState.value === 1) {
|
||||
return '拼团成功,请等待商家发货'
|
||||
}
|
||||
if (groupState.value === 2) {
|
||||
return '拼团失败,可以再次开团啊~'
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取拼团详情
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetGroupByDetailTeamworkId() {
|
||||
const res = await getGroupByDetailTeamworkId({id: teamworkId.value});
|
||||
groupDetail.value = res
|
||||
groupDetail.value.teamworkId = teamworkId.value
|
||||
// 校验状态 state 0 进行中 1 成功 2 失败
|
||||
groupState.value = res.state
|
||||
await doGetGoodsDetail()
|
||||
if (res.state > 0) return
|
||||
// 设置倒计时
|
||||
setTimePlay(res.closeTime)
|
||||
}
|
||||
|
||||
// ========================= sku 商品相关 ===========================================
|
||||
const selectAttrPanel = ref()
|
||||
const canDoOrder = ref(true) // 是否能立即下单
|
||||
const goodsDetail = ref() // 商品详情
|
||||
const selectSku = ref() // 选中的sku
|
||||
const skuNum = ref(1) // 选中sku下单数量
|
||||
/**
|
||||
* 获取商品详情
|
||||
* 请求回来给sku选择器使用
|
||||
*/
|
||||
async function doGetGoodsDetail() {
|
||||
goodsDetail.value = await getProductDetail({productId: groupDetail.value.id, skuId: groupDetail.value.skuId});
|
||||
canDoOrder.value = true
|
||||
groupByInvitationShare({...groupDetail.value,cartInfo:[{productInfo:{image:groupDetail.value.image}}]});
|
||||
const {productValue} = goodsDetail.value
|
||||
for (const skuName in productValue) {
|
||||
const sku = productValue[skuName]
|
||||
if (sku.id !== groupDetail.value.skuId) continue;
|
||||
selectSku.value = sku
|
||||
break;
|
||||
}
|
||||
// 没有sku
|
||||
if (!selectSku.value) {
|
||||
toast({title: '此规格下架了~看看其他商品吧~'})
|
||||
canDoOrder.value = false
|
||||
return
|
||||
}
|
||||
// sku没有库存
|
||||
if (!selectSku.value.campaignStock || selectSku.value <= 0) {
|
||||
toast({title: '此规格库存不足~看看其他商品吧~'})
|
||||
canDoOrder.value = false
|
||||
return
|
||||
}
|
||||
canDoOrder.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* sku选择器confirm
|
||||
* @param attr
|
||||
*/
|
||||
function handleSelectAttr(attr) {
|
||||
const {store, num} = attr
|
||||
// 与拼团商品sku不一致
|
||||
if (store.id !== groupDetail.value.skuId) {
|
||||
toast({title: '检测到您选择的规格和好友下单拼团规格不一致~请重新选择', time: 3000})
|
||||
return
|
||||
}
|
||||
// 下单逻辑
|
||||
selectSku.value = store
|
||||
skuNum.value = num
|
||||
handleBuy()
|
||||
}
|
||||
|
||||
/**
|
||||
* 下单
|
||||
* @param orderType 1、普通下单,2、商品活动下单
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
const handleBuy = async (orderType = 2) => {
|
||||
uni.showLoading({
|
||||
title: "加载中",
|
||||
});
|
||||
let res = await getCartAdd({
|
||||
orderType,
|
||||
cartNum: skuNum.value,
|
||||
productId: selectSku.value.productId,
|
||||
uniqueId: selectSku.value.unique,
|
||||
new: 1,
|
||||
teamworkId: groupDetail.value.teamworkId,
|
||||
});
|
||||
uni.hideLoading();
|
||||
const data = handleSubmitParams(orderType, res)
|
||||
push(
|
||||
{url: "/pages/submitOrder/submitOrder"},
|
||||
{data}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理跳转参数
|
||||
* @param orderType
|
||||
* @param cardRes 下单购物车返回信息
|
||||
* @return {{orderType, campaignDetailId: any, campaignType: any, cartId, teamworkType: number}|{orderType, cartId}}
|
||||
*/
|
||||
const handleSubmitParams = (orderType, cardRes) => {
|
||||
let data = {
|
||||
cartId: cardRes.cartId,
|
||||
orderType
|
||||
}
|
||||
// 活动
|
||||
if (orderType === 2) {
|
||||
data.campaignType = selectSku.value.campaignType
|
||||
data.campaignDetailId = selectSku.value.campaignDetailId
|
||||
data.teamworkType = 2 // 1开团 2拼团
|
||||
data.teamworkId = groupDetail.value.teamworkId
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
// ========================= 倒计时相关 ===========================================
|
||||
let timeObj = ref({}), time
|
||||
const timestamp = ref(0)
|
||||
|
||||
/**
|
||||
* 开始倒计时
|
||||
* @param closeTime
|
||||
*/
|
||||
function setTimePlay(closeTime) {
|
||||
const nowTime = Date.now()
|
||||
timestamp.value = closeTime
|
||||
if (timestamp.value === 0 || closeTime - nowTime <= 0) return
|
||||
const setTime = () => {
|
||||
timeObj.value = getTimeAfterNow(timestamp.value)
|
||||
if (closeTime - nowTime <= 0) {
|
||||
// 倒计时结束
|
||||
stopTimePlay()
|
||||
timeOver()
|
||||
}
|
||||
}
|
||||
setTime()
|
||||
time = setInterval(() => setTime(), 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止倒计时
|
||||
*/
|
||||
function stopTimePlay() {
|
||||
time && clearInterval(time)
|
||||
time = undefined
|
||||
timestamp.value = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 倒计时结束
|
||||
*/
|
||||
function timeOver() {
|
||||
doGetGroupByDetailTeamworkId()
|
||||
}
|
||||
|
||||
/**
|
||||
* 还差多少人成团
|
||||
* @type {ComputedRef<number|*>}
|
||||
*/
|
||||
const residuePersonNum = computed(() => {
|
||||
if (!groupDetail.value) return 1
|
||||
return groupDetail.value.person - groupDetail.value.users.length
|
||||
})
|
||||
|
||||
/**
|
||||
* 当前用户是否已经购买
|
||||
* @type {ComputedRef<unknown>}
|
||||
*/
|
||||
const isBuy = computed(() => {
|
||||
if (!groupDetail.value) return false
|
||||
return groupDetail.value.users.findIndex(item => item.uid === mainStore.user.id) > -1
|
||||
})
|
||||
|
||||
const groupRules = [
|
||||
{
|
||||
index: 1,
|
||||
info: '开团或成团享团购价'
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
info: '邀请好友参与优惠多'
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
info: '人满发货不满退款'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
/**
|
||||
* 立即拼团
|
||||
*/
|
||||
function takePartInAGroup() {
|
||||
if (!canDoOrder.value) return toast({title: "此规格不能下单,可以看看其他上哦~"})
|
||||
selectAttrPanel.value.open()
|
||||
}
|
||||
|
||||
|
||||
// ============================ 邀请 ==================================================
|
||||
const {shareInfo,groupByInvitationShare, shareH5, shareAppMessage, shareTimeline} = useShare()
|
||||
onShareAppMessage(shareAppMessage)
|
||||
onShareTimeline(shareTimeline)
|
||||
|
||||
const groupByPosterRef = ref()
|
||||
const inviteFriendShareRef = ref()
|
||||
|
||||
function inviteFriend() {
|
||||
if (groupState.value === 2) return
|
||||
inviteFriendShareRef.value.open()
|
||||
}
|
||||
|
||||
function againGroup() {
|
||||
push({
|
||||
url: '/pages/goodsDetail/goodsDetail',
|
||||
}, {
|
||||
data: {id: groupDetail.value.id, "skuId": groupDetail.value.skuId},
|
||||
type: 'redirectTo'
|
||||
})
|
||||
}
|
||||
|
||||
function handleShareConfirm(shareItem) {
|
||||
if (shareItem.value === 'wechat') {
|
||||
shareH5()
|
||||
} else {
|
||||
groupByPosterRef.value.open(groupDetail.value, shareInfo.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
class="group-by-detail"
|
||||
>
|
||||
<Header :scroll-top="scrollTop"> 拼团详情</Header>
|
||||
<view
|
||||
class="main"
|
||||
v-if="groupDetail">
|
||||
<!-- 商品信息 -->
|
||||
<view
|
||||
class="goods-row row">
|
||||
<Goods
|
||||
:goods="groupDetail"
|
||||
:ratio="true"
|
||||
imgWidth="200rpx"
|
||||
infoPadding="20rpx 20rpx"
|
||||
row
|
||||
>
|
||||
<template #options="{goods}">
|
||||
<span class="price">
|
||||
¥{{ goods.price }}
|
||||
</span>
|
||||
</template>
|
||||
</Goods>
|
||||
</view>
|
||||
|
||||
<!-- 拼团信息 -->
|
||||
<view class="row group-info">
|
||||
<view class="title">
|
||||
{{ groupTitle }}
|
||||
</view>
|
||||
<view
|
||||
class="time"
|
||||
v-if="timeObj && groupState===0">
|
||||
剩余
|
||||
<view class="time-group">
|
||||
<text class="time-item">{{ timeObj.hours }}</text>
|
||||
<text class="primary-color">:</text>
|
||||
<text class="time-item">{{ timeObj.minutes }}</text>
|
||||
<text class="primary-color">:</text>
|
||||
<text class="time-item">{{ timeObj.seconds }}</text>
|
||||
</view>
|
||||
结束
|
||||
</view>
|
||||
<view class="users">
|
||||
<view
|
||||
v-for="user in groupDetail.users"
|
||||
:key="user.id"
|
||||
class="user-item">
|
||||
<image :src="user.avatar" />
|
||||
<view
|
||||
class="first-group"
|
||||
v-if="user.isHead === '1'">
|
||||
团长
|
||||
</view>
|
||||
</view>
|
||||
<!-- 该团还需要几个人 -->
|
||||
<view
|
||||
class="user-item plus"
|
||||
v-for="item in residuePersonNum"
|
||||
:key="item"
|
||||
@click="inviteFriend">
|
||||
<u-icon name="plus" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 如果自己已经在团内 -->
|
||||
<template v-if="isBuy">
|
||||
<view
|
||||
v-if="groupState === 0"
|
||||
class="btn animation-button"
|
||||
@click="inviteFriend">
|
||||
邀请好友拼团
|
||||
</view>
|
||||
<view
|
||||
v-if="[1,2].includes(groupState)"
|
||||
class="btn animation-button"
|
||||
@click="againGroup">
|
||||
再次拼团
|
||||
</view>
|
||||
</template>
|
||||
<!-- 如果自己不在团内 -->
|
||||
<template v-else>
|
||||
<view
|
||||
v-if="[1,2].includes(groupState)"
|
||||
class="btn animation-button"
|
||||
@click="againGroup">
|
||||
再次拼团
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
class="btn animation-button"
|
||||
@click="takePartInAGroup">
|
||||
立即参团
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<!-- 拼团玩法 -->
|
||||
<view class="group-rule group-info row">
|
||||
<view class="title">
|
||||
拼团玩法
|
||||
</view>
|
||||
<view class="rule-box">
|
||||
<view
|
||||
v-for="item in groupRules"
|
||||
:key="item.index"
|
||||
class="rule-item">
|
||||
<view class="circle-number">
|
||||
{{ item.index }}
|
||||
</view>
|
||||
<view class="info">
|
||||
{{ item.info }}
|
||||
</view>
|
||||
<view class="line"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- SKU选择器 -->
|
||||
<GoodAttrSelect
|
||||
v-if="goodsDetail && selectSku"
|
||||
ref="selectAttrPanel"
|
||||
:goods-detail="goodsDetail"
|
||||
:sku-id="selectSku.id"
|
||||
@select="handleSelectAttr" />
|
||||
|
||||
<!-- 邀请好友 -->
|
||||
<InviteFriends
|
||||
ref="inviteFriendShareRef"
|
||||
@share="handleShareConfirm" />
|
||||
<GroupByPoster ref="groupByPosterRef" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: $page-bg-color;
|
||||
}
|
||||
</style>
|
||||
<style
|
||||
lang="scss"
|
||||
scoped>
|
||||
.group-by-detail {
|
||||
width: 100%;
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
@include usePadding(30, 30);
|
||||
|
||||
.row {
|
||||
width: 100%;
|
||||
background: $white-color;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 25rpx;
|
||||
}
|
||||
|
||||
.price {
|
||||
color: $primary-color;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.group-info {
|
||||
@include usePadding(30, 30);
|
||||
width: 100%;
|
||||
|
||||
.title, .time {
|
||||
@include useFlex(center, center)
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
margin-top: 15rpx;
|
||||
font-size: 24rpx;
|
||||
@include useFlex(center, center);
|
||||
|
||||
.time-group {
|
||||
margin: 0 15rpx;
|
||||
@include useFlex(center, center);
|
||||
|
||||
.time-item {
|
||||
@include usePadding(10, 6);
|
||||
background: #FDEFEA;
|
||||
border: 1rpx solid #E85A2B;
|
||||
color: #E85A2B;
|
||||
border-radius: 5rpx;
|
||||
}
|
||||
|
||||
.primary-color {
|
||||
color: #E85A2B;
|
||||
padding: 0 5rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.users {
|
||||
margin: 40rpx 0;
|
||||
@include useFlex(center, center, row, wrap, 20rpx);
|
||||
width: 100%;
|
||||
|
||||
.user-item {
|
||||
width: 130rpx;
|
||||
height: 130rpx;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 10rpx rgba(220, 220, 220, 0.7);
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background: #e5e5e5;
|
||||
}
|
||||
|
||||
.first-group {
|
||||
@include usePadding(16, 5);
|
||||
background: $primary-color;
|
||||
color: $white-color;
|
||||
white-space: nowrap;
|
||||
border-radius: 34rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.plus {
|
||||
transition: all .3s;
|
||||
|
||||
&:active {
|
||||
scale: 1.1;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-top: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 80rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.group-rule {
|
||||
width: 100%;
|
||||
|
||||
.rule-box {
|
||||
@include usePadding(0, 30);
|
||||
@include useFlex(space-between, center);
|
||||
font-size: 20rpx;
|
||||
color: $tips-color;
|
||||
|
||||
.rule-item {
|
||||
position: relative;
|
||||
@include useFlex(center, center, column);
|
||||
|
||||
.circle-number {
|
||||
@include useFlex(center, center);
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
color: #000;
|
||||
border-radius: 50%;
|
||||
background: #f5f5f5;
|
||||
margin: 16rpx 0;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 5rpx;
|
||||
background: #f5f5f5;
|
||||
top: 35%;
|
||||
left: 65%;
|
||||
}
|
||||
|
||||
&:last-child:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
background: #f5f5f5;
|
||||
top: 35%;
|
||||
left: 60%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.success-color {
|
||||
color: #E85A2B;
|
||||
}
|
||||
</style>
|
229
views/activity/groupBy/index.vue
Normal file
229
views/activity/groupBy/index.vue
Normal file
@ -0,0 +1,229 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-16 11:19
|
||||
@description:index
|
||||
@update: 2024-01-16 11:19
|
||||
-->
|
||||
<script setup>
|
||||
import { emptyCollectIcon, groupByBg } from "@/utils/images";
|
||||
import Header from "@/components/Header/index.vue";
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import ListLoadLoading from "@/components/ListLoadLoading/index.vue";
|
||||
import ListLoadOver from "@/components/ListLoadOver/index.vue";
|
||||
import Empty from "@/components/Empty/index.vue";
|
||||
import Goods from "@/components/goodsComponents/Goods.vue";
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { usePage } from "@/hooks";
|
||||
import { getProductList } from "@/api/product";
|
||||
import { useRouter } from "@/hooks/useRouter";
|
||||
import { computed, toRefs } from "vue";
|
||||
import GoodsItemOptions from "@/views/activity/groupBy/component/GoodsItemOptions.vue";
|
||||
|
||||
const {scrollTop} = useScroll()
|
||||
|
||||
const {refresh, dataList, loadend, loading, listEmpty} = usePage(getProductList)
|
||||
const {push} = useRouter()
|
||||
onLoad(() => {
|
||||
refresh()
|
||||
})
|
||||
const props = defineProps({
|
||||
more: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
}
|
||||
})
|
||||
const {more} = toRefs(props)
|
||||
|
||||
|
||||
const colList = computed(() => [
|
||||
{
|
||||
name: 'all',
|
||||
data: dataList.value.filter((item, index) => index % 2 === 0)
|
||||
},
|
||||
{
|
||||
name: 'right',
|
||||
data: dataList.value.filter((item, index) => index % 2 !== 0)
|
||||
}
|
||||
])
|
||||
const executeRefresh = () => {
|
||||
refresh()
|
||||
console.log()
|
||||
}
|
||||
|
||||
defineExpose({executeRefresh})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
>
|
||||
拼团专区
|
||||
</Header>
|
||||
<view class="group-buy-container">
|
||||
<!-- 背景 -->
|
||||
<view class="bg-box">
|
||||
<image
|
||||
class="bg"
|
||||
:src="groupByBg" />
|
||||
</view>
|
||||
|
||||
<!-- 内容 -->
|
||||
<view class="main-box">
|
||||
<view class="main-box__inner">
|
||||
<view
|
||||
class="row-product"
|
||||
v-if="colList[0].data.length>0">
|
||||
<Goods
|
||||
row
|
||||
:ratio="true"
|
||||
:goods="colList[0].data[0]"
|
||||
imgWidth="200rpx"
|
||||
infoPadding="0rpx 20rpx"
|
||||
>
|
||||
<template #options="{goods}">
|
||||
<GoodsItemOptions :goods="goods" />
|
||||
</template>
|
||||
</Goods>
|
||||
</view>
|
||||
<view
|
||||
class="product-box"
|
||||
v-if="!listEmpty"
|
||||
>
|
||||
<!-- 分两列 后期瀑布流 -->
|
||||
<template
|
||||
v-for="col in colList"
|
||||
:key="col.name"
|
||||
>
|
||||
<view class="goods-col">
|
||||
<template
|
||||
v-for="item in col.data"
|
||||
:key="item.id"
|
||||
>
|
||||
<view class="product">
|
||||
<Goods
|
||||
:ratio="true"
|
||||
:goods="item"
|
||||
infoPadding="30rpx 10rpx"
|
||||
>
|
||||
<template #options="{goods}">
|
||||
<GoodsItemOptions :goods="goods" />
|
||||
</template>
|
||||
</Goods>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<Empty
|
||||
v-else
|
||||
:iconSrc="emptyCollectIcon"
|
||||
>
|
||||
暂时没有商品推荐哦~
|
||||
</Empty>
|
||||
<!-- 加载中 -->
|
||||
<ListLoadLoading v-if="loading" />
|
||||
<!-- 加载完毕-->
|
||||
<ListLoadOver v-if="loadend">
|
||||
<template v-if="more">
|
||||
<span @click="push({ url: '/pages/goodsList/goodsList' })">
|
||||
浏览更多商品
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
到底了~
|
||||
</template>
|
||||
</ListLoadOver>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: $page-bg-color;
|
||||
}
|
||||
</style>
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.group-buy-container {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.bg-box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
aspect-ratio: 750/435;
|
||||
|
||||
.bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.main-box {
|
||||
top: 250rpx;
|
||||
position: relative;
|
||||
@include usePadding(30, 30);
|
||||
|
||||
&__inner {
|
||||
//background: #000;
|
||||
.row-product {
|
||||
@include usePadding(30, 30);
|
||||
width: 100%;
|
||||
background: $white-color;
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
.product-box {
|
||||
margin-top: 30rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
//display: flex;
|
||||
//gap: 20rpx;
|
||||
|
||||
.goods-col {
|
||||
width: 49%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
flex-grow: 0;
|
||||
float: left;
|
||||
|
||||
&:nth-child(2) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
.product {
|
||||
flex-grow: 0;
|
||||
width: 100%;
|
||||
background: $white-color;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.good-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 20rpx;
|
||||
|
||||
.price {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.sale {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
504
views/distribution/applyFor/index.vue
Normal file
504
views/distribution/applyFor/index.vue
Normal file
@ -0,0 +1,504 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-17 11:12
|
||||
@description:申请分销商
|
||||
@update: 2024-01-17 11:12
|
||||
-->
|
||||
<script setup>
|
||||
import Header from '@/components/Header/index.vue'
|
||||
import AddressSelect from '@/components/AddressSelect/index.vue'
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { onLoad, onPageScroll, onShow } from "@dcloudio/uni-app";
|
||||
import { useMainStore } from "@/store/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { nextTick, ref } from "vue";
|
||||
import { distributionAudit, distributionError } from "@/utils/images";
|
||||
import { useJump } from "@/hooks/useJump";
|
||||
import { useInterface } from "@/hooks/useInterface";
|
||||
import { applyDistribution, checkIsDistribution, updateApply } from "@/api/distribution";
|
||||
import { useRouter } from "@/hooks/useRouter";
|
||||
import { getDictByType } from "@/api/global";
|
||||
import { distributionAgreementUrl } from "@/api/auth";
|
||||
|
||||
const {scrollTop} = useScroll()
|
||||
onPageScroll(() => {
|
||||
})
|
||||
const {goDistribution} = useJump()
|
||||
const {getParams} = useRouter()
|
||||
const mainStore = useMainStore();
|
||||
const {user, areaList} = storeToRefs(mainStore);
|
||||
const {goWebview, goHome} = useJump();
|
||||
const {push} = useRouter();
|
||||
const {toast} = useInterface()
|
||||
// ======================== 表单 ===================================
|
||||
const showPrivacy = ref(true)
|
||||
|
||||
/**
|
||||
* 设置隐私协议模块是否可见
|
||||
* 解决键盘弹起fixed内容上顶问题
|
||||
* @param flag
|
||||
*/
|
||||
function setShowPrivacy(flag) {
|
||||
nextTick(() => {
|
||||
showPrivacy.value = flag
|
||||
})
|
||||
}
|
||||
|
||||
const userCheckData = ref({
|
||||
status: null,
|
||||
refuse: '',
|
||||
realName: '',
|
||||
levelName: '',
|
||||
superiorName: '',
|
||||
addUpWages: 0,
|
||||
refuseAmount: 0,
|
||||
amount: 0
|
||||
})
|
||||
const subLoading = ref(false)
|
||||
const form = ref({
|
||||
realName: '',
|
||||
address: '',
|
||||
reason: '',
|
||||
superiorIdP1: undefined
|
||||
})
|
||||
|
||||
function verify() {
|
||||
// 校验表单
|
||||
if (agreementFlag.value.length <= 0) {
|
||||
toast({title: '请先阅读并勾选协议'})
|
||||
return false
|
||||
}
|
||||
if (!form.value.realName) {
|
||||
toast({title: '请输入真实姓名'})
|
||||
return false
|
||||
}
|
||||
if (!form.value.address) {
|
||||
toast({title: '请选择真实地址'})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (subLoading.value) return
|
||||
if (!verify()) return
|
||||
try {
|
||||
subLoading.value = true
|
||||
if (!isUpdate.value) {
|
||||
await applyDistribution(form.value)
|
||||
} else {
|
||||
await updateApply(form.value)
|
||||
isUpdate.value = false
|
||||
}
|
||||
await doCheckIsDistribution()
|
||||
} finally {
|
||||
subLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前用户是否是分销商
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function doCheckIsDistribution() {
|
||||
userCheckData.value = await checkIsDistribution();
|
||||
if (userCheckData.value.status === 1 && !isUpdate.value) {
|
||||
goDistribution({}, true)
|
||||
}
|
||||
return userCheckData.value
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 再次申请
|
||||
*/
|
||||
function applyForAgain() {
|
||||
form.value = {
|
||||
...form.value,
|
||||
...{
|
||||
realName: '',
|
||||
address: '',
|
||||
reason: ''
|
||||
}
|
||||
}
|
||||
userCheckData.value = {
|
||||
status: null,
|
||||
refuse: '',
|
||||
realName: '',
|
||||
levelName: '',
|
||||
superiorName: '',
|
||||
addUpWages: 0,
|
||||
refuseAmount: 0,
|
||||
amount: 0
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 地址相关 ================================
|
||||
const addressPickerRef = ref()
|
||||
const defaultSelect = ref([0, 0, 0])
|
||||
|
||||
function handleChooseAddress() {
|
||||
addressPickerRef.value.open(isUpdate.value ? form.value.address : '')
|
||||
}
|
||||
|
||||
function handleConfirmAddress(values) {
|
||||
form.value.address = values.reduce((pre, now, index) => {
|
||||
if (index === 0) {
|
||||
pre = now.name
|
||||
} else {
|
||||
pre = pre + '-' + now.name
|
||||
}
|
||||
return pre
|
||||
}, '');
|
||||
}
|
||||
|
||||
const agreementFlag = ref([]) // length > 0就是勾选了
|
||||
|
||||
/**
|
||||
* 阅读分销协议
|
||||
*/
|
||||
async function readAgreement() {
|
||||
push({url: '/pages/webview/index'}, {data: {src: distributionAgreementUrl}})
|
||||
}
|
||||
|
||||
const showFlag = ref(false)
|
||||
|
||||
onShow(async () => {
|
||||
showFlag.value = false
|
||||
if (isUpdate.value) {
|
||||
const data = await doCheckIsDistribution()
|
||||
form.value.realName = data.realName
|
||||
form.value.address = data.address
|
||||
// form.value.address =
|
||||
form.value.reason = data.reason
|
||||
} else {
|
||||
await doCheckIsDistribution()
|
||||
}
|
||||
showFlag.value = true
|
||||
})
|
||||
const isUpdate = ref(false)
|
||||
|
||||
onLoad(async (options) => {
|
||||
const params = getParams(options);
|
||||
// 是否是更新
|
||||
isUpdate.value = (params && params.update) || false
|
||||
// 有上级ID处理上级ID
|
||||
form.value.superiorIdP1 = (params && params.id) || undefined;
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
class="apply-for"
|
||||
v-if="user">
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff">申请分销商
|
||||
</Header>
|
||||
<view class="inner">
|
||||
<view class="bg"></view>
|
||||
<!-- userinfo -->
|
||||
<view class="userinfo flex flex-column flex-ai__center flex-jc__center">
|
||||
<image :src="user.avatar" />
|
||||
<text class="username">{{ userCheckData.realName || user.nickname }}</text>
|
||||
</view>
|
||||
<!-- card -->
|
||||
<view
|
||||
class="apply-card"
|
||||
v-if="showFlag">
|
||||
<!-- form -->
|
||||
<view
|
||||
v-if="userCheckData.status === null || isUpdate"
|
||||
class="form">
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
真实姓名
|
||||
</text>
|
||||
<div class="right">
|
||||
<input
|
||||
:adjust-position="false"
|
||||
v-model="form.realName"
|
||||
type="text"
|
||||
placeholder="请输入真实姓名"
|
||||
@focus="setShowPrivacy(false)"
|
||||
@blur="setShowPrivacy(true)" />
|
||||
</div>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
现居城市
|
||||
</text>
|
||||
<div
|
||||
class="right flex flex-jc__sb flex-ai__center"
|
||||
@click="handleChooseAddress">
|
||||
<text
|
||||
class="placeholder"
|
||||
v-if="!form.address">
|
||||
请选择省、市、区
|
||||
</text>
|
||||
<text
|
||||
class="placeholder"
|
||||
v-else>
|
||||
{{ form.address }}
|
||||
</text>
|
||||
<u-icon name="arrow-right" />
|
||||
</div>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
申请原因
|
||||
</text>
|
||||
<div class="right">
|
||||
<input
|
||||
:adjust-position="false"
|
||||
@focus="setShowPrivacy(false)"
|
||||
@blur="setShowPrivacy(true)"
|
||||
v-model="form.reason"
|
||||
type="text"
|
||||
placeholder="请输入申请原因" />
|
||||
</div>
|
||||
</view>
|
||||
<view
|
||||
class="submit"
|
||||
:class="{'hide-box':!showPrivacy}">
|
||||
<view class="tips">提交成功后,我们将会在1-3个工作日内给您回复</view>
|
||||
<view
|
||||
class="button animation-button"
|
||||
@click="handleSubmit">提交审核
|
||||
</view>
|
||||
<view class="agreement tips flex flex-ai__center">
|
||||
<u-checkbox-group
|
||||
activeColor="#ed6d47"
|
||||
shape="circle"
|
||||
v-model="agreementFlag"
|
||||
>
|
||||
<u-checkbox
|
||||
name=""
|
||||
/>
|
||||
</u-checkbox-group>
|
||||
勾选代表同意
|
||||
<text
|
||||
class="primary-color"
|
||||
@click="readAgreement">《分销协议》
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- state success -->
|
||||
<view
|
||||
class="state"
|
||||
v-if="userCheckData.status === 0 && !isUpdate">
|
||||
<image :src="distributionAudit" />
|
||||
<view class="title">申请提交成功,请耐心等待...</view>
|
||||
<view
|
||||
class="btn"
|
||||
@click="goHome">继续逛逛
|
||||
</view>
|
||||
</view>
|
||||
<!-- state error -->
|
||||
<view
|
||||
class="state"
|
||||
v-if="userCheckData.status === 2 && !isUpdate">
|
||||
<image :src="distributionError" />
|
||||
<view class="title">不好意思,商家拒绝了您的申请...</view>
|
||||
<view class="sub-title">拒绝理由:{{ userCheckData.refuse }}</view>
|
||||
<view class="flex flex-jc__center flex-ai__center">
|
||||
<view
|
||||
class="btn line-btn"
|
||||
@click="applyForAgain">
|
||||
再次申请
|
||||
</view>
|
||||
<view
|
||||
class="btn"
|
||||
@click="goHome">
|
||||
继续逛逛
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- state 清退 -->
|
||||
<view
|
||||
class="state"
|
||||
v-if="userCheckData.status === -1 && !isUpdate">
|
||||
<image :src="distributionError" />
|
||||
<view class="title">您已被清退,请重新申请</view>
|
||||
<view class="sub-title">{{ userCheckData.refuse }}</view>
|
||||
<view class="flex flex-jc__center flex-ai__center">
|
||||
<view
|
||||
class="btn line-btn"
|
||||
@click="applyForAgain">
|
||||
再次申请
|
||||
</view>
|
||||
<view
|
||||
class="btn"
|
||||
@click="goHome">
|
||||
继续逛逛
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<AddressSelect
|
||||
ref="addressPickerRef"
|
||||
v-model:default-value="defaultSelect"
|
||||
@confirm="handleConfirmAddress" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.apply-for {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.inner {
|
||||
position: relative;
|
||||
|
||||
.bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
aspect-ratio: 750/460;
|
||||
background: #333333;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.userinfo {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
@include usePadding(0, 72);
|
||||
|
||||
image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid #fff;
|
||||
}
|
||||
|
||||
.username {
|
||||
color: #fff;
|
||||
font-size: 34rpx;
|
||||
margin-top: 30rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.apply-card {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: #f6f6f6;
|
||||
border-radius: 30rpx;
|
||||
@include usePadding(34, 34);
|
||||
|
||||
.form {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
@include usePadding(32, 0);
|
||||
border-radius: 30rpx;
|
||||
|
||||
.row {
|
||||
@include useFlex(flex-start, center);
|
||||
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.right {
|
||||
@include usePadding(0, 32);
|
||||
border-bottom: 1rpx solid #f6f6f6;
|
||||
flex-grow: 1;
|
||||
|
||||
.placeholder {
|
||||
color: #808080;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.submit {
|
||||
width: 80%;
|
||||
position: fixed;
|
||||
bottom: 50rpx;
|
||||
@include useFlex(center, center, column);
|
||||
|
||||
|
||||
.tips {
|
||||
color: $tips-color;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 100%;
|
||||
margin-top: 30rpx;
|
||||
height: 80rpx;
|
||||
@include useFlex(center, center);
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
.agreement {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.state {
|
||||
width: 100%;
|
||||
@include useFlex(center, center, column);
|
||||
@include usePadding(0, 50);
|
||||
|
||||
image {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
color: $tips-color
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 230rpx;
|
||||
height: 80rpx;
|
||||
background: #333333;
|
||||
color: #fff;
|
||||
@include useFlex(center, center, column);
|
||||
border-radius: 15rpx;
|
||||
margin: 30rpx 15rpx;
|
||||
}
|
||||
|
||||
.line-btn {
|
||||
background: #fff;
|
||||
border: 1rpx solid #333333;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.hide-box {
|
||||
bottom: -500rpx !important;
|
||||
}
|
||||
|
||||
</style>
|
67
views/distribution/center/index.data.js
Normal file
67
views/distribution/center/index.data.js
Normal file
@ -0,0 +1,67 @@
|
||||
import {
|
||||
distributionGoods,
|
||||
distributionMoney,
|
||||
distributionOrder,
|
||||
distributionShare,
|
||||
distributionTeam,
|
||||
distributionUser
|
||||
} from "@/utils/images";
|
||||
import { useJump } from "@/hooks/useJump";
|
||||
|
||||
const {
|
||||
goDistribution,
|
||||
goDistributionApplyFor,
|
||||
goDistributionTeam,
|
||||
goDistributionCommission,
|
||||
goDistributionOrder,
|
||||
goDistributionGoods,
|
||||
goDistributionDatum
|
||||
} = useJump();
|
||||
|
||||
export const distributionCenterInfoList = [
|
||||
{
|
||||
label: '总收益(元)',
|
||||
field: 'addUpWages'
|
||||
},
|
||||
{
|
||||
label: '待入账佣金(元)',
|
||||
field: 'refuseAmount'
|
||||
},
|
||||
{
|
||||
label: '已入账佣金(元)',
|
||||
field: 'amount'
|
||||
}
|
||||
]
|
||||
|
||||
export const distributionCenterCardList = [
|
||||
{
|
||||
label: '我的团队',
|
||||
path: goDistributionTeam,
|
||||
icon: distributionTeam
|
||||
},
|
||||
{
|
||||
label: '佣金明细',
|
||||
path: goDistributionCommission,
|
||||
icon: distributionMoney
|
||||
},
|
||||
{
|
||||
label: '分销订单',
|
||||
path: goDistributionOrder,
|
||||
icon: distributionOrder
|
||||
},
|
||||
{
|
||||
label: '推广商品',
|
||||
path: (data) => goDistributionGoods({distributionId: data.id}),
|
||||
icon: distributionGoods
|
||||
},
|
||||
{
|
||||
label: '邀请海报',
|
||||
path: 'share',
|
||||
icon: distributionShare
|
||||
},
|
||||
{
|
||||
label: '我的资料',
|
||||
path: goDistributionDatum,
|
||||
icon: distributionUser
|
||||
}
|
||||
]
|
307
views/distribution/center/index.vue
Normal file
307
views/distribution/center/index.vue
Normal file
@ -0,0 +1,307 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-17 11:12
|
||||
@description:分销中心
|
||||
@update: 2024-01-17 11:12
|
||||
-->
|
||||
<script setup>
|
||||
import Header from "@/components/Header/index.vue";
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { onLoad, onPageScroll, onShareAppMessage, onShareTimeline, onShow } from "@dcloudio/uni-app";
|
||||
import { useMainStore } from "@/store/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, ref } from "vue";
|
||||
import { distributionCenterCardList, distributionCenterInfoList } from "@/views/distribution/center/index.data";
|
||||
import DistributionShare from '@/components/Share/Distribution.vue'
|
||||
import DistributionPoster from '@/components/Poster/Distribution.vue'
|
||||
import { checkIsDistribution } from "@/api/distribution";
|
||||
import { useJump } from "@/hooks/useJump";
|
||||
import { SharePathKey, useShare } from "@/hooks/useShare";
|
||||
import { useRouter } from "@/hooks/useRouter";
|
||||
|
||||
// ========================= hooks =========================
|
||||
const {scrollTop} = useScroll();
|
||||
const {getParams} = useRouter();
|
||||
const {goDistributionApplyFor} = useJump();
|
||||
const {distributionShare, shareAppMessage, shareTimeline, shareH5} = useShare();
|
||||
onShareAppMessage(shareAppMessage)
|
||||
onShareTimeline(shareTimeline)
|
||||
onPageScroll(() => {
|
||||
})
|
||||
|
||||
const mainStore = useMainStore();
|
||||
const {user} = storeToRefs(mainStore);
|
||||
const phoneEncrypt = computed(() => {
|
||||
if (!user.value || !user.value.mobile) return '-'
|
||||
const truePhone = user.value.mobile
|
||||
return `${ truePhone.slice(0, 3) }****${ truePhone.slice(-4) }`
|
||||
})
|
||||
|
||||
const distributionShareRef = ref()
|
||||
const distributionPosterRef = ref()
|
||||
|
||||
/**
|
||||
* 确认分享
|
||||
* @param shareItem
|
||||
*/
|
||||
function shareConfirm(shareItem) {
|
||||
// 调用share方法
|
||||
const shareInfo = distributionShare(userDistributionInfo.value.id);
|
||||
if (shareItem.value === 'photo') {
|
||||
distributionPosterRef.value.open(shareInfo, userDistributionInfo.value)
|
||||
} else {
|
||||
// 邀请好友 处理h5 微信的邀请逻辑已经在share组件内部做了
|
||||
shareH5()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 点击了卡片选项
|
||||
* @param cardItem
|
||||
*/
|
||||
function clickCardItem(cardItem) {
|
||||
if (cardItem.path === 'share') {
|
||||
distributionShareRef.value.open()
|
||||
return
|
||||
}
|
||||
if (typeof cardItem.path === 'function') {
|
||||
cardItem.path(userDistributionInfo.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 经销商详情
|
||||
const userDistributionInfo = ref({
|
||||
id: '',
|
||||
status: -1,
|
||||
refuse: '',
|
||||
realName: '',
|
||||
levelName: '',
|
||||
superiorName: '',
|
||||
addUpWages: 0,
|
||||
refuseAmount: 0,
|
||||
amount: 0
|
||||
})
|
||||
// 是否展示当前页面
|
||||
const showPage = ref(false)
|
||||
|
||||
/**
|
||||
* 检查当前用户是否是分销商
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function doCheckIsDistribution() {
|
||||
userDistributionInfo.value = await checkIsDistribution();
|
||||
// 使用一个flag避免页面山东问题
|
||||
if (userDistributionInfo.value.status !== 1) {
|
||||
// status不等于2不是经销商,跳转申请页面
|
||||
goDistributionApplyFor({id: shareId.value}, true)
|
||||
// showPage.value = true
|
||||
} else {
|
||||
// 否则展示
|
||||
showPage.value = true
|
||||
}
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
doCheckIsDistribution()
|
||||
})
|
||||
|
||||
const shareId = ref(undefined)
|
||||
|
||||
onLoad((options) => {
|
||||
const params = getParams(options);
|
||||
// 处理是否是扫码跳转过来的,扫码跳转过来的会携带上级ID,赋值给shareId并且传递给申请页面处理
|
||||
if (Reflect.ownKeys(params).length > 0 && params.t && params.t === SharePathKey.DISTRIBUTION_USER) {
|
||||
shareId.value = params.id
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff">
|
||||
分销中心
|
||||
</Header>
|
||||
<view
|
||||
class="distribution-center"
|
||||
v-if="showPage">
|
||||
|
||||
<!-- userinfo -->
|
||||
<view
|
||||
class="userinfo-box flex flex-jc__start"
|
||||
v-if="user">
|
||||
<image
|
||||
class="user-header"
|
||||
:src="user.avatar" />
|
||||
<view class="userinfo flex flex-column flex-jc__sb">
|
||||
<view class="phone"> {{ phoneEncrypt }}</view>
|
||||
<view class="tag-list flex flex-jc__start flex-ai__center flex-wrap">
|
||||
<view class="tag-item primary-color">
|
||||
{{ userDistributionInfo.superiorName }} 推荐
|
||||
</view>
|
||||
<view class="tag-item">
|
||||
分销等级 {{ userDistributionInfo.levelName }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 分销信息 -->
|
||||
<view class="distribution-card">
|
||||
<view class="info-row flex flex-ai__center flex-jc__sa">
|
||||
<view
|
||||
class="item"
|
||||
v-for="infoItem in distributionCenterInfoList"
|
||||
:key="infoItem.field">
|
||||
<span class="data"> {{ userDistributionInfo[infoItem.field] || '0.00' }} </span>
|
||||
<span class="title">{{ infoItem.label }}</span>
|
||||
</view>
|
||||
</view>
|
||||
<!-- grid -->
|
||||
<view class="grid-row">
|
||||
<view
|
||||
class="item"
|
||||
v-for="cardItem in distributionCenterCardList"
|
||||
:key="cardItem.label"
|
||||
@click="clickCardItem(cardItem)">
|
||||
<image :src="cardItem.icon" />
|
||||
<text>{{ cardItem.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<DistributionShare
|
||||
ref="distributionShareRef"
|
||||
@share="shareConfirm" />
|
||||
<DistributionPoster ref="distributionPosterRef" />
|
||||
</view>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: $page-bg-color;
|
||||
}
|
||||
</style>
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.distribution-center {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(to bottom, #fdfdfd, $page-bg-color);
|
||||
@include usePadding(30, 30);
|
||||
|
||||
.userinfo-box {
|
||||
width: 100%;
|
||||
|
||||
.user-header, .userinfo {
|
||||
flex-shrink: 0
|
||||
}
|
||||
|
||||
.user-header {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%
|
||||
}
|
||||
|
||||
.userinfo {
|
||||
margin-left: 24rpx;
|
||||
font-size: 44rpx;
|
||||
font-weight: bold;
|
||||
max-width: 80%;
|
||||
|
||||
.tag-list {
|
||||
margin-top: 5rpx;
|
||||
gap: 10rpx;
|
||||
|
||||
.tag-item {
|
||||
font-size: 20rpx;
|
||||
flex-shrink: 0;
|
||||
@include usePadding(16, 5);
|
||||
border-radius: 50rpx;
|
||||
border: 1rpx solid #697591;
|
||||
color: #697591;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.distribution-card {
|
||||
margin-top: 30rpx;
|
||||
width: 100%;
|
||||
background: #333333;
|
||||
color: #fff;
|
||||
border-radius: 15rpx;
|
||||
|
||||
.info-row {
|
||||
@include usePadding(0, 30);
|
||||
|
||||
.item {
|
||||
@include useFlex(center, center, column);
|
||||
font-size: 32rpx;
|
||||
position: relative;
|
||||
flex: 1 0 auto;
|
||||
|
||||
.title {
|
||||
font-size: 24rpx;
|
||||
color: $tips-color;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
transform: translateY(-50%);
|
||||
width: 2rpx;
|
||||
border-radius: 2rpx;
|
||||
height: 50%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
&:after {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grid-row {
|
||||
@include usePadding(0, 48);
|
||||
background: #fff;
|
||||
color: #2B2B2B;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
row-gap: 32rpx;
|
||||
border-radius: 15rpx;
|
||||
|
||||
.item {
|
||||
@include useFlex(center, center, column);
|
||||
transition: all .3s;
|
||||
|
||||
image {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: #fbfbfb;
|
||||
}
|
||||
|
||||
text {
|
||||
margin-top: 16rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
&:active {
|
||||
scale: 0.9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.primary-color {
|
||||
border-color: $primary-color !important;
|
||||
color: $primary-color !important;
|
||||
}
|
||||
</style>
|
414
views/distribution/commission/index.vue
Normal file
414
views/distribution/commission/index.vue
Normal file
@ -0,0 +1,414 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-17 15:53
|
||||
@description:佣金明细
|
||||
@update: 2024-01-17 15:53
|
||||
-->
|
||||
<script setup>
|
||||
import Header from '@/components/Header/index.vue'
|
||||
import { onLoad, onShow, onPageScroll } from "@dcloudio/uni-app";
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { ref } from "vue";
|
||||
import moment from "moment";
|
||||
import { usePaging } from "@/hooks/usePaging";
|
||||
import { checkIsDistribution, getMyCommissionDetail, pageCommission } from "@/api/distribution";
|
||||
import { emptyOrderIcon } from "@/utils/images";
|
||||
import Empty from "@/components/Empty/index.vue";
|
||||
import { useJump } from "@/hooks/useJump";
|
||||
|
||||
// ================================= hooks ================================
|
||||
const {scrollTop} = useScroll()
|
||||
const {goWithdraw} = useJump()
|
||||
onPageScroll(() => {
|
||||
})
|
||||
|
||||
// ============================== 数据 ==================================
|
||||
const {otherParams, list, loading, refreshPage} = usePaging({
|
||||
request: pageCommission,
|
||||
});
|
||||
|
||||
const myCommissionDetail = ref({
|
||||
income: 0, // 总收入
|
||||
expenses: 0 // 总支出
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取总收入、总支出
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetMyCommissionDetail() {
|
||||
myCommissionDetail.value = await getMyCommissionDetail({createTime: otherParams.value.createTime});
|
||||
}
|
||||
|
||||
|
||||
const userDistributionInfo = ref({
|
||||
status: -1,
|
||||
refuse: '',
|
||||
realName: '',
|
||||
levelName: '',
|
||||
superiorName: '',
|
||||
addUpWages: 0,
|
||||
refuseAmount: 0,
|
||||
amount: 0
|
||||
})
|
||||
|
||||
/**
|
||||
* 查询分销商详细信息
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function doCheckIsDistribution() {
|
||||
userDistributionInfo.value = await checkIsDistribution();
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
doCheckIsDistribution()
|
||||
})
|
||||
|
||||
onLoad(() => {
|
||||
otherParams.value.createTime = [`${ selectTimeStr.value } 00:00:00`, `${ selectTimeStr.value } 23:59:59`]
|
||||
refreshPage()
|
||||
doGetMyCommissionDetail()
|
||||
|
||||
})
|
||||
|
||||
// ======================== level tab ====================
|
||||
const leverTabs = [
|
||||
{
|
||||
label: '全部',
|
||||
filed: '',
|
||||
value: 0
|
||||
}, {
|
||||
label: '收入',
|
||||
filed: '',
|
||||
value: 1
|
||||
}, {
|
||||
label: '支出',
|
||||
filed: '',
|
||||
value: 4
|
||||
}]
|
||||
const leverCurrent = ref(0)
|
||||
|
||||
function leverSelect(value) {
|
||||
leverCurrent.value = value
|
||||
if (value !== 0) {
|
||||
otherParams.value.type = value
|
||||
} else {
|
||||
delete otherParams.value.type
|
||||
}
|
||||
refreshPage()
|
||||
}
|
||||
|
||||
// ============================ time ===========================
|
||||
const selectTimeStr = ref(moment(Date.now()).format('YYYY-MM-DD'))
|
||||
const showTime = ref(false)
|
||||
const timeModelValue = ref(Date.now())
|
||||
|
||||
function doShowTime() {
|
||||
showTime.value = true
|
||||
}
|
||||
|
||||
function dateTimeConfirm(e) {
|
||||
selectTimeStr.value = moment(e.value).format('YYYY-MM-DD')
|
||||
otherParams.value.createTime = [`${ selectTimeStr.value } 00:00:00`, `${ selectTimeStr.value } 23:59:59`]
|
||||
showTime.value = false
|
||||
refreshPage()
|
||||
doGetMyCommissionDetail()
|
||||
}
|
||||
|
||||
const colorType = ['red-color', '', 'red-color', 'yellow-color', 'green-color', 'red-color']
|
||||
const statusType = ['待入账', '已入账', '已取消', '提现中', '提现成功', '提现失败']
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="commission">
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff">佣金明细
|
||||
</Header>
|
||||
<view class="commission__inner">
|
||||
<!-- 账户信息-->
|
||||
<view class="account-card">
|
||||
<view class="header flex flex-jc__sb flex-ai__center">
|
||||
<view class="col">
|
||||
<view class="title grey-color">
|
||||
总收益(元)
|
||||
</view>
|
||||
<view class="money">
|
||||
{{ userDistributionInfo.addUpWages || '0.00' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="col flex flex-jc__sb flex-ai__center btn-group">
|
||||
<view
|
||||
class="btn line-btn"
|
||||
@click="goWithdraw({type:0,maxMoney:userDistributionInfo.amount||0})"> 提现
|
||||
</view>
|
||||
<view
|
||||
class="btn"
|
||||
@click="goWithdraw({type:1,maxMoney:userDistributionInfo.amount||0})">转余额
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="gradation-line"></view>
|
||||
|
||||
<view class="bottom">
|
||||
<view class="item">
|
||||
<view class="title grey-color">待入账佣金</view>
|
||||
<view class="count"> {{ userDistributionInfo.refuseAmount || '0.00' }}</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="title grey-color">可提现佣金</view>
|
||||
<view class="count"> {{ userDistributionInfo.amount || '0.00' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 筛选 -->
|
||||
<view class="filtrate-row flex flex-ai__center flex-jc__sb">
|
||||
<view
|
||||
class="date flex flex-ai__center"
|
||||
@click="doShowTime">
|
||||
{{ selectTimeStr }}
|
||||
<u-icon
|
||||
name="arrow-down"
|
||||
color="#333" />
|
||||
</view>
|
||||
<view class="info grey-color">
|
||||
总收入¥{{ myCommissionDetail.income }} 总支出¥{{ myCommissionDetail.expenses }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- 佣金明细列表 -->
|
||||
<view class="money-list">
|
||||
<!-- tab -->
|
||||
<view class="tab-content">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{current:leverCurrent===lever.value}"
|
||||
v-for="lever in leverTabs"
|
||||
:key="lever.value"
|
||||
@click="leverSelect(lever.value)"
|
||||
>
|
||||
{{ lever.label }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- money-item -->
|
||||
<template v-if="list.length>0">
|
||||
<view
|
||||
class="money-item flex flex-jc__sb"
|
||||
v-for="item in list">
|
||||
<view class="left flex flex-column flex-jc__sb">
|
||||
<view class="name">{{ item.distributorName }}</view>
|
||||
<view class="id">订单号:{{ item.orderId }}</view>
|
||||
</view>
|
||||
<view class="right">
|
||||
<view class="title">
|
||||
<text v-if="item.type===1"> +</text>
|
||||
<text v-if="item.type===4"> -</text>
|
||||
${{ item.amount }}
|
||||
</view>
|
||||
<view
|
||||
class="status"
|
||||
:class="[colorType[item.type]]">
|
||||
{{ statusType[item.type] }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<Empty
|
||||
padding="30rpx 0 0 0"
|
||||
:iconSrc="emptyOrderIcon"
|
||||
v-else
|
||||
>
|
||||
您还没有相关订单~
|
||||
</Empty>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 时间选择器 -->
|
||||
<u-datetime-picker
|
||||
:show="showTime"
|
||||
v-model="timeModelValue"
|
||||
mode="date"
|
||||
@confirm="dateTimeConfirm"
|
||||
@cancel="showTime=false"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.commission {
|
||||
&__inner {
|
||||
@include usePadding(34, 34);
|
||||
|
||||
.account-card {
|
||||
@include usePadding(32, 32);
|
||||
width: 100%;
|
||||
background: #333333;
|
||||
border-radius: 15rpx;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
|
||||
.header {
|
||||
padding-bottom: 32rpx;
|
||||
//border-bottom: 1rpx dashed #f5f5f5;
|
||||
|
||||
.col {
|
||||
.money {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@include usePadding(24, 13);
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
.line-btn {
|
||||
border: 1rpx solid #fff;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
gap: 20rpx
|
||||
}
|
||||
}
|
||||
|
||||
.gradation-line {
|
||||
margin: 0 auto;
|
||||
width: 90%;
|
||||
height: 1rpx;
|
||||
background: linear-gradient(to right, #333333, #f5f5f5, #333);
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: 32rpx;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
|
||||
.item {
|
||||
font-size: 28rpx;
|
||||
|
||||
.count {
|
||||
font-size: 32rpx;
|
||||
margin-top: 5rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filtrate-row {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin: 24rpx 0;
|
||||
|
||||
.date {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.money-list {
|
||||
@include usePadding(32, 32);
|
||||
border-radius: 15rpx;
|
||||
background: #fff;
|
||||
|
||||
.tab-content {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
@include useFlex(space-between, center, row, nowrap, 30rpx);
|
||||
|
||||
.tab-item {
|
||||
@include usePadding(0, 16);
|
||||
text-align: center;
|
||||
border-radius: 15rpx;
|
||||
flex: 1 0 auto;
|
||||
background: #f6f8f8;
|
||||
color: #333333;
|
||||
transition: all .3s;
|
||||
|
||||
&.current {
|
||||
background: $primary-color;
|
||||
color: $white-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.money-item {
|
||||
@include usePadding(0, 20);
|
||||
width: 100%;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.left {
|
||||
color: $tips-color;
|
||||
|
||||
.name {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.id {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
.title {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.status {
|
||||
@include usePadding(24, 4);
|
||||
background: #f6f6f6;
|
||||
color: #999999;
|
||||
border-radius: 50rpx;
|
||||
font-size: 24rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grey-color {
|
||||
color: $tips-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.orange-color {
|
||||
background: #E85A2B12 !important;
|
||||
color: #E85A2B !important;
|
||||
}
|
||||
|
||||
.yellow-color {
|
||||
background: #FFFADF !important;
|
||||
color: #E8A31D !important;
|
||||
}
|
||||
|
||||
.green-color {
|
||||
background: #EAF9EC !important;
|
||||
color: #28c445 !important;
|
||||
}
|
||||
|
||||
.red-color {
|
||||
background: rgba(232, 43, 150, 0.07) !important;
|
||||
color: #df151c !important;
|
||||
}
|
||||
</style>
|
243
views/distribution/datum/index.vue
Normal file
243
views/distribution/datum/index.vue
Normal file
@ -0,0 +1,243 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-17 11:12
|
||||
@description:我的资料
|
||||
@update: 2024-01-17 11:12
|
||||
-->
|
||||
<script setup>
|
||||
import Header from '@/components/Header/index.vue'
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { onPageScroll } from "@dcloudio/uni-app";
|
||||
import { useMainStore } from "@/store/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { checkIsDistribution } from "@/api/distribution";
|
||||
import { ref } from "vue";
|
||||
import { useJump } from "@/hooks/useJump";
|
||||
|
||||
const {goDistributionApplyFor} = useJump();
|
||||
|
||||
const {scrollTop} = useScroll()
|
||||
onPageScroll(() => {
|
||||
})
|
||||
const mainStore = useMainStore();
|
||||
const {user, areaList} = storeToRefs(mainStore);
|
||||
|
||||
const distributionDetailsInfo = ref({})
|
||||
|
||||
async function doGetDistributionDetails() {
|
||||
distributionDetailsInfo.value = await checkIsDistribution();
|
||||
}
|
||||
|
||||
function doUpdate() {
|
||||
goDistributionApplyFor({update: true})
|
||||
}
|
||||
|
||||
doGetDistributionDetails()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
class="apply-for"
|
||||
v-if="user">
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff">我的资料
|
||||
</Header>
|
||||
<view
|
||||
class="inner"
|
||||
v-if="distributionDetailsInfo.realName">
|
||||
<view class="bg"></view>
|
||||
<!-- userinfo -->
|
||||
<view class="userinfo flex flex-column flex-ai__center flex-jc__center">
|
||||
<image :src="user.avatar" />
|
||||
<text class="username">{{ user.nickname }}</text>
|
||||
</view>
|
||||
<!-- card -->
|
||||
<view class="apply-card">
|
||||
<!-- form -->
|
||||
<view class="form">
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
真实姓名
|
||||
</text>
|
||||
<div class="right">
|
||||
<input
|
||||
type="text"
|
||||
disabled
|
||||
v-model="distributionDetailsInfo.realName"
|
||||
placeholder="请输入真实姓名" />
|
||||
</div>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
现居城市
|
||||
</text>
|
||||
<div
|
||||
class="right flex flex-jc__sb flex-ai__center">
|
||||
<text class="placeholder">{{ distributionDetailsInfo.address || '-' }}</text>
|
||||
</div>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">
|
||||
申请原因
|
||||
</text>
|
||||
<div class="right">
|
||||
<input
|
||||
disabled
|
||||
v-model="distributionDetailsInfo.reason"
|
||||
type="text"
|
||||
placeholder="请输入申请原因" />
|
||||
</div>
|
||||
</view>
|
||||
<view class="submit">
|
||||
<view
|
||||
class="button animation-button"
|
||||
@click="doUpdate">修改信息
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.apply-for {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.inner {
|
||||
position: relative;
|
||||
|
||||
.bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
aspect-ratio: 750/460;
|
||||
background: #333333;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.userinfo {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
@include usePadding(0, 72);
|
||||
|
||||
image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid #fff;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 34rpx;
|
||||
margin-top: 30rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.apply-card {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: #f6f6f6;
|
||||
border-radius: 30rpx;
|
||||
@include usePadding(34, 34);
|
||||
|
||||
.form {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
@include usePadding(32, 0);
|
||||
border-radius: 30rpx;
|
||||
|
||||
.row {
|
||||
@include useFlex(flex-start, center);
|
||||
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.right {
|
||||
@include usePadding(0, 32);
|
||||
border-bottom: 1rpx solid #f6f6f6;
|
||||
flex-grow: 1;
|
||||
|
||||
.placeholder {
|
||||
color: #808080;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.submit {
|
||||
width: 80%;
|
||||
position: fixed;
|
||||
bottom: 50rpx;
|
||||
@include useFlex(center, center, column);
|
||||
|
||||
|
||||
.tips {
|
||||
color: $tips-color;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 100%;
|
||||
margin-top: 30rpx;
|
||||
height: 80rpx;
|
||||
@include useFlex(center, center);
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.state {
|
||||
width: 100%;
|
||||
@include useFlex(center, center, column);
|
||||
@include usePadding(0, 50);
|
||||
|
||||
image {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 230rpx;
|
||||
height: 80rpx;
|
||||
background: #333333;
|
||||
color: #fff;
|
||||
@include useFlex(center, center, column);
|
||||
border-radius: 15rpx;
|
||||
margin: 0 15rpx;
|
||||
}
|
||||
|
||||
.line-btn {
|
||||
background: #fff;
|
||||
border: 1rpx solid #333333;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
159
views/distribution/goods/index.vue
Normal file
159
views/distribution/goods/index.vue
Normal file
@ -0,0 +1,159 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-17 17:00
|
||||
@description:分销商品
|
||||
@update: 2024-01-17 17:00
|
||||
-->
|
||||
<script setup>
|
||||
import Header from '@/components/Header/index.vue'
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { onLoad, onPageScroll, onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
|
||||
import { onMounted, ref } from "vue";
|
||||
import GoodsCom from "@/components/goodsComponents/Goods.vue";
|
||||
import { usePage } from "@/hooks";
|
||||
import { getProductList } from "@/api/product";
|
||||
import { useRouter } from "@/hooks/useRouter";
|
||||
import { useMainStore } from "@/store/store";
|
||||
import { useShare } from "@/hooks/useShare";
|
||||
import GoodsSharePopup from '@/components/Share/Goods.vue'
|
||||
import GoodsPoster from '@/components/Poster/Goods.vue'
|
||||
|
||||
const mainStore = useMainStore();
|
||||
const {scrollTop} = useScroll()
|
||||
onPageScroll(() => {
|
||||
})
|
||||
const {refresh, dataList, loadend, loading, listEmpty, otherQuery} = usePage(getProductList)
|
||||
const {shareAppMessage, shareTimeline, distributionGoodsDetailShare, shareH5} = useShare();
|
||||
|
||||
onShareAppMessage(shareAppMessage)
|
||||
onShareTimeline(shareTimeline)
|
||||
const {push, getParams} = useRouter()
|
||||
|
||||
const distributorId = ref()
|
||||
onLoad((options) => {
|
||||
const params = getParams(options);
|
||||
distributorId.value = params.distributionId
|
||||
otherQuery.value = {
|
||||
isDistribution: 1
|
||||
}
|
||||
})
|
||||
|
||||
// ======================= 分享相关 ================================
|
||||
const goodsShare = ref()
|
||||
const goodsPoster = ref()
|
||||
|
||||
function doShare(goods) {
|
||||
goodsShare.value.open('分享赚', goods)
|
||||
}
|
||||
|
||||
async function shareConfirm(shareItem, goods) {
|
||||
distributionGoodsDetailShare(goods, distributorId.value)
|
||||
if (shareItem.value === 'photo') {
|
||||
goodsPoster.value.open(goods)
|
||||
return
|
||||
}
|
||||
if (shareItem.value === 'wechat') {
|
||||
shareH5()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
refresh()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<view class="goods-list">
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff"> 分销商品
|
||||
</Header>
|
||||
<view class="goods-list__inner">
|
||||
<template
|
||||
v-for="item in dataList"
|
||||
:key="item.id"
|
||||
>
|
||||
<view class="product">
|
||||
<GoodsCom
|
||||
row
|
||||
img-width="160rpx"
|
||||
:ratio="true"
|
||||
:goods="item"
|
||||
infoPadding="10rpx 10rpx"
|
||||
>
|
||||
<template #options>
|
||||
<view class="good-bottom">
|
||||
<view class="price">
|
||||
¥{{ item.price }}
|
||||
</view>
|
||||
<view class="sale flex flex-jc__sb flex-ai__end">
|
||||
<view class="left">
|
||||
<text class="gray-color">预计佣金:</text>
|
||||
¥999-999
|
||||
</view>
|
||||
<view
|
||||
class="btn"
|
||||
@click.stop="doShare(item)">
|
||||
分享赚
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</GoodsCom>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<GoodsSharePopup
|
||||
ref="goodsShare"
|
||||
@share="shareConfirm" />
|
||||
|
||||
<GoodsPoster ref="goodsPoster" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.goods-list {
|
||||
&__inner {
|
||||
width: 100%;
|
||||
@include usePadding(34, 34);
|
||||
|
||||
.product {
|
||||
background: #fff;
|
||||
border-radius: 15rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.good-bottom {
|
||||
width: 100%;
|
||||
|
||||
.price {
|
||||
color: $primary-color;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.sale {
|
||||
width: 100%;
|
||||
font-size: 24rpx;
|
||||
|
||||
.btn {
|
||||
@include usePadding(24, 11);
|
||||
color: #fff;
|
||||
background: #333;
|
||||
border-radius: 15rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gray-color {
|
||||
color: $tips-color;
|
||||
}
|
||||
</style>
|
142
views/distribution/order/component/DistributorInfoItem.vue
Normal file
142
views/distribution/order/component/DistributorInfoItem.vue
Normal file
@ -0,0 +1,142 @@
|
||||
<!--
|
||||
@name: DistributorDataItem
|
||||
@author: kahu4
|
||||
@date: 2024-01-17 15:50
|
||||
@description:DistributorDataItem
|
||||
@update: 2024-01-17 15:50
|
||||
-->
|
||||
<script setup>
|
||||
|
||||
import { distributorDataList } from "../index.data";
|
||||
import { computed, toRefs } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const {data} = toRefs(props)
|
||||
|
||||
const tagType = computed(() => {
|
||||
return ["待结算", "已结算", "已取消"][data.value.status]
|
||||
})
|
||||
const tagClass = computed(() => {
|
||||
return ["", "success-tag", "default-tag"][data.value.status]
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="user-item">
|
||||
<view class="userinfo-row flex flex-jc__start">
|
||||
<view class="userinfo flex flex-column flex-jc__sb">
|
||||
<view class="username flex flex-jc__sb">
|
||||
{{ data.userName }}
|
||||
<text
|
||||
class="tag default-tag"
|
||||
:class="tagClass">
|
||||
{{ tagType }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="time">订单号:{{ data.orderId }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="distribution-info-row">
|
||||
<view
|
||||
class="info-item"
|
||||
v-for="distributorData in distributorDataList"
|
||||
:key="distributorData.label">
|
||||
<view class="title">{{ distributorData.label }}</view>
|
||||
<view class="count">{{ data[distributorData.field] || 0 }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.user-item {
|
||||
margin-bottom: 16rpx;
|
||||
@include usePadding(32, 32);
|
||||
background: $white-color;
|
||||
border-radius: 15rpx;
|
||||
|
||||
.userinfo-row {
|
||||
padding-bottom: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
border-bottom: 1rpx solid $page-bg-color;
|
||||
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 24rpx;
|
||||
background: #fbfbfb;
|
||||
}
|
||||
|
||||
.userinfo {
|
||||
width: 100%;
|
||||
|
||||
.username {
|
||||
width: 100%;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
@include useFlex(space-between, center);
|
||||
|
||||
.tag {
|
||||
@include usePadding(24, 4);
|
||||
margin-left: 12rpx;
|
||||
color: $primary-color;
|
||||
background: #E85A2B12;
|
||||
border-radius: 50rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.default-tag {
|
||||
color: #999999;
|
||||
background: #F6F6F6;
|
||||
}
|
||||
|
||||
.success-tag {
|
||||
background: #EAF9EC !important;
|
||||
color: #28c445 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.time {
|
||||
color: $tips-color;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.distribution-info-row {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
column-gap: 16rpx;
|
||||
|
||||
.info-item {
|
||||
@include usePadding(16, 16);
|
||||
border-radius: 15rpx;
|
||||
background: #f6f6f6;
|
||||
color: #7A7A7A;
|
||||
font-size: 24rpx;
|
||||
|
||||
.title {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.count {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
17
views/distribution/order/index.data.js
Normal file
17
views/distribution/order/index.data.js
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name: index.data
|
||||
* @author: kahu4
|
||||
* @date: 2024-01-17 15:39
|
||||
* @description:index.data
|
||||
* @update: 2024-01-17 15:39
|
||||
* */
|
||||
export const distributorDataList = [
|
||||
{
|
||||
label: '商品金额(元)',
|
||||
field: 'productPrice'
|
||||
},
|
||||
{
|
||||
label: '商品佣金(元)',
|
||||
field: 'wages'
|
||||
},
|
||||
]
|
264
views/distribution/order/index.vue
Normal file
264
views/distribution/order/index.vue
Normal file
@ -0,0 +1,264 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-17 16:30
|
||||
@description:分销订单
|
||||
@update: 2024-01-17 16:30
|
||||
-->
|
||||
<script setup>
|
||||
import Header from '@/components/Header/index.vue'
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { onLoad, onPageScroll } from "@dcloudio/uni-app";
|
||||
import { ref } from "vue";
|
||||
import DistributorInfoItem from "./component/DistributorInfoItem.vue";
|
||||
import UniDatetimePicker from "@/components/Uni/uni-datetime-picker/uni-datetime-picker.vue";
|
||||
import { usePaging } from "@/hooks/usePaging";
|
||||
import { getDistributionDetail, pageDistributionOrder } from "@/api/distribution";
|
||||
import moment from "moment";
|
||||
import { emptyOrderIcon } from "@/utils/images";
|
||||
import Empty from "@/components/Empty/index.vue";
|
||||
|
||||
// ======================= hooks =====================
|
||||
const {scrollTop} = useScroll()
|
||||
onPageScroll(() => {
|
||||
})
|
||||
|
||||
// ======================== level tab ====================
|
||||
const leverTabs = ref([
|
||||
{label: '全部', filed: '', value: 0, time: []},
|
||||
{
|
||||
label: '今日',
|
||||
filed: '',
|
||||
value: 1,
|
||||
time: [moment().format("YYYY-MM-DD 00:00:00"), moment().format("YYYY-MM-DD 23:59:59")]
|
||||
},
|
||||
{
|
||||
label: '昨日',
|
||||
filed: '',
|
||||
value: 2,
|
||||
time: [moment().subtract(1, "days").format("YYYY-MM-DD 00:00:00"), moment().subtract(1, "days").format("YYYY-MM-DD 23:59:59")]
|
||||
},
|
||||
{
|
||||
label: '本月', filed: '', value: 3, time: [
|
||||
moment()
|
||||
.startOf("month")
|
||||
.format("YYYY-MM-DD 00:00:00"),
|
||||
moment()
|
||||
.endOf("month")
|
||||
.format("YYYY-MM-DD 23:59:59")
|
||||
]
|
||||
},
|
||||
{label: '自定义', filed: '', value: 4, time: []}
|
||||
])
|
||||
const leverCurrent = ref(0)
|
||||
|
||||
function leverSelect(value) {
|
||||
if (value === 4) {
|
||||
showSelectTime()
|
||||
leverCurrent.value = value
|
||||
} else {
|
||||
leverTabs.value[4].time = []
|
||||
leverCurrent.value = value
|
||||
otherParams.value.createTime = leverTabs.value[leverCurrent.value].time
|
||||
if(!otherParams.value.createTime || otherParams.value.createTime.length<=0){
|
||||
delete otherParams.value.createTime
|
||||
}
|
||||
refreshPage()
|
||||
doGetDistributionDetail()
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== date ==============================
|
||||
const dateRangeRef = ref()
|
||||
|
||||
function showSelectTime() {
|
||||
dateRangeRef.value.show()
|
||||
}
|
||||
|
||||
function timeChange(e) {
|
||||
leverTabs.value[4].time = e
|
||||
// 请求接口
|
||||
otherParams.value.createTime = e
|
||||
refreshPage()
|
||||
doGetDistributionDetail()
|
||||
}
|
||||
|
||||
// ===================== data ==============================
|
||||
const {otherParams, list, loading, refreshPage} = usePaging({
|
||||
request: pageDistributionOrder,
|
||||
load:false
|
||||
});
|
||||
|
||||
const distributionDetail = ref({
|
||||
"refuseAmount": 0,
|
||||
"amount": 0,
|
||||
"orderCount": 0
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取分销佣金详情
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetDistributionDetail() {
|
||||
const data = {}
|
||||
if(otherParams.value.createTime&&otherParams.value.createTime.length>0){
|
||||
data.createTime = otherParams.value.createTime
|
||||
}
|
||||
distributionDetail.value = await getDistributionDetail(data);
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
otherParams.value.createTime = leverTabs.value[leverCurrent.value].time
|
||||
if(!otherParams.value.createTime || otherParams.value.createTime.length<=0){
|
||||
delete otherParams.value.createTime
|
||||
}
|
||||
refreshPage()
|
||||
doGetDistributionDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="distribution-order">
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
header-area-bg="#fff"
|
||||
system-bar-area-bg="#fff">
|
||||
分销订单
|
||||
</Header>
|
||||
<!-- tabs -->
|
||||
<view class="tab-content">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{current:leverCurrent===lever.value}"
|
||||
v-for="lever in leverTabs"
|
||||
:key="lever.value"
|
||||
@click="leverSelect(lever.value)"
|
||||
>
|
||||
{{ lever.label }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- main -->
|
||||
<view class="distribution-order__inner">
|
||||
<!-- 统计 -->
|
||||
<view class="statistics-card">
|
||||
<view class="header">
|
||||
共{{ distributionDetail.orderCount }}笔订单
|
||||
</view>
|
||||
<view class="row flex flex-jc__sb flex-ai__center">
|
||||
<view class="item">
|
||||
<view class="count">{{ distributionDetail.refuseAmount }}</view>
|
||||
<view class="title gray-color">待结算佣金(元)</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="count">{{ distributionDetail.amount }}</view>
|
||||
<view class="title gray-color">已结算佣金(元)</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-list">
|
||||
<template v-if="list.length>0">
|
||||
<DistributorInfoItem
|
||||
v-for="item in list"
|
||||
:data="item" />
|
||||
</template>
|
||||
<Empty :icon-src="emptyOrderIcon" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 时间选择器 -->
|
||||
<uni-datetime-picker
|
||||
style="width: 0;height: 0;overflow: hidden"
|
||||
ref="dateRangeRef"
|
||||
type="datetimerange"
|
||||
:value="leverTabs[4].time"
|
||||
@change="timeChange"
|
||||
>
|
||||
</uni-datetime-picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.distribution-order {
|
||||
width: 100%;
|
||||
|
||||
.tab-content {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
@include usePadding(34, 34);
|
||||
@include useFlex(space-between, center, row, nowrap, 30rpx);
|
||||
|
||||
.tab-item {
|
||||
@include usePadding(0, 16);
|
||||
text-align: center;
|
||||
border-radius: 15rpx;
|
||||
flex: 1 0 auto;
|
||||
background: #f6f8f8;
|
||||
color: #333333;
|
||||
transition: all .3s;
|
||||
|
||||
&.current {
|
||||
background: $primary-color;
|
||||
color: $white-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__inner {
|
||||
@include usePadding(34, 34)
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
width: 100%;
|
||||
@include usePadding(8, 8);
|
||||
border-radius: 15rpx;
|
||||
background: #333333;
|
||||
color: #fff;
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
@include usePadding(24, 16);
|
||||
border-radius: 15rpx;
|
||||
background: linear-gradient(to right, rgba(246, 246, 246, 0.4) 0%, rgba(246, 246, 246, 0.2) 15%, transparent 50%);
|
||||
}
|
||||
|
||||
.row {
|
||||
@include usePadding(0, 32);
|
||||
|
||||
.item {
|
||||
flex: 1 0 auto;
|
||||
font-size: 24rpx;
|
||||
@include useFlex(center, center, column);
|
||||
border-right: 1rpx solid #fff;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-list {
|
||||
@include usePadding(0, 34)
|
||||
}
|
||||
|
||||
.gray-color {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
// #ifdef MP-WEIXIN
|
||||
.uni-date{
|
||||
position: absolute;
|
||||
top: -500px!important;
|
||||
}
|
||||
// #endif
|
||||
</style>
|
121
views/distribution/team/component/DistributorInfoItem.vue
Normal file
121
views/distribution/team/component/DistributorInfoItem.vue
Normal file
@ -0,0 +1,121 @@
|
||||
<!--
|
||||
@name: DistributorDataItem
|
||||
@author: kahu4
|
||||
@date: 2024-01-17 15:50
|
||||
@description:DistributorDataItem
|
||||
@update: 2024-01-17 15:50
|
||||
-->
|
||||
<script setup>
|
||||
|
||||
import { distributorDataList } from "@/views/distribution/team/index.data";
|
||||
import { toRefs } from "vue";
|
||||
import moment from "moment";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const {data} = toRefs(props)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="user-item">
|
||||
<view class="userinfo-row flex flex-jc__start">
|
||||
<image :src="data.avatar" />
|
||||
<view class="userinfo flex flex-column flex-jc__sb">
|
||||
<view class="username">
|
||||
{{ data.nickname }}
|
||||
<text class="tag">{{ data.isDistributor === 0 ? '客户' : '分销商' }}</text>
|
||||
</view>
|
||||
<view class="time">加入时间:{{ moment(data.createTime).format('YYYY-MM-DD') }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="distribution-info-row">
|
||||
<view
|
||||
class="info-item"
|
||||
v-for="distributorData in distributorDataList"
|
||||
:key="distributorData.label">
|
||||
<view class="title">{{ distributorData.label }}</view>
|
||||
<view class="count">{{ data[distributorData.field] || '0' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.user-item {
|
||||
margin-bottom: 16rpx;
|
||||
@include usePadding(32, 32);
|
||||
background: $white-color;
|
||||
border-radius: 15rpx;
|
||||
|
||||
.userinfo-row {
|
||||
padding-bottom: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
border-bottom: 1rpx solid $page-bg-color;
|
||||
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 24rpx;
|
||||
background: #fbfbfb;
|
||||
}
|
||||
|
||||
.userinfo {
|
||||
|
||||
|
||||
.username {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
|
||||
.tag {
|
||||
@include usePadding(24, 4);
|
||||
margin-left: 12rpx;
|
||||
color: $primary-color;
|
||||
background: #E85A2B12;
|
||||
border-radius: 50rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.time {
|
||||
color: $tips-color;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.distribution-info-row {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
column-gap: 16rpx;
|
||||
|
||||
.info-item {
|
||||
@include usePadding(16, 16);
|
||||
border-radius: 15rpx;
|
||||
background: #f6f6f6;
|
||||
color: #7A7A7A;
|
||||
font-size: 24rpx;
|
||||
|
||||
.title {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.count {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
21
views/distribution/team/index.data.js
Normal file
21
views/distribution/team/index.data.js
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name: index.data
|
||||
* @author: kahu4
|
||||
* @date: 2024-01-17 15:39
|
||||
* @description:index.data
|
||||
* @update: 2024-01-17 15:39
|
||||
* */
|
||||
export const distributorDataList = [
|
||||
{
|
||||
label: '商品金额(元)',
|
||||
field: 'productAmount'
|
||||
},
|
||||
{
|
||||
label: '订单数',
|
||||
field: 'orderCount'
|
||||
},
|
||||
{
|
||||
label: '佣金收入(元)',
|
||||
field: 'wages'
|
||||
},
|
||||
]
|
170
views/distribution/team/index.vue
Normal file
170
views/distribution/team/index.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-17 14:52
|
||||
@description:我的团队
|
||||
@update: 2024-01-17 14:52
|
||||
-->
|
||||
<script setup>
|
||||
import Header from '@/components/Header/index.vue'
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { onLoad, onPageScroll } from "@dcloudio/uni-app";
|
||||
import { ref } from "vue";
|
||||
import DistributorInfoItem from "@/views/distribution/team/component/DistributorInfoItem.vue";
|
||||
import { usePaging } from "@/hooks/usePaging";
|
||||
import { getUserAddCount, getUserAllCount, pageMyUserTeam } from "@/api/distribution";
|
||||
import Empty from "@/components/Empty/index.vue"
|
||||
import { emptyOrderIcon } from "@/utils/images";
|
||||
// ======================= hooks =====================
|
||||
const {scrollTop} = useScroll()
|
||||
onPageScroll(() => {
|
||||
})
|
||||
|
||||
// ======================== level tab ====================
|
||||
const leverTabs = [{label: '一级', filed: 'p1Count', value: 1}, {label: '二级', filed: 'p2Count', value: 2}]
|
||||
const leverCurrent = ref(1)
|
||||
|
||||
async function leverSelect(value) {
|
||||
leverCurrent.value = value
|
||||
otherParams.value.type = value
|
||||
await refreshPage()
|
||||
await doGetUserAddCount()
|
||||
}
|
||||
|
||||
const {loading, otherParams, list, refreshPage} = usePaging({
|
||||
request: pageMyUserTeam,
|
||||
});
|
||||
|
||||
const addUserCount = ref(0)
|
||||
|
||||
async function doGetUserAddCount() {
|
||||
addUserCount.value = await getUserAddCount({
|
||||
type: otherParams.value.type
|
||||
})
|
||||
}
|
||||
|
||||
const allUserCount = ref({
|
||||
p1Count: 0,
|
||||
p2Count: 0
|
||||
})
|
||||
|
||||
async function doGetUserAllCount() {
|
||||
allUserCount.value = await getUserAllCount()
|
||||
}
|
||||
|
||||
onLoad(async () => {
|
||||
otherParams.value.type = 1
|
||||
await refreshPage()
|
||||
await doGetUserAddCount()
|
||||
await doGetUserAllCount()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="team">
|
||||
<Header
|
||||
:scroll-top="scrollTop"
|
||||
system-bar-area-bg="#fff"
|
||||
header-area-bg="#fff"> 我的团队
|
||||
</Header>
|
||||
<!-- tabs -->
|
||||
<view class="tab-content">
|
||||
<view class="tab-inner">
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{current:leverCurrent===lever.value}"
|
||||
v-for="lever in leverTabs"
|
||||
:key="lever.value"
|
||||
@click="leverSelect(lever.value)"
|
||||
>
|
||||
{{ lever.label }}({{ allUserCount[lever.filed] }})
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- main -->
|
||||
<view class="main">
|
||||
<!-- user num -->
|
||||
<view class="user-num-box">
|
||||
<view class="title">今日新增:
|
||||
<text class="num primary-color">{{ addUserCount }}人</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- user list -->
|
||||
<view
|
||||
class="user-list-box"
|
||||
v-if="list.length>0">
|
||||
<template
|
||||
v-for="item in list"
|
||||
:key="item.id">
|
||||
<DistributorInfoItem :data="item" />
|
||||
</template>
|
||||
</view>
|
||||
<view
|
||||
class="user-list-box"
|
||||
v-else>
|
||||
<Empty :icon-src="emptyOrderIcon" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.team {
|
||||
width: 100%;
|
||||
|
||||
.tab-content {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
@include usePadding(34, 34);
|
||||
|
||||
.tab-inner {
|
||||
@include useFlex(space-between, center, row, nowrap, 30rpx);
|
||||
border-radius: 50rpx;
|
||||
width: 100%;
|
||||
background: #f5f5f5;
|
||||
border: 5rpx solid #f5f5f5;
|
||||
overflow: hidden;
|
||||
|
||||
.tab-item {
|
||||
border-radius: 50rpx;
|
||||
|
||||
@include usePadding(0, 16);
|
||||
text-align: center;
|
||||
flex: 1 0 auto;
|
||||
background: #f6f8f8;
|
||||
color: #333333;
|
||||
transition: all .3s;
|
||||
|
||||
&.current {
|
||||
background: $white-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
@include usePadding(34, 34);
|
||||
}
|
||||
|
||||
.user-num-box {
|
||||
margin-bottom: 24rpx;
|
||||
width: 100%;
|
||||
@include useFlex(flex-start, center);
|
||||
font-size: 28rpx;
|
||||
|
||||
.title {
|
||||
color: $tips-color;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.user-list-box {
|
||||
}
|
||||
}
|
||||
</style>
|
40
views/member/index/index.data.js
Normal file
40
views/member/index/index.data.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { memberVip1, memberVip2, memberVip3, memberVip4, memberVip5 } from "@/utils/images";
|
||||
|
||||
export const vipList = [
|
||||
{
|
||||
label: '普通会员',
|
||||
value: 0,
|
||||
bg: memberVip1
|
||||
},
|
||||
{
|
||||
label: '白银会员',
|
||||
value: 1,
|
||||
bg: memberVip2
|
||||
},
|
||||
{
|
||||
label: '黄金会员',
|
||||
value: 2,
|
||||
bg: memberVip3
|
||||
},
|
||||
{
|
||||
label: '铂金会员',
|
||||
value: 3,
|
||||
bg: memberVip4
|
||||
},
|
||||
{
|
||||
label: '钻石会员',
|
||||
value: 4,
|
||||
bg: memberVip5
|
||||
}
|
||||
]
|
||||
|
||||
export const EnumTaskButton = {
|
||||
CONSUME: 'consume', // 消费
|
||||
INVITE: 'invite' // 邀请
|
||||
}
|
||||
|
||||
export const taskButtonLabel = {
|
||||
[EnumTaskButton.CONSUME]: '消费',
|
||||
[EnumTaskButton.INVITE]: '邀请'
|
||||
}
|
||||
|
482
views/member/index/index.vue
Normal file
482
views/member/index/index.vue
Normal file
@ -0,0 +1,482 @@
|
||||
<!--
|
||||
@name: index
|
||||
@author: kahu4
|
||||
@date: 2024-01-18 17:35
|
||||
@description:index
|
||||
@update: 2024-01-18 17:35
|
||||
-->
|
||||
<script setup>
|
||||
import Header from "@/components/Header/index.vue"
|
||||
import { useScroll } from "@/hooks/useScroll";
|
||||
import { onMounted, ref, toRefs, unref } from "vue";
|
||||
import { getMemberEquityList, getMemberLevelList, getMemberTaskList, getUserMemberLevel } from "@/api/member";
|
||||
import { EnumTaskButton, taskButtonLabel } from "@/views/member/index/index.data";
|
||||
import { useJump } from "@/hooks/useJump";
|
||||
import InviteFriends from "@/components/Share/InviteFriends.vue";
|
||||
import InviteFriendsPoster from "@/components/Poster/InviteFriends.vue";
|
||||
|
||||
import { useShare } from "@/hooks/useShare";
|
||||
import { useMainStore } from "@/store/store";
|
||||
import { onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
|
||||
|
||||
const {scrollTop} = useScroll();
|
||||
const loading = ref(false)
|
||||
const {goHome} = useJump()
|
||||
// ================================ swiper ================================
|
||||
const swiperCurrent = ref(0) // 当前swiper选中
|
||||
|
||||
/**
|
||||
*
|
||||
* @param e
|
||||
*/
|
||||
function swiperChange(e) {
|
||||
swiperCurrent.value = e.detail.current
|
||||
// 刷新会员权益
|
||||
doGetMemberEquityList()
|
||||
}
|
||||
|
||||
// ================================ 会员等级列表 ================================
|
||||
// 当前用户会员信息
|
||||
const memberLeverInfo = ref({
|
||||
currentGrowthValue: 0,
|
||||
currentLevel: null,
|
||||
needGrowthValue: 0,
|
||||
nextLevel: null
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取当前用户会员信息
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetUserMemberLevel() {
|
||||
try {
|
||||
loading.value = true
|
||||
memberLeverInfo.value = await getUserMemberLevel()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const memberLevelList = ref([])
|
||||
|
||||
/**
|
||||
* 获取会员等级列表
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetMemberLevelList() {
|
||||
try {
|
||||
loading.value = true
|
||||
const data = await getMemberLevelList()
|
||||
// 获取当前用户的等级id
|
||||
let index = 0
|
||||
if (memberLeverInfo.value.currentLevel) {
|
||||
// 没有会员等级
|
||||
index = data.findIndex(item => item.id === memberLeverInfo.value.currentLevel.id);
|
||||
}
|
||||
swiperCurrent.value = index
|
||||
memberLevelList.value = data
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================================== 会员任务 ==============================================
|
||||
const memberTaskList = ref([])
|
||||
|
||||
/**
|
||||
* 获取会员任务列表
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetMemberTaskList() {
|
||||
try {
|
||||
loading.value = true
|
||||
memberTaskList.value = await getMemberTaskList()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleTaskButtonClick(type) {
|
||||
const tabFunc = {
|
||||
[EnumTaskButton.CONSUME]: goToConsumption,
|
||||
[EnumTaskButton.INVITE]: goToInvite
|
||||
}
|
||||
tabFunc[type]()
|
||||
}
|
||||
|
||||
/**
|
||||
* 去消费
|
||||
*/
|
||||
function goToConsumption() {
|
||||
goHome()
|
||||
}
|
||||
|
||||
/**
|
||||
* 去邀请
|
||||
*/
|
||||
function goToInvite() {
|
||||
openShare()
|
||||
}
|
||||
|
||||
// ================================= 会员权益 ====================================================
|
||||
const memberEquityList = ref([])
|
||||
|
||||
/**
|
||||
* 获取会员权益
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function doGetMemberEquityList() {
|
||||
try {
|
||||
loading.value = true
|
||||
const currentLevelId = memberLevelList.value[swiperCurrent.value].id
|
||||
memberEquityList.value = await getMemberEquityList({levelId: currentLevelId});
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ================================ 邀请 =====================================================
|
||||
const mainStore = useMainStore()
|
||||
const {user} = toRefs(mainStore)
|
||||
const {userInvitationShare, shareH5, shareAppMessage, shareTimeline} = useShare()
|
||||
onShareAppMessage(shareAppMessage)
|
||||
onShareTimeline(shareTimeline)
|
||||
const inviteFriendsRef = ref()
|
||||
const inviteFriendsPosterRef = ref()
|
||||
|
||||
function openShare() {
|
||||
inviteFriendsRef.value.open()
|
||||
}
|
||||
|
||||
function handleShareConfirm(shareItem) {
|
||||
const share = userInvitationShare(unref(user).invitationCode || '');
|
||||
if (shareItem.value === 'wechat') {
|
||||
shareH5()
|
||||
} else {
|
||||
inviteFriendsPosterRef.value.open(share)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 1.获取当前用户等级 2.获取会员等级列表(寻找默认选中) 3.获取会员任务列表 4.获取会员权益
|
||||
await doGetUserMemberLevel()
|
||||
await doGetMemberLevelList()
|
||||
await doGetMemberTaskList()
|
||||
await doGetMemberEquityList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<Header
|
||||
:header-area-bg="'#fff'"
|
||||
system-bar-area-bg="#fff"
|
||||
:scroll-top="scrollTop"
|
||||
prop-up
|
||||
>
|
||||
会员中心
|
||||
</Header>
|
||||
<view
|
||||
class="member"
|
||||
v-if="memberLevelList.length>0">
|
||||
<!-- 背景 -->
|
||||
<view
|
||||
class="bg"
|
||||
:style="{
|
||||
background: `linear-gradient(to bottom, ${memberLevelList[swiperCurrent].colorNum}, #F6F6F6)`
|
||||
}"
|
||||
:class="`bg-${swiperCurrent}`"></view>
|
||||
<!-- swiper -->
|
||||
<swiper
|
||||
class="swiper"
|
||||
previous-margin="24rpx"
|
||||
next-margin="24rpx"
|
||||
circular
|
||||
:current="swiperCurrent"
|
||||
@change="swiperChange">
|
||||
<swiper-item
|
||||
v-for="item in memberLevelList"
|
||||
:key="item.id"
|
||||
>
|
||||
<view
|
||||
class="swiper-item"
|
||||
:style="{backgroundImage:`url(${item.backgroundUrl})`,color:item.colorNum}">
|
||||
<view class="name">
|
||||
{{ item.levelName }}
|
||||
</view>
|
||||
<view class="level">
|
||||
<template v-if="memberLeverInfo.currentLevel">
|
||||
当前等级 {{ memberLeverInfo.currentLevel.levelName }}
|
||||
</template>
|
||||
<template v-else>
|
||||
当前不是会员
|
||||
</template>
|
||||
</view>
|
||||
<view class="bottom flex flex-ai__center flex-jc__sb">
|
||||
<!-- 会员 -->
|
||||
<template v-if="memberLeverInfo.currentLevel">
|
||||
<view class="left">
|
||||
<view class="process">
|
||||
<view
|
||||
class="schedule"
|
||||
:style="{width:`${memberLeverInfo.currentGrowthValue>=item.growthValue?100:memberLeverInfo.currentGrowthValue/item.growthValue}%`,background:item.colorNum}"></view>
|
||||
</view>
|
||||
<view
|
||||
class="tips"
|
||||
v-if="item.growthValue > memberLeverInfo.currentGrowthValue">
|
||||
再获取{{ item.growthValue - memberLeverInfo.currentGrowthValue }}经验,可升级为{{ item.levelName }}会员
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="right"
|
||||
v-if="memberEquityList.length>0">
|
||||
尊享以下{{ memberEquityList.length }}项特权
|
||||
</view>
|
||||
</template>
|
||||
<!-- 非会员 -->
|
||||
<template v-else>
|
||||
<view class="left">
|
||||
<view class="process">
|
||||
<view
|
||||
class="schedule"
|
||||
:style="{width:`${memberLeverInfo.currentGrowthValue/item.growthValue}%`,background:item.colorNum}"></view>
|
||||
</view>
|
||||
<view
|
||||
class="tips"
|
||||
v-if="item.growthValue > memberLeverInfo.currentGrowthValue">
|
||||
再获取{{ item.growthValue - memberLeverInfo.currentGrowthValue }}经验,可升级为{{ item.levelName }}会员
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="right"
|
||||
v-if="memberEquityList.length>0">
|
||||
尊享以下{{ memberEquityList.length }}项特权
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- card 特权-->
|
||||
<view
|
||||
class="card"
|
||||
v-if="memberEquityList.length>0">
|
||||
<view class="card__inner">
|
||||
<view class="title">
|
||||
会员特权
|
||||
</view>
|
||||
<view class="grid-box">
|
||||
<view
|
||||
class="grid-item flex flex-ai__center flex-jc__center flex-column"
|
||||
v-for="equity in memberEquityList"
|
||||
:key="equity.id">
|
||||
<image :src="equity.iconUrl" />
|
||||
<view class="name">
|
||||
{{ equity.typeName }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- card 成长任务-->
|
||||
<view
|
||||
class="card"
|
||||
v-if="memberTaskList.length>0">
|
||||
<view class="card__inner">
|
||||
<view class="title">
|
||||
成长任务
|
||||
</view>
|
||||
<view class="list-box">
|
||||
<view
|
||||
class="list-item flex flex-ai__center"
|
||||
v-for="task in memberTaskList"
|
||||
:key="task.id">
|
||||
<image :src="task.iconUrl" />
|
||||
<view class="info flex flex-jc__sb flex-ai__center">
|
||||
<view class="left">
|
||||
<view class="name">{{ task.typeName }}</view>
|
||||
<view class="tips">每次{{ task.typeName }}可获{{ task.growthValue }}成长值</view>
|
||||
</view>
|
||||
<view
|
||||
class="button animation-button"
|
||||
@click="handleTaskButtonClick(task.type)">
|
||||
去{{ taskButtonLabel[task.type] }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 邀请好友 -->
|
||||
<InviteFriends
|
||||
ref="inviteFriendsRef"
|
||||
@share="handleShareConfirm" />
|
||||
<InviteFriendsPoster ref="inviteFriendsPosterRef" />
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<style
|
||||
scoped
|
||||
lang="scss">
|
||||
.member {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
@include usePadding(0, 24);
|
||||
|
||||
.swiper {
|
||||
width: 100%;
|
||||
height: 338rpx;
|
||||
|
||||
.swiper-item {
|
||||
padding: 92rpx 40rpx 20rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
margin: 0 10rpx;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
transition: all .3s;
|
||||
|
||||
.name {
|
||||
position: relative;
|
||||
font-size: 48rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.level {
|
||||
font-size: 24rpx;
|
||||
margin-top: 17rpx;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
font-size: 20rpx;
|
||||
position: absolute;
|
||||
bottom: 20rpx;
|
||||
width: calc(100% - 80rpx);
|
||||
|
||||
.left {
|
||||
flex-grow: 1;
|
||||
padding-right: 30rpx;
|
||||
|
||||
.process {
|
||||
width: 100%;
|
||||
height: 10rpx;
|
||||
border-radius: 10rpx;
|
||||
background: #664B301A;
|
||||
position: relative;
|
||||
|
||||
.schedule {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin-top: 12rpx;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.bg {
|
||||
width: 100%;
|
||||
height: 386rpx;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: linear-gradient(to bottom, #402E0A, #F6F6F6);
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 100%;
|
||||
margin: 24rpx 0;
|
||||
background: none;
|
||||
@include usePadding(34, 0);
|
||||
|
||||
.card__inner {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
@include usePadding(32, 32);
|
||||
border-radius: 15rpx;
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.grid-box {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
row-gap: 20rpx;
|
||||
@include usePadding(0, 16);
|
||||
|
||||
.grid-item {
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: #fbfbfb;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
}
|
||||
|
||||
.name {
|
||||
|
||||
color: #2B2B2B;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-box {
|
||||
.list-item {
|
||||
margin: 28rpx 0;
|
||||
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: #fbfbfb;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.info {
|
||||
flex-grow: 1;
|
||||
|
||||
.name {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: $tips-color;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.button {
|
||||
@include usePadding(32, 14);
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
Reference in New Issue
Block a user