feat(积分)

This commit is contained in:
Ankkaya 2024-08-30 17:06:53 +08:00
parent 860b5221a9
commit 593887ae2e
13 changed files with 759 additions and 19 deletions

View File

@ -1,3 +1,3 @@
{ {
"prompt": "template" "prompt" : "template"
} }

View File

@ -22,7 +22,8 @@
/* */ /* */
"modules" : { "modules" : {
"Barcode" : {}, "Barcode" : {},
"Camera" : {} "Camera" : {},
"Payment" : {}
}, },
/* */ /* */
"distribute" : { "distribute" : {
@ -52,7 +53,14 @@
}, },
/* SDK */ /* SDK */
"sdkConfigs" : { "sdkConfigs" : {
"ad" : {} "ad" : {},
"payment" : {
"weixin" : {
"__platform__" : [ "android" ],
"appid" : "",
"UniversalLinks" : ""
}
}
}, },
"icons" : { "icons" : {
"android" : { "android" : {

View File

@ -126,6 +126,15 @@
"auth": true "auth": true
} }
}, },
{
"path": "wallet/withdrawLog",
"style": {
"navigationBarTitleText": "提现记录"
},
"meta": {
"auth": true
}
},
{ {
"path": "point/buy", "path": "point/buy",
"style": { "style": {
@ -153,6 +162,15 @@
"meta": { "meta": {
"auth": true "auth": true
} }
},
{
"path": "point/result",
"style": {
"navigationBarTitleText": "支付结果"
},
"meta": {
"auth": true
}
} }
] ]
}, },
@ -162,7 +180,8 @@
{ {
"path": "manageGoods", "path": "manageGoods",
"style": { "style": {
"navigationBarTitleText": "商品管理" "navigationBarTitleText": "商品管理",
"enablePullDownRefresh": true
}, },
"meta": { "meta": {
"auth": false "auth": false

View File

@ -76,8 +76,8 @@ const state = ref({
code: '', code: '',
}, },
rules: { rules: {
mobile,
code, code,
mobile,
}, },
inputStyle: { inputStyle: {
backgroundColor: '#ECECEC', backgroundColor: '#ECECEC',
@ -102,7 +102,7 @@ async function smsLoginSubmit() {
setTimeout(() => { setTimeout(() => {
state.value.isShaking = false state.value.isShaking = false
}, 1000) }, 1000)
peach.$helper.toast('请勾选同意') peach.$helper.toast('请勾选同意商家入驻协议')
return return
} }
@ -117,15 +117,12 @@ async function smsLoginSubmit() {
border-radius: 41rpx; border-radius: 41rpx;
padding: 0 10rpx; padding: 0 10rpx;
} }
.is-focused { .is-focused {
::v-deep .content-clear-icon {
.uniui-clear { .uniui-clear {
color: red !important; color: red !important;
} }
} }
} }
}
} }
</style> </style>

View File

