feat(商品): 规格修改

feat(我的): 提现,钱包记录
This commit is contained in:
Ankkaya 2024-08-29 18:06:05 +08:00
parent b577f0730c
commit 860b5221a9
13 changed files with 839 additions and 611 deletions

View File

@ -20,13 +20,14 @@
<view class="remain flex justify-between align-center">
<view class="left flex align-center">
<view class="unit self-start"></view>
<view class="sremain ss-font-60">{{ remain }}</view>
<view class="sremain ss-font-60">{{ fen2yuan(remain) }}</view>
</view>
<button @click="peach.$router.go('/pages/user/wallet/money')" class="right-btn ss-reset-button">
查看详情
</button>
</view>
<view class="cent ss-m-t-20"> 总获取积分 {{ userInfo.point }} </view>
<view class="cent ss-m-t-20"> 冻结金额 {{ fen2yuan(freezePrice || 0) }} </view>
</view>
<view class="menu ss-m-t-70">
@ -47,6 +48,7 @@
<script setup>
import { ref, computed } from 'vue'
import { fen2yuan } from '@/peach/hooks/useGoods'
import { onShow } from '@dcloudio/uni-app'
import AuthUtil from '@/peach/api/member/auth'
import peach from '@/peach'
@ -88,6 +90,10 @@ const remain = computed(() => {
return userStore.userWallet?.balance
})
const freezePrice = computed(() => {
return userStore.userWallet?.freezePrice
})
const userInfo = computed(() => {
return userStore.userInfo
})
@ -137,7 +143,7 @@ onShow(() => {
}
.statistic {
height: 200rpx;
height: 230rpx;
background-color: #fffefe;
opacity: 0.9;
border-radius: 26rpx;

View File

@ -1,89 +1,89 @@
<template>
<div class="sku-item">
<uni-forms label-width="176rpx" label-position="left">
<template v-if="specType">
<template v-for="item in formData.properties">
<uni-forms-item :label="item.propertyName">
<uni-easyinput type="text" :value="item.valueName" disabled />
</uni-forms-item>
</template>
</template>
<div class="sku-item">
<uni-forms label-width="176rpx" label-position="left">
<template v-if="specType">
<template v-for="item in formData.properties">
<uni-forms-item :label="item.propertyName">
<uni-easyinput type="text" :value="item.valueName" disabled />
</uni-forms-item>
</template>
</template>
<uni-forms-item label="商品封面图" name="picUrl" label-position="top">
<p-uploader
v-model:url="formData.picUrl"
:readonly="!canEdit"
fileMediatype="image"
limit="1"
mode="grid"
:imageStyles="{ width: '168rpx', height: '168rpx' }"
/>
</uni-forms-item>
<uni-forms-item label="商品条码" name="barCode">
<uni-easyinput
type="text"
trim="all"
v-model="formData.barCode"
:disabled="!canEdit"
placeholder="请输入商品条码"
/>
</uni-forms-item>
<uni-forms-item label="销售价" name="price">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.price"
:disabled="!canEdit"
placeholder="请输入商品销售价"
/>
</uni-forms-item>
<uni-forms-item label="市场价" name="marketPrice">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.marketPrice"
:disabled="!canEdit"
placeholder="请输入商品销售价"
/>
</uni-forms-item>
<uni-forms-item label="成本价" name="costPrice">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.costPrice"
:disabled="!canEdit"
placeholder="请输入商品销售价"
/>
</uni-forms-item>
<uni-forms-item label="库存" name="stock">
<uni-easyinput
type="number"
trim="all"
v-model="formData.stock"
:disabled="!canEdit"
placeholder="请输入商品库存"
/>
</uni-forms-item>
<uni-forms-item label="重量kg" name="weight">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.weight"
:disabled="!canEdit"
placeholder="请输入商品重量"
/>
</uni-forms-item>
<uni-forms-item label="体积" name="volume">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.volume"
:disabled="!canEdit"
placeholder="请输入商品体积"
/>
</uni-forms-item>
</uni-forms>
</div>
<uni-forms-item label="商品封面图" name="picUrl" label-position="top">
<p-uploader
v-model:url="formData.picUrl"
:readonly="!canEdit"
fileMediatype="image"
limit="1"
mode="grid"
:imageStyles="{ width: '168rpx', height: '168rpx' }"
/>
</uni-forms-item>
<uni-forms-item label="商品条码" name="barCode">
<uni-easyinput
type="text"
trim="all"
v-model="formData.barCode"
:disabled="!canEdit"
placeholder="请输入商品条码"
/>
</uni-forms-item>
<uni-forms-item label="销售价" name="price">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.price"
:disabled="!canEdit"
placeholder="请输入商品销售价"
/>
</uni-forms-item>
<uni-forms-item label="市场价" name="marketPrice">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.marketPrice"
:disabled="!canEdit"
placeholder="请输入商品销售价"
/>
</uni-forms-item>
<uni-forms-item label="成本价" name="costPrice">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.costPrice"
:disabled="!canEdit"
placeholder="请输入商品销售价"
/>
</uni-forms-item>
<uni-forms-item label="库存" name="stock">
<uni-easyinput
type="number"
trim="all"
v-model="formData.stock"
:disabled="!canEdit"
placeholder="请输入商品库存"
/>
</uni-forms-item>
<uni-forms-item label="重量kg" name="weight">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.weight"
:disabled="!canEdit"
placeholder="请输入商品重量"
/>
</uni-forms-item>
<uni-forms-item label="体积" name="volume">
<uni-easyinput
type="digit"
trim="all"
v-model="formData.volume"
:disabled="!canEdit"
placeholder="请输入商品体积"
/>
</uni-forms-item>
</uni-forms>
</div>
</template>
<script setup>
@ -91,36 +91,35 @@ import { ref, watch, computed, defineProps } from 'vue'
import peach from '@/peach'
import { canEdit } from '../js/sku'
const props = defineProps({
skus: {
type: Array,
default: () => [],
},
skus: {
type: Array,
default: () => [],
},
})
const formData = ref({})
const specType = computed(() => peach.$store('trade').specType)
watch(
() => props.skus,
(newVal) => {
console.log(newVal)
// sku
if (!specType.value) {
if (newVal) formData.value = newVal[0] ?? {}
return
}
formData.value = newVal ?? {}
},
{ immediate: true }
() => props.skus,
(newVal) => {
// sku
if (!specType.value) {
if (newVal) formData.value = newVal[0] ?? {}
return
}
formData.value = newVal ?? {}
},
{ immediate: true }
)
</script>
<style lang="scss" scoped>
.sku-item {
margin: 40rpx;
padding-top: 40rpx;
margin: 40rpx;
padding-top: 40rpx;
}
.sku-item:first-child {
border-top: 1px solid var(--ui-BG-Main);
border-top: 1px solid var(--ui-BG-Main);
}
</style>

View File

@ -6,9 +6,9 @@
<view class="property-name-text">规格名</view>
<view class="property-name-value">{{ item.name }}</view>
</view>
<view class="property-value ss-flex ss-gap-40">
<view class="property-value ss-flex ss-gap-40" style="flex-wrap: wrap">
<view class="property-value-text">规格值</view>
<view class="property-value-value ss-flex ss-gap-10">
<view v-if="item.propertyValues.length > 0" class="property-value-value ss-flex ss-gap-10">
<view
v-for="sitem in item.propertyValues"
@tap="chooseProperty(sitem)"

View File

@ -16,7 +16,11 @@
>
{{ item.name }}
</view>
<view v-if="canEdit" style="height: 60rpx" @click="addPropertyClick">
<view
v-if="canEdit"
style="height: 60rpx; position: relative; top: 5px; display: inline-block"
@click="addPropertyClick"
>
<text style="color: #ff3300; font-size: 30px" class="cicon-add-round-o"></text>
</view>
</view>
@ -127,6 +131,7 @@ const inputFormdata = ref({
function addPropertyClick() {
propertyTitle.value = '新增'
inputFormdata.value.name = ''
inputFormdata.value.id = ''
inputDialogRef.value.open()
}
@ -160,12 +165,11 @@ defineExpose({
.popup-content {
height: 500rpx;
padding: 40rpx;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
justify-content: flex-start;
.property-item {
display: inline-block;
margin-right: 10px;
margin-bottom: 10px;
text-align: center;
line-height: 60rpx;
height: 60rpx;

View File

@ -1,34 +1,34 @@
export const SPEC_TYPE = [
{
label: '单规格',
value: false,
},
{
label: '多规格',
value: true,
},
{
label: '单规格',
value: false,
},
{
label: '多规格',
value: true,
},
]
// sku 相关属性校验
export const SKU_RULE_CONFIG = [
{
name: 'stock',
rule: (arg) => arg >= 0,
message: '商品库存必须大于等于 1 ',
},
{
name: 'price',
rule: (arg) => arg >= 0.01,
message: '商品销售价格必须大于等于 0.01 元!!!',
},
{
name: 'marketPrice',
rule: (arg) => arg >= 0.01,
message: '商品市场价格必须大于等于 0.01 元!!!',
},
{
name: 'costPrice',
rule: (arg) => arg >= 0.01,
message: '商品成本价格必须大于等于 0.00 元!!!',
},
{
name: 'stock',
rule: (arg) => arg >= 0,
message: '商品库存必须大于等于 1 ',
},
{
name: 'price',
rule: (arg) => arg >= 0.01,
message: '商品销售价格必须大于等于 0.01 元!!!',
},
{
name: 'marketPrice',
rule: (arg) => arg >= 0.01,
message: '商品市场价格必须大于等于 0.01 元!!!',
},
{
name: 'costPrice',
rule: (arg) => arg >= 0.01,
message: '商品成本价格必须大于等于 0.01 元!!!',
},
]

View File

@ -29,11 +29,16 @@ async function showPropertyList() {
}
function onRDPickerConfirm(e) {
formData.value.specType = SPEC_TYPE[e.value[0]].value
formData.value.specText = SPEC_TYPE[e.value[0]].label
if (formData.value.specType !== SPEC_TYPE[e.value[0]].value) {
peach.$store('trade').specType = SPEC_TYPE[e.value[0]].value
peach.$store('trade').selectedProperty = []
// 如果商品规格不一致,则需要重新初始化 sku 列表
initSku()
formData.value.specType = SPEC_TYPE[e.value[0]].value
formData.value.specText = SPEC_TYPE[e.value[0]].label
// 如果商品规格不一致,则需要重新初始化 sku 列表
initSku()
}
}
function pickerProperty() {
@ -46,6 +51,7 @@ function pickerProperty() {
async function onPropertyConfirm(e) {
await getGoodsProperty()
changeSubProperty()
}
function onConfirm() {
@ -59,30 +65,32 @@ async function getGoodsProperty() {
// 把 propertyList 中 id 相同的属性合并,并去重
propertyList.value = peach.$store('trade').selectedProperty || []
let tempSkus = peach.$store('trade').skus || []
// 根据已经选择数据,设置默认选中
data.forEach((item) => {
// 判断属性是否已经选中
let propertyParent = propertyList.value.find((sitem) => sitem?.id === item.id)
item.checked = propertyParent ? true : false
// 如果属性已经选中,查询子类中是否有选中
if (item.checked) {
item.propertyValues.forEach((child) => {
let childResult = propertyParent?.children.some((schild) => schild === child.id)
child.checked = childResult ? true : false
tempSkus.forEach((sitem) => {
sitem.properties.forEach((titem) => {
item.propertyValues.forEach((child) => {
if (!child.checked) {
child.checked = titem.valueId === child.id ? true : false
}
})
})
}
})
})
goodsPropertyList.value = data
console.log(goodsPropertyList.value)
}
function changeSubProperty() {
// 修改子属性状态,需要同步更新 skus 的显示
console.log(goodsPropertyList.value)
// 过滤父属性 checked 选项,深拷贝避免后面循环改变元数据内容
let temp = JSON.parse(JSON.stringify(goodsPropertyList.value.filter((item) => item.checked)))
temp.forEach((item) => {
@ -115,7 +123,32 @@ function changeSubProperty() {
tempSkus.push(obj)
}
tempSkus.forEach((item) => {
skus.value.forEach((sitem) => {
if (sitem.properties.length === item.properties.length && item.properties.length > 0) {
let sitemIds = sitem.properties.map((child) => child.valueId)
let itemIds = item.properties.map((tchild) => tchild.valueId)
let isExist = sitemIds.every((schild) => itemIds.includes(schild))
if (isExist) {
item.picUrl = sitem.picUrl
item.barCode = sitem.barCode
item.price = sitem.price
item.marketPrice = sitem.marketPrice
item.costPrice = sitem.costPrice
item.stock = sitem.stock
item.weight = sitem.weight
item.volume = sitem.volume
}
}
})
})
skus.value = tempSkus
peach.$store('trade').$patch({
skus: skus.value,
})
}
/**
@ -168,6 +201,7 @@ function submitProperty() {
peach.$store('trade').$patch({
skus: skus.value,
specType: formData.value.specType,
isSave: true,
})
peach.$router.back()
} catch (e) {
@ -278,10 +312,10 @@ const inputPropertyValueFormdata = ref({
remark: '',
})
function addPropertyValueClick(item) {
console.log(item)
propertyValueTitle.value = '新增'
inputPropertyValueFormdata.value.name = ''
inputPropertyValueFormdata.value.propertyId = item.id
inputPropertyValueFormdata.value.id = ''
inputPropertyValueDialogRef.value.open()
}
@ -296,11 +330,12 @@ async function dialogInputPropertyValueConfirm(value) {
await GoodsApi.createPropertyValue(inputPropertyValueFormdata.value)
}
await getGoodsProperty()
changeSubProperty()
inputPropertyValueDialogRef.value.close()
}
function longPropertyValuePress(item) {
console.log(item)
if (canEdit.value) {
// 震动提示
uni.vibrateShort()
@ -317,6 +352,7 @@ function longPropertyValuePress(item) {
if (res.tapIndex === 1) {
await GoodsApi.delPropertyValue({ id: item.id })
await getGoodsProperty()
changeSubProperty()
}
},
})

View File

@ -153,7 +153,7 @@
<script setup>
import { ref, computed } from 'vue'
import { onLoad, onPageScroll } from '@dcloudio/uni-app'
import { onLoad, onPageScroll, onShow } from '@dcloudio/uni-app'
import _ from 'lodash'
import GoodsApi from '@/peach/api/trade/goods'
import piaoyiEditor from '@/uni_modules/piaoyi-editor/components/piaoyi-editor/piaoyi-editor.vue'
@ -408,9 +408,6 @@ function saveContens(e) {
}
function onSubmit() {
console.log('res', formData.value)
console.log('richtext', richValues.value)
formData.value.skus = peach.$store('trade').skus
formRef.value
@ -486,6 +483,21 @@ onLoad(async (options) => {
}
})
onShow(() => {
let isSave = peach.$store('trade').isSave
if (isSave) {
formData.value.skus = peach.$store('trade').skus
formData.value.specType = peach.$store('trade').specType
peach.$store('trade').isSave = false
} else {
peach.$store('trade').$patch({
skus: formData.value.skus || [],
specType: formData.value.specType,
})
}
})
onPageScroll((e) => {
if (canEdit.value && e.scrollTop > 200) {
richReadOnly.value = false

View File

@ -1,62 +1,77 @@
<template>
<pb-layout
navbar="inner"
class="wallet-wrap"
iconColor="#fff"
leftIcon="leftIcon"
color="#fff"
title="余额"
:bgStyle="bgStyle"
>
<view class="header-box ss-row-center ss-col-center">
<view class="card-box ss-flex ss-row-between ss-col-center">
<view class="ss-flex ss-flex-col ss-row-between ss-col-top ss-gap-40">
<view class="card-head ss-flex ss-col-center">
<view class="card-title ss-m-r-10">账户余额</view>
<view
@tap="state.showMoney = !state.showMoney"
class="ss-eye-icon"
:class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'"
/>
</view>
<pb-layout
navbar="inner"
class="wallet-wrap"
iconColor="#fff"
leftIcon="leftIcon"
color="#fff"
title="余额"
:bgStyle="bgStyle"
>
<view class="header-box ss-row-center ss-col-center">
<view class="card-box ss-flex ss-row-between ss-col-center">
<view class="ss-flex ss-flex-col ss-row-between ss-col-top ss-gap-40">
<view class="card-head ss-flex ss-col-center">
<view class="card-title ss-m-r-10">账户余额</view>
<view
@tap="state.showMoney = !state.showMoney"
class="ss-eye-icon"
:class="state.showMoney ? 'cicon-eye' : 'cicon-eye-off'"
/>
</view>
<view class="money-num">{{ state.showMoney ? fen2yuan(userWallet.balance) : '*****' }}</view>
</view>
<button class="ss-reset-button topup-btn" @tap="peach.$router.go('/pages/user/wallet/withdraw')">
提现
</button>
</view>
<view class="money-num">{{ state.showMoney ? fen2yuan(userWallet.balance) : '*****' }}</view>
</view>
<button class="ss-reset-button topup-btn" @tap="peach.$router.go('/pages/user/wallet/withdraw')">提现</button>
</view>
</view>
<p-empty v-if="state.pagination.total === 0" :marginTop="40" text="暂无数据" icon="/static/data-empty.png" />
<!-- 钱包记录 -->
<view v-if="state.pagination.total > 0">
<view class="wallet-list ss-flex 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">
{{ peach.$helper.timeFormat(state.createTime, 'yyyy-mm-dd hh:MM:ss') }}
</text>
</view>
</view>
<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 class="total-box">
<view class="ss-m-b-10">总收入{{ fen2yuan(state.summary.totalIncome) }}</view>
<view>总支出{{ fen2yuan(state.summary.totalExpense) }}</view>
</view>
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}"
/>
</pb-layout>
</view>
<pb-tabs :list="tabMaps" @change="onChange" :scrollable="false" :current="state.currentTab"></pb-tabs>
</pb-sticky>
<p-empty v-if="state.pagination.total === 0" :marginTop="40" text="暂无数据" icon="/static/data-empty.png" />
<!-- 钱包记录 -->
<view v-if="state.pagination.total > 0">
<view class="wallet-list ss-flex 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">
{{ peach.$helper.timeFormat(state.createTime, 'yyyy-mm-dd hh:MM:ss') }}
</text>
</view>
</view>
</view>
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}"
/>
</pb-layout>
</template>
<script setup>
@ -70,203 +85,264 @@ import PayWalletApi from '@/peach/api/pay/wallet'
import { resetPagination } from '@/peach/utils'
const bgStyle = {
backgroundImage: '/static/bg-page.png',
imageType: 'local',
backgroundColor: '#fff',
description: '',
backgroundImage: '/static/bg-page.png',
imageType: 'local',
backgroundColor: '#fff',
description: '',
}
// const headerBg = peach.$url.css('/static/img/shop/user/wallet_card_bg.png')
//
const state = reactive({
showMoney: false,
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 8,
},
loadStatus: '',
showMoney: false,
currentTab: 0,
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 8,
},
loadStatus: '',
summary: {
totalIncome: 0,
totalExpense: 0,
},
today: '',
})
const tabMaps = [
{
name: '全部',
value: '',
},
{
name: '收入',
value: '1',
},
{
name: '支出',
value: '2',
},
]
//
const dateFilterText = computed(() => {
if (state.date[0] === state.date[1]) {
return state.date[0]
} else {
return state.date.join('~')
}
})
const userWallet = computed(() => peach.$store('user').userWallet)
//
async function getLogList() {
state.loadStatus = 'loading'
// const { data, code } = await PayWalletApi.getWalletTransactionPage({
// pageNo: state.pagination.pageNo,
// pageSize: state.pagination.pageSize,
state.loadStatus = 'loading'
const { data, code } = await PayWalletApi.flowList({
pageNo: state.pagination.pageNo,
pageSize: state.pagination.pageSize,
type: 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'
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'
}
//
async function getStatistic() {
let res = await PayWalletApi.statistics({
createTime: [state.date[0] + ' 00:00:00', state.date[1] + ' 23:59:59'],
})
if (code !== 0) {
return
}
state.summary = data
}
// tab
function onChange(e) {
state.currentTab = e.index
//
resetPagination(state.pagination)
getLogList()
getStatistic()
}
onLoad(() => {
getLogList()
//
// peach.$store('user').getWallet()
state.today = dayjs().format('YYYY-MM-DD')
state.date = [state.today, state.today]
getLogList()
getStatistic()
//
// peach.$store('user').getWallet()
})
onReachBottom(() => {
if (state.loadStatus === 'noMore') {
return
}
state.pagination.pageNo++
getLogList()
if (state.loadStatus === 'noMore') {
return
}
state.pagination.pageNo++
getLogList()
})
</script>
<style lang="scss" scoped>
.header-box {
.card-box {
width: 100%;
min-height: 250rpx;
padding: 0 40rpx;
background-size: 100% 100%;
border-radius: 30rpx;
overflow: hidden;
position: relative;
z-index: 1;
box-sizing: border-box;
.card-box {
width: 100%;
min-height: 250rpx;
padding: 0 40rpx;
background-size: 100% 100%;
border-radius: 30rpx;
overflow: hidden;
position: relative;
z-index: 1;
box-sizing: border-box;
.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: 150rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 30px;
font-size: 26rpx;
font-weight: 500;
background-color: $white;
color: var(--ui-BG-Main);
}
.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: 150rpx;
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;
height: 114rpx;
background-color: $bg-page;
.total-box {
font-size: 24rpx;
font-weight: 500;
color: $dark-9;
}
.date-btn {
background-color: $white;
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;
}
.total-box {
font-size: 24rpx;
font-weight: 500;
color: $dark-9;
}
.date-btn {
width: fit-content;
background-color: $white;
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;
}
}
}
// tab
.wallet-tab-card {
.tab-item {
height: 80rpx;
position: relative;
.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);
}
.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;
padding: 30rpx;
background-color: #ffff;
.head-img {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
background: $gray-c;
.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;
}
.list-content {
justify-content: space-between;
align-items: flex-start;
flex: 1;
.time {
color: $gray-c;
font-size: 22rpx;
}
}
.title {
font-size: 28rpx;
color: $dark-3;
width: 400rpx;
}
.money {
font-size: 28rpx;
font-weight: bold;
font-family: OPPOSANS;
.time {
color: $gray-c;
font-size: 22rpx;
}
.add {
color: var(--ui-BG-Main);
}
.money {
font-size: 28rpx;
font-weight: bold;
font-family: OPPOSANS;
.add {
color: var(--ui-BG-Main);
}
.minus {
color: $dark-3;
}
.minus {
color: $dark-3;
}
}
}
</style>

View File

@ -1,53 +1,97 @@
<template>
<pb-layout navbar="normal" class="withdraw-wrap" leftIcon="leftIcon" title="提现" :bgStyle="bgStyle">
<view class="alert ss-m-x-40 ss-m-y-20">
提现至
</view>
<view class="alert ss-m-x-40 ss-m-y-20"> 提现至 </view>
<view class="method ss-m-x-40 ss-flex ss-col-center ss-row-between">
<view class="left ss-flex ss-gap-20 ss-col-center">
<view class="cicon-weixin" style="color:#2f9326;font-size: 60rpx"></view>
<view class="cicon-weixin" style="color: #2f9326; font-size: 60rpx"></view>
<view class="info">
<view class="label">其选择提现方式</view>
<view class="note ss-m-t-10">立即到账</view>
<view class="label">微信</view>
<view class="note ss-m-t-10">极速到账</view>
</view>
</view>
<view class="cicon-angle m-t-40"></view>
<!-- <view class="cicon-angle m-t-40"></view> -->
</view>
<view class="detail ss-m-40 ss-p-30">
<view class="label ss-m-b-50">提现金额</view>
<view class="account ss-m-t-135 ss-m-b-20 ss-flex ss-row-between">
<uni-easyinput :styles="{ backgroundColor: 'transparent' }" :clearable="false" :inputBorder="false"
type="number" trim="all" placeholder="请输入提现金额" />
<view class="all self-end">全部提现</view>
<view class="account ss-m-t-15 ss-m-b-20 ss-flex ss-row-between">
<uni-easyinput
v-model="formData.amount"
:styles="{ backgroundColor: 'transparent' }"
:clearable="false"
:inputBorder="false"
type="digit"
trim="all"
placeholder="请输入提现金额"
/>
<view class="all self-end" @click="allWithdraw">全部提现</view>
</view>
<view class="note ss-flex ss-row-between">
<view class="last">
剩余额度{{ state.last }}
</view>
<view class="can-use">
可用额度{{ state.canUse }}
</view>
<view class="last"> 最低提现金额{{ config?.takingRule }} </view>
<view class="can-use"> 余额{{ fen2yuan(remain) }} </view>
</view>
<view class="last">
提现费率{{ config?.takingRate }} 即每提现 1000 元扣除手续费{{ 1000 * config?.takingRate }}
</view>
</view>
<view class="footer-box">
<button class="ss-reset-button draw-btn ui-Shadow-Main" @tap="onSubmit">提现</button>
</view>
</pb-layout>
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { fen2yuan } from '@/peach/hooks/useGoods'
import peach from '@/peach'
import WalletApi from '@/peach/api/pay/wallet'
const bgStyle = {
backgroundColor: '#fff',
description: '',
}
const state = ref({
last: 1000,
canUse: 10000
const config = ref(null)
const userStore = peach.$store('user')
const remain = computed(() => {
return userStore.userWallet?.balance
})
const formData = ref({
amount: 0,
})
async function getWithdrawConfig() {
let res = await WalletApi.withdrawConfig()
config.value = res.data
}
function allWithdraw() {
formData.value.amount = fen2yuan(remain.value)
}
async function onSubmit() {
if (!formData.value.amount || formData.value.amount <= 0) {
peach.$helper.toast('请输入正确的金额')
return
}
try {
await WalletApi.withdraw({
amount: formData.value.amount * 100,
})
setTimeout(() => {
peach.$router.back()
}, 1000)
} catch (e) {
console.log(e)
}
}
onLoad(() => {
getWithdrawConfig()
})
</script>
@ -63,14 +107,14 @@ const state = ref({
font-size: 26rpx;
.note {
color: #b5b5b5
color: #b5b5b5;
}
}
}
}
.detail {
background-color: rgba(236, 236, 236, .3);
background-color: rgba(236, 236, 236, 0.3);
border-radius: 20rpx;
.label {
@ -79,7 +123,6 @@ const state = ref({
}
.account {
:deep(.uni-easyinput__content-input) {
font-size: 28px;
}
@ -95,7 +138,7 @@ const state = ref({
.account::before {
content: '¥';
font-size: 48rpx;
color: #333
color: #333;
}
.note {
@ -103,7 +146,16 @@ const state = ref({
font-size: 26rpx;
}
.last {
color: #a3a3a3;
font-size: 26rpx;
margin-top: 5px;
}
.can-use {
color: #333;
font-size: 26rpx;
}
}
.footer-box {

View File

@ -1,7 +1,46 @@
import request from "@/peach/request";
import request from '@/peach/request'
const PayWalletApi = {
getWalletTransactionPage: () => {},
};
// 提现余额
withdraw: (data) => {
return request({
url: '/pay/wallet/withdraw',
method: 'post',
data,
custom: {
showSuccess: true,
successMsg: '提现成功',
},
})
},
export default PayWalletApi;
// 提现配置信息
withdrawConfig: () => {
return request({
url: '/shop/particulars-config/get',
method: 'get',
})
},
// 钱包流水记录
flowList: (params) => {
const queryString = Object.keys(params)
.map((key) => encodeURIComponent(key) + '=' + params[key])
.join('&')
return request({
url: `/pay/wallet-transaction/page?${queryString}`,
method: 'get',
})
},
// 钱包统计
statistics: (params) => {
const queryString = `createTime=${params.createTime[0]}&createTime=${params.createTime[1]}`
return request({
url: `/pay/wallet-transaction/get-summary?${queryString}`,
method: 'get',
})
},
}
export default PayWalletApi

View File

@ -107,7 +107,7 @@ const GoodsApi = {
// 创建商品属性值
createPropertyValue: (data) => {
return request({
url: '/property/value/create',
url: '/product/property/value/create',
method: 'POST',
data,
custom: {
@ -118,7 +118,7 @@ const GoodsApi = {
// 删除商品属性值
delPropertyValue: (data) => {
return request({
url: '/property/value/delete',
url: '/product/property/value/delete',
method: 'DELETE',
params: data,
custom: {
@ -129,7 +129,7 @@ const GoodsApi = {
// 修改商品属性值
editPropertyValue: (data) => {
return request({
url: '/property/value/update',
url: '/product/property/value/update',
method: 'PUT',
data,
custom: {

View File

@ -1,58 +1,58 @@
<template>
<view class="ss-goods-wrap">
<view v-if="size === 'lg'" class="lg-goods-card ss-flex ss-col-stretch" :style="[elStyles]" @click="onClick">
<image class="lg-img-box" :src="peach.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
<view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
<view>
<view
v-if="goodsFields.title?.show || goodsFields.name?.show"
class="lg-goods-title ss-line-2"
:style="[{ color: titleColor }]"
>
{{ data.title || data.name }}
</view>
<view
v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
class="lg-goods-subtitle ss-m-t-10 ss-line-1"
:style="[{ color: subTitleColor, background: subTitleBackground }]"
>
{{ data.subtitle || data.introduction }}
</view>
</view>
<view>
<view class="ss-flex ss-col-bottom ss-m-t-10">
<view
v-if="goodsFields.price?.show"
class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
:style="[{ color: goodsFields.price.color }]"
>
<text class="ss-font-24">{{ priceUnit }}</text>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</view>
<view
v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0)
"
class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS"
:style="[{ color: originPriceColor }]"
>
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
<view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
</view>
</view>
<view class="ss-m-t-8 ss-flex ss-col-center ss-flex-wrap">
<view class="sales-text">{{ salesAndStock }}</view>
</view>
</view>
<view class="ss-goods-wrap">
<view v-if="size === 'lg'" class="lg-goods-card ss-flex ss-col-stretch" :style="[elStyles]" @click="onClick">
<image class="lg-img-box" :src="peach.$url.cdn(data.image || data.picUrl)" mode="aspectFill"></image>
<view class="lg-goods-content ss-flex-1 ss-flex-col ss-row-between ss-p-b-10 ss-p-t-20">
<view>
<view
v-if="goodsFields.title?.show || goodsFields.name?.show"
class="lg-goods-title ss-line-2"
:style="[{ color: titleColor }]"
>
{{ data.title || data.name }}
</view>
<view
v-if="goodsFields.subtitle?.show || goodsFields.introduction?.show"
class="lg-goods-subtitle ss-m-t-10 ss-line-3"
:style="[{ color: subTitleColor, background: subTitleBackground }]"
>
{{ data.subtitle || data.introduction }}
</view>
</view>
<view>
<view class="ss-flex ss-col-bottom ss-m-t-10">
<view
v-if="goodsFields.price?.show"
class="lg-goods-price ss-m-r-12 ss-flex ss-col-bottom font-OPPOSANS"
:style="[{ color: goodsFields.price.color }]"
>
<text class="ss-font-24">{{ priceUnit }}</text>
{{ isArray(data.price) ? fen2yuan(data.price[0]) : fen2yuan(data.price) }}
</view>
<view
v-if="
(goodsFields.original_price?.show || goodsFields.marketPrice?.show) &&
(data.original_price > 0 || data.marketPrice > 0)
"
class="goods-origin-price ss-flex ss-col-bottom font-OPPOSANS"
:style="[{ color: originPriceColor }]"
>
<text class="price-unit ss-font-20">{{ priceUnit }}</text>
<view class="ss-m-l-8">{{ fen2yuan(data.marketPrice) }}</view>
</view>
</view>
<view class="ss-m-t-8 ss-flex ss-col-center ss-flex-wrap">
<view class="sales-text">{{ salesAndStock }}</view>
</view>
</view>
<view class="ss-flex ss-row-around" :style="btnStyles">
<button class="ss-reset-button btn-group" @click="clickGoods('detail')">详情</button>
<button class="ss-reset-button btn-group" @click="clickGoods('edit')">编辑</button>
<button class="ss-reset-button btn-group btn-del" @click="clickGoods('del')">删除</button>
</view>
</view>
</view>
<view class="ss-flex ss-row-around" :style="btnStyles">
<button class="ss-reset-button btn-group" @click="clickGoods('detail')">详情</button>
<button class="ss-reset-button btn-group" @click="clickGoods('edit')">编辑</button>
<button class="ss-reset-button btn-group btn-del" @click="clickGoods('del')">删除</button>
</view>
</view>
</template>
<script setup>
@ -64,185 +64,185 @@ import { fen2yuan, formatSales, formatStock } from '@/peach/hooks/useGoods'
import { unix } from 'dayjs'
const props = defineProps({
goodsFields: {
type: [Array, Object],
default() {
return {
price: { show: true },
stock: { show: true },
name: { show: true },
introduction: { show: true },
marketPrice: { show: true },
salesCount: { show: true },
}
},
},
data: {
type: Object,
default: {},
},
size: {
type: String,
default: '',
},
originPriceColor: {
type: String,
default: '#C4C4C4',
},
topRadius: {
type: Number,
default: 0,
},
bottomRadius: {
type: Number,
default: 0,
},
priceUnit: {
type: String,
default: '¥',
},
titleColor: {
type: String,
default: '#333',
},
subTitleColor: {
type: String,
default: '#999999',
},
subTitleBackground: {
type: String,
default: '',
goodsFields: {
type: [Array, Object],
default() {
return {
price: { show: true },
stock: { show: true },
name: { show: true },
introduction: { show: true },
marketPrice: { show: true },
salesCount: { show: true },
}
},
},
data: {
type: Object,
default: {},
},
size: {
type: String,
default: '',
},
originPriceColor: {
type: String,
default: '#C4C4C4',
},
topRadius: {
type: Number,
default: 0,
},
bottomRadius: {
type: Number,
default: 0,
},
priceUnit: {
type: String,
default: '¥',
},
titleColor: {
type: String,
default: '#333',
},
subTitleColor: {
type: String,
default: '#999999',
},
subTitleBackground: {
type: String,
default: '',
},
})
const emits = defineEmits(['click', 'refresh'])
function onClick() {
emits('click')
emits('click')
}
const elStyles = computed(() => {
return {
background: props.background,
'border-top-left-radius': props.topRadius + 'px',
'border-top-right-radius': props.topRadius + 'px',
}
return {
background: props.background,
'border-top-left-radius': props.topRadius + 'px',
'border-top-right-radius': props.topRadius + 'px',
}
})
const btnStyles = computed(() => {
return {
background: '#fff',
'border-bottom-left-radius': props.bottomRadius + 'px',
'border-bottom-right-radius': props.bottomRadius + 'px',
padding: '8px 0',
}
return {
background: '#fff',
'border-bottom-left-radius': props.bottomRadius + 'px',
'border-bottom-right-radius': props.bottomRadius + 'px',
padding: '8px 0',
}
})
//
const salesAndStock = computed(() => {
let text = []
if (props.goodsFields.salesCount?.show) {
text.push(formatSales(props.data.sales_show_type, props.data.salesCount))
}
if (props.goodsFields.stock?.show) {
text.push(formatStock(props.data.stock_show_type, props.data.stock))
}
return text.join(' | ')
let text = []
if (props.goodsFields.salesCount?.show) {
text.push(formatSales(props.data.sales_show_type, props.data.salesCount))
}
if (props.goodsFields.stock?.show) {
text.push(formatStock(props.data.stock_show_type, props.data.stock))
}
return text.join(' | ')
})
function clickGoods(mark) {
if (mark === 'detail' || mark === 'edit') {
peach.$store('trade').$patch({
selectedProperty: null,
goodsInfo: null,
skus: null,
specType: false,
})
peach.$router.go('/pages/product/manageGoods', {
id: props.data.id,
mark: mark,
title: mark === 'detail' ? '商品详情' : '编辑商品',
})
} else if (mark === 'del') {
uni.showModal({
title: '提示',
content: '是否删除该商品?',
success: async (res) => {
if (res.confirm) {
await GoodsApi.delProduct({ id: props.data.id })
if (mark === 'detail' || mark === 'edit') {
peach.$store('trade').$patch({
selectedProperty: null,
goodsInfo: null,
skus: null,
specType: false,
})
peach.$router.go('/pages/product/manageGoods', {
id: props.data.id,
mark: mark,
title: mark === 'detail' ? '商品详情' : '编辑商品',
})
} else if (mark === 'del') {
uni.showModal({
title: '提示',
content: '是否删除该商品?',
success: async (res) => {
if (res.confirm) {
await GoodsApi.delProduct({ id: props.data.id })
uni.showToast({
title: '删除成功',
icon: 'none',
})
uni.showToast({
title: '删除成功',
icon: 'none',
})
emits('refresh')
}
},
})
}
emits('refresh')
}
},
})
}
}
</script>
<style lang="scss" scoped>
.ss-goods-wrap {
.lg-goods-card {
overflow: hidden;
position: relative;
z-index: 1;
background-color: $white;
height: 280rpx;
.lg-goods-card {
overflow: hidden;
position: relative;
z-index: 1;
background-color: $white;
height: 280rpx;
.lg-img-box {
width: 280rpx;
height: 280rpx;
margin-right: 20rpx;
}
.lg-goods-title {
font-size: 28rpx;
font-weight: 500;
color: #333333;
// line-height: 36rpx;
// width: 410rpx;
}
.lg-goods-subtitle {
font-size: 24rpx;
font-weight: 400;
color: #999999;
// line-height: 30rpx;
// width: 410rpx;
}
.lg-goods-price {
font-size: 30rpx;
color: $red;
line-height: 36rpx;
}
.sales-text {
display: table;
font-size: 24rpx;
transform: scale(0.8);
margin-left: 0rpx;
color: #c4c4c4;
}
.lg-img-box {
width: 280rpx;
height: 280rpx;
margin-right: 20rpx;
}
.btn-group {
width: 140rpx;
height: 55rpx;
line-height: 55rpx;
background: var(--ui-BG-1);
border-radius: 25rpx;
font-size: 24rpx;
color: #000;
.lg-goods-title {
font-size: 28rpx;
font-weight: 500;
color: #333333;
// line-height: 36rpx;
// width: 410rpx;
}
.btn-del {
color: var(--ui-BG-Main);
background-color: var(--ui-BG-Main-opacity-1);
.lg-goods-subtitle {
font-size: 24rpx;
font-weight: 400;
color: #999999;
// line-height: 30rpx;
// width: 410rpx;
}
.lg-goods-price {
font-size: 30rpx;
color: $red;
line-height: 36rpx;
}
.sales-text {
display: table;
font-size: 24rpx;
transform: scale(0.8);
margin-left: 0rpx;
color: #c4c4c4;
}
}
.btn-group {
width: 140rpx;
height: 55rpx;
line-height: 55rpx;
background: var(--ui-BG-1);
border-radius: 25rpx;
font-size: 24rpx;
color: #000;
}
.btn-del {
color: var(--ui-BG-Main);
background-color: var(--ui-BG-Main-opacity-1);
}
}
</style>

View File

@ -2,32 +2,36 @@ import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
const useTradeStore = defineStore('trade', () => {
// 已选择规格类型
const selectedProperty = ref(null)
// 已选择规格类型
const selectedProperty = ref(null)
// 商品信息
const goodsInfo = ref(null)
// 商品信息
const goodsInfo = ref(null)
// 详情标记
const detailTag = ref('edit')
// 详情标记
const detailTag = ref('edit')
// 规格类型,默认单规格
const specType = ref(false)
// 是否触发保存操作
const isSave = ref(false)
// 商品属性
const skus = ref(null)
// 规格类型,默认单规格
const specType = ref(false)
// 商品是否可编辑
const canEdit = computed(() => (detailTag.value === 'detail' ? false : true))
// 商品属性
const skus = ref(null)
return {
selectedProperty,
goodsInfo,
skus,
canEdit,
detailTag,
specType,
}
// 商品是否可编辑
const canEdit = computed(() => (detailTag.value === 'detail' ? false : true))
return {
selectedProperty,
goodsInfo,
skus,
canEdit,
detailTag,
specType,
isSave,
}
})
export default useTradeStore