@ -9,7 +9,21 @@
color="black" color="black"
:leftIcon="''" :leftIcon="''"
> >
<view v-if="state.pagination.total > 0" class="goods-list ss-m-t-20"> <pb-sticky>
<view class="top-search">
<uni-search-bar
v-model="state.searchContent"
radius="5"
placeholder="请输入商品名称"
bgColor="#fff"
clearButton="auto"
cancelButton="none"
@clear="onSearch"
@confirm="onSearch"
/>
</view>
</pb-sticky>
<view v-if="state.pagination.total > 0" class="goods-list">
<view class="ss-p-l-20 ss-p-r-20 ss-m-b-20" v-for="item in state.pagination.list" :key="item.id"> <view class="ss-p-l-20 ss-p-r-20 ss-m-b-20" v-for="item in state.pagination.list" :key="item.id">
<p-goods-column <p-goods-column
size="lg" size="lg"
@ -57,6 +71,7 @@ const bgStyle = {
} }
const state = ref({ const state = ref({
searchContent: '',
pagination: { pagination: {
list: [], list: [],
total: 0, total: 0,
@ -81,6 +96,7 @@ async function getList() {
let { data } = await GoodApi.getProductList({ let { data } = await GoodApi.getProductList({
pageNo: state.value.pagination.pageNo, pageNo: state.value.pagination.pageNo,
pageSize: state.value.pagination.pageSize, pageSize: state.value.pagination.pageSize,
name: state.value.searchContent,
}) })
state.value.pagination.list = _.concat(state.value.pagination.list, data.list) state.value.pagination.list = _.concat(state.value.pagination.list, data.list)
@ -127,6 +143,7 @@ onReachBottom(() => {
// //
onPullDownRefresh(() => { onPullDownRefresh(() => {
resetPagination(state.value.pagination) resetPagination(state.value.pagination)
state.value.searchContent = ''
getList() getList()
setTimeout(function () { setTimeout(function () {
uni.stopPullDownRefresh() uni.stopPullDownRefresh()
@ -136,6 +153,9 @@ onPullDownRefresh(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.product-list { .product-list {
.top-search {
background-color: var(--ui-BG-1);
}
.add-product { .add-product {
position: fixed; position: fixed;
color: var(--ui-BG-Main); color: var(--ui-BG-Main);

View File

@ -100,13 +100,35 @@ function chooseItemClick(index) {
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确认购买该积分额度?', content: '确认购买该积分额度?',
success: function (res) { success: async function (res) {
if (res.confirm) { if (res.confirm) {
let data = await PointApi.buyIntegral({
point: state.value.pointList[index].point,
price: state.value.pointList[index].price * 100,
})
uni.requestPayment({
provider: 'wxpay',
orderInfo: data.data,
success: (res) => {
payResult('success')
},
fail: (err) => {
err.errMsg !== 'requestPayment:fail cancel' && payResult('fail')
},
})
} }
}, },
}) })
} }
// ,success:fail:
function payResult(resultType) {
// peach.$router.redirect('/pages/pay/result', {
// payState: resultType,
// })
}
onShow(() => { onShow(() => {
getPointConfig() getPointConfig()
}) })
@ -174,6 +196,7 @@ onShow(() => {
border-radius: 28rpx; border-radius: 28rpx;
font-size: 32rpx; font-size: 32rpx;
font-weight: 500; font-weight: 500;
margin-top: 20px;
} }
.draw-btn-raw { .draw-btn-raw {

226
pages/user/point/result.vue Normal file
View File

@ -0,0 +1,226 @@
<!-- 支付结果页面 -->
<template>
<pb-layout title="支付结果" :bgStyle="{ color: '#FFF' }">
<view class="pay-result-box ss-flex-col ss-row-center ss-col-center">
<!-- 信息展示 -->
<view class="pay-waiting ss-m-b-30" v-if="payResult === 'waiting'" />
<image
class="pay-img ss-m-b-30"
v-if="payResult === 'success'"
:src="sheep.$url.static('/static/img/shop/order/order_pay_success.gif')"
/>
<image
class="pay-img ss-m-b-30"
v-if="['failed', 'closed'].includes(payResult)"
:src="sheep.$url.static('/static/img/shop/order/order_paty_fail.gif')"
/>
<view class="tip-text ss-m-b-30" v-if="payResult === 'success'">支付成功</view>
<view class="tip-text ss-m-b-30" v-if="payResult === 'failed'">支付失败</view>
<view class="tip-text ss-m-b-30" v-if="payResult === 'closed'">该订单已关闭</view>
<view class="tip-text ss-m-b-30" v-if="payResult === 'waiting'">检测支付结果...</view>
<view class="pay-total-num ss-flex" v-if="payResult === 'success'">
<view>{{ fen2yuan(state.orderInfo.price) }}</view>
</view>
<!-- 操作区 -->
<view class="btn-box ss-flex ss-row-center ss-m-t-50">
<button class="back-btn ss-reset-button" @tap="peach.$router.go('/pages/index/index')">返回首页</button>
<button
class="check-btn ss-reset-button"
v-if="payResult === 'failed'"
@tap="sheep.$router.redirect('/pages/user/point/buy')"
>
重新购买
</button>
</view>
</view>
</pb-layout>
</template>
<script setup>
import { onLoad, onHide, onShow } from '@dcloudio/uni-app'
import { reactive, computed } from 'vue'
import { isEmpty } from 'lodash'
import peach from '@/peach'
import PayOrderApi from '@/peach/api/pay/point'
import { fen2yuan } from '@/peach/hooks/useGoods'
const state = reactive({
id: 0, //
result: 'unpaid', //
orderInfo: {}, //
counter: 0, //
})
// result => payResult
const payResult = computed(() => {
if (state.result === 'unpaid') {
return 'waiting'
}
if (state.result === 'paid') {
return 'success'
}
if (state.result === 'failed') {
return 'failed'
}
if (state.result === 'closed') {
return 'closed'
}
})
//
async function getOrderInfo(id) {
state.counter++
// 1.
const { data, code } = await PayOrderApi.getOrder(id)
if (code === 0) {
state.orderInfo = data
if (!state.orderInfo || state.orderInfo.status === 30) {
//
state.result = 'closed'
return
}
if (state.orderInfo.status !== 0) {
// 退
state.result = 'paid'
return
}
}
// 2.1
if (state.counter < 3 && state.result === 'unpaid') {
setTimeout(() => {
getOrderInfo(id)
}, 1500)
}
// 2.2
if (state.counter >= 3) {
state.result = 'failed'
}
}
onLoad(async (options) => {
//
if (options.id) {
state.id = options.id
}
//
if (options.payState === 'fail') {
state.result = 'failed'
} else {
//
await getOrderInfo(state.id)
}
})
onShow(() => {
if (isEmpty(state.orderInfo)) {
return
}
getOrderInfo(state.id)
})
onHide(() => {
state.result = 'unpaid'
state.counter = 0
})
</script>
<style lang="scss" scoped>
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.score-img {
width: 36rpx;
height: 36rpx;
margin: 0 4rpx;
}
.pay-result-box {
padding: 60rpx 0;
.pay-waiting {
margin-top: 20rpx;
width: 60rpx;
height: 60rpx;
border: 10rpx solid rgb(233, 231, 231);
border-bottom-color: rgb(204, 204, 204);
border-radius: 50%;
display: inline-block;
// -webkit-animation: rotation 1s linear infinite;
animation: rotation 1s linear infinite;
}
.pay-img {
width: 130rpx;
height: 130rpx;
}
.tip-text {
font-size: 30rpx;
font-weight: bold;
color: #333333;
}
.pay-total-num {
font-size: 36rpx;
font-weight: 500;
color: #333333;
font-family: OPPOSANS;
}
.btn-box {
width: 100%;
.back-btn {
width: 190rpx;
height: 70rpx;
font-size: 28rpx;
border: 2rpx solid #dfdfdf;
border-radius: 35rpx;
font-weight: 400;
color: #595959;
}
.check-btn {
width: 190rpx;
height: 70rpx;
font-size: 28rpx;
border: 2rpx solid #dfdfdf;
border-radius: 35rpx;
font-weight: 400;
color: #595959;
margin-left: 32rpx;
}
}
.subscribe-box {
.subscribe-img {
width: 44rpx;
height: 44rpx;
}
.subscribe-title {
font-weight: 500;
font-size: 32rpx;
line-height: 36rpx;
color: #434343;
}
.subscribe-start {
color: var(--ui-BG-Main);
font-weight: 700;
font-size: 32rpx;
line-height: 36rpx;
}
}
}
</style>

View File

@ -59,7 +59,7 @@
</view> </view>
</view> </view>
<text class="time"> <text class="time">
{{ peach.$helper.timeFormat(state.createTime, 'yyyy-mm-dd hh:MM:ss') }} {{ peach.$helper.timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss') }}
</text> </text>
</view> </view>
</view> </view>
@ -159,7 +159,7 @@ async function getLogList() {
// //
async function getStatistic() { async function getStatistic() {
let res = await PayWalletApi.statistics({ let { data, code } = await PayWalletApi.statistics({
createTime: [state.date[0] + ' 00:00:00', state.date[1] + ' 23:59:59'], createTime: [state.date[0] + ' 00:00:00', state.date[1] + ' 23:59:59'],
}) })

View File

@ -9,6 +9,7 @@
<view class="note ss-m-t-10">极速到账</view> <view class="note ss-m-t-10">极速到账</view>
</view> </view>
</view> </view>
<button class="ss-reset-button draw-btn-log" @tap="navLogTap">提现记录</button>
<!-- <view class="cicon-angle m-t-40"></view> --> <!-- <view class="cicon-angle m-t-40"></view> -->
</view> </view>
<view class="detail ss-m-40 ss-p-30"> <view class="detail ss-m-40 ss-p-30">
@ -26,7 +27,7 @@
<view class="all self-end" @click="allWithdraw">全部提现</view> <view class="all self-end" @click="allWithdraw">全部提现</view>
</view> </view>
<view class="note ss-flex ss-row-between"> <view class="note ss-flex ss-row-between">
<view class="last"> 最低提现金额{{ config?.takingRule }} </view> <view class="last"> 最低提现金额{{ fen2yuan(config?.takingRule || 0) }} </view>
<view class="can-use"> 余额{{ fen2yuan(remain) }} </view> <view class="can-use"> 余额{{ fen2yuan(remain) }} </view>
</view> </view>
<view class="last"> <view class="last">
@ -64,6 +65,10 @@ const formData = ref({
amount: 0, amount: 0,
}) })
function navLogTap() {
peach.$router.go('/pages/user/wallet/withdrawLog')
}
async function getWithdrawConfig() { async function getWithdrawConfig() {
let res = await WalletApi.withdrawConfig() let res = await WalletApi.withdrawConfig()
config.value = res.data config.value = res.data
@ -111,6 +116,12 @@ onLoad(() => {
} }
} }
} }
.draw-btn-log {
border-radius: 40rpx;
background: var(--ui-BG-Main);
width: 100px;
color: $white;
}
} }
.detail { .detail {

View File

@ -0,0 +1,416 @@
<!-- 我的钱包 -->
<template>
<pb-layout class="wallet-wrap" title="提现记录">
<pb-sticky>
<!-- 统计 -->
<view class="filter-box ss-p-x-30 ss-flex ss-col-center ss-row-between">
<uni-datetime-picker v-model="state.data" type="daterange" @change="onChangeTime" :end="state.today">
<button class="ss-reset-button date-btn">
<text>{{ dateFilterText }}</text>
<text class="cicon-drop-down ss-seldate-icon"></text>
</button>
</uni-datetime-picker>
</view>
<pb-tabs :list="tabMaps" @change="onChange" :scrollable="false" :current="state.currentTab"></pb-tabs>
</pb-sticky>
<p-empty v-if="state.pagination.total === 0" text="暂无数据" icon="/static/data-empty.png" bgColor="transparent" />
<!-- 钱包记录 -->
<view v-if="state.pagination.total > 0" style="padding: 20rpx 0">
<view class="log-list border-bottom" v-for="item in state.pagination.list" :key="item.id">
<!-- <view class="list-content">
<view class="title-box ss-flex ss-row-between ss-m-b-20">
<text class="title ss-line-1">
{{ item.title }}
</text>
<view class="money">
<text v-if="item.price >= 0" class="add">+{{ fen2yuan(item.price) }}</text>
<text v-else class="minus">{{ fen2yuan(item.price) }}</text>
</view>
</view>
<text class="time">
{{ sheep.$helper.timeFormat(state.createTime, 'yyyy-mm-dd hh:MM:ss') }}
</text>
</view> -->
<view class="head ss-flex ss-col-center ss-row-between">
<view class="title">提现金额</view>
<view class="num"> {{ fen2yuan(item.price) }} </view>
</view>
<view style="height: 1px; background: #eee; width: calc(100% - 20px); margin: 5px 10px"></view>
<view class="head ss-flex ss-col-center ss-row-between">
<view class="title">实际到账</view>
<view class="num"> {{ fen2yuan(item.realityPrice) }} </view>
</view>
<view style="height: 1px; background: #eee; width: 100%; margin-bottom: 10px"></view>
<view class="status-box item ss-flex ss-col-center ss-row-between">
<view class="item-title">审核状态</view>
<view
class="status-text"
:class="item.status === 10 ? 'warning-color' : item.status === 20 ? 'success-color' : 'danger-color'"
>
{{ item.status === 10 ? '待审核' : item.status === 20 ? '已通过' : '已驳回' }}
</view>
</view>
<view class="time-box item ss-flex ss-col-center ss-row-between">
<text class="item-title">手续费</text>
<view class="time"> {{ fen2yuan(item.fee) }} </view>
</view>
<view class="time-box item ss-flex ss-col-center ss-row-between">
<text class="item-title">提现时间</text>
<view class="time"> {{ sheep.$helper.timeFormat(item.payTime, 'yyyy-mm-dd hh:MM:ss') }}</view>
</view>
</view>
</view>
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}"
/>
</pb-layout>
</template>
<script setup>
import { computed, reactive } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import sheep from '@/peach'
import dayjs from 'dayjs'
import _ from 'lodash'
import PayWalletApi from '@/peach/api/pay/wallet'
import { fen2yuan } from '@/peach/hooks/useGoods'
import { resetPagination } from '@/peach/utils'
const headerBg = sheep.$url.css('/static/img/shop/user/wallet_card_bg.png')
//
const state = reactive({
showMoney: false,
date: [], //
currentTab: 0,
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 8,
},
summary: {
totalIncome: 0,
totalExpense: 0,
},
loadStatus: '',
today: '',
})
const tabMaps = [
{
name: '全部',
value: '',
},
{
name: '待审核',
value: '10',
},
{
name: '已通过',
value: '20',
},
{
name: '已驳回',
value: '30',
},
]
//
const dateFilterText = computed(() => {
if (state.date[0] === state.date[1]) {
return state.date[0]
} else {
return state.date.join('~')
}
})
//
async function getLogList() {
state.loadStatus = 'loading'
const { data, code } = await PayWalletApi.withdrawList({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
status: tabMaps[state.currentTab].value,
'createTime[0]': state.date[0] + ' 00:00:00',
'createTime[1]': state.date[1] + ' 23:59:59',
})
if (code !== 0) {
return
}
state.pagination.list = _.concat(state.pagination.list, data.list)
state.pagination.total = data.total
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore'
}
onLoad(() => {
state.today = dayjs().format('YYYY-MM-DD')
state.date = [state.today, state.today]
getLogList()
})
// tab
function onChange(e) {
state.currentTab = e.index
//
resetPagination(state.pagination)
getLogList()
}
//
function onChangeTime(e) {
state.date[0] = e[0]
state.date[1] = e[e.length - 1]
//
resetPagination(state.pagination)
getLogList()
}
onReachBottom(() => {
if (state.loadStatus === 'noMore') {
return
}
state.pagination.pageNo++
getLogList()
})
</script>
<style lang="scss" scoped>
//
.header-box {
background-color: $white;
padding: 30rpx;
.card-box {
width: 100%;
min-height: 300rpx;
padding: 40rpx;
background-size: 100% 100%;
border-radius: 30rpx;
overflow: hidden;
position: relative;
z-index: 1;
box-sizing: border-box;
&::after {
content: '';
display: block;
width: 100%;
height: 100%;
z-index: 2;
position: absolute;
top: 0;
left: 0;
background: v-bind(headerBg) no-repeat;
pointer-events: none;
}
.card-head {
color: $white;
font-size: 30rpx;
}
.ss-eye-icon {
font-size: 40rpx;
color: $white;
}
.money-num {
font-size: 70rpx;
line-height: 70rpx;
font-weight: 500;
color: $white;
font-family: OPPOSANS;
}
.reduce-num {
font-size: 26rpx;
font-weight: 400;
color: $white;
}
.topup-btn {
width: 120rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 30px;
font-size: 26rpx;
font-weight: 500;
background-color: $white;
color: var(--ui-BG-Main);
}
}
}
//
.filter-box {
height: 114rpx;
background-color: $bg-page;
.total-box {
font-size: 24rpx;
font-weight: 500;
color: $dark-9;
}
.date-btn {
background-color: $white;
width: 150px;
line-height: 54rpx;
border-radius: 27rpx;
padding: 0 20rpx;
font-size: 24rpx;
font-weight: 500;
color: $dark-6;
.ss-seldate-icon {
font-size: 50rpx;
color: $dark-9;
}
}
}
.tabs-box {
background: $white;
border-bottom: 2rpx solid #eeeeee;
}
// tab
.wallet-tab-card {
.tab-item {
height: 80rpx;
position: relative;
.tab-title {
font-size: 30rpx;
}
.cur-tab-title {
font-weight: $font-weight-bold;
}
.tab-line {
width: 60rpx;
height: 6rpx;
border-radius: 6rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 2rpx;
background-color: var(--ui-BG-Main);
}
}
}
//
.wallet-list {
padding: 30rpx;
background-color: #ffff;
margin-bottom: 10rpx;
padding-bottom: 10rpx;
.head-img {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
background: $gray-c;
}
.list-content {
justify-content: space-between;
align-items: flex-start;
flex: 1;
.title {
font-size: 28rpx;
color: $dark-3;
width: 400rpx;
}
.time {
color: $gray-c;
font-size: 22rpx;
}
}
.money {
font-size: 28rpx;
font-weight: bold;
font-family: OPPOSANS;
.add {
color: var(--ui-BG-Main);
}
.minus {
color: $dark-3;
}
}
}
.log-list {
min-height: 213rpx;
background: $white;
margin-bottom: 20rpx;
padding-bottom: 10rpx;
.head {
padding: 0 35rpx;
height: 60rpx;
.title {
font-size: 28rpx;
font-weight: 500;
color: $dark-3;
}
.num {
font-size: 28rpx;
font-weight: 500;
}
}
.item {
padding: 0 30rpx 20rpx;
.item-icon {
color: $gray-d;
font-size: 36rpx;
margin-right: 8rpx;
}
.item-title {
width: 180rpx;
font-size: 24rpx;
font-weight: 400;
color: #666666;
}
.status-text {
font-size: 24rpx;
font-weight: 500;
}
.time {
font-size: 24rpx;
font-weight: 400;
color: #c0c0c0;
}
}
.warning-color {
color: #faad14;
}
.danger-color {
color: #ff4d4f;
}
.success-color {
color: #67c23a;
}
}
</style>

View File

@ -27,6 +27,15 @@ const PointApi = {
method: 'get', method: 'get',
}) })
}, },
// 购买积分
buyIntegral: (data) => {
return request({
url: '/particulars/point/point-recharge',
method: 'post',
data,
})
},
} }
export default PointApi export default PointApi

View File

@ -41,6 +41,17 @@ const PayWalletApi = {
method: 'get', method: 'get',
}) })
}, },
// 提现记录
withdrawList: (params) => {
const queryString = Object.keys(params)
.map((key) => encodeURIComponent(key) + '=' + params[key])
.join('&')
return request({
url: `/pay/withdraw/page?${queryString}`,
method: 'get',
})
},
} }
export default PayWalletApi export default PayWalletApi

View File

@ -119,7 +119,7 @@ http.interceptors.response.use(
// 自定义处理【error 错误提示】:如果需要显示错误提示,则显示错误提示 // 自定义处理【error 错误提示】:如果需要显示错误提示,则显示错误提示
if (response.data.code !== 0) { if (response.data.code !== 0) {
// 特殊:如果 401 错误码,则跳转到登录页 or 刷新令牌 // 特殊:如果 401 错误码,则跳转到登录页 or 刷新令牌
if (response.data.code === 401) { if (response.data.code === 401 || response.data.code === 1012001016) {
return refreshToken(response.config) return refreshToken(response.config)
} }