商品编辑增加套餐商品,称重商品,定时上下架,是否允许临时改价,限购等

This commit is contained in:
2024-11-29 13:29:10 +08:00
parent 0f2563af2d
commit 23546c7b27
6 changed files with 1293 additions and 301 deletions

View File

@@ -1,34 +1,30 @@
<template>
<view class="mask u-fixed position-all u-flex u-col-bottom" v-if="show" @tap="close">
<view class="bg-fff w-full" @tap.stop="nullFunction">
<view class="mask u-fixed position-all u-flex u-col-bottom u-font-28" v-if="show" @tap="close">
<view class="bg-fff w-full " @tap.stop="nullFunction">
<view class="u-p-30">
<view class="font-bold u-text-center">选择商品</view>
<view class="u-m-t-32 u-flex">
<view class="u-flex-1 u-p-r-16">
<view class="u-m-b-12">商品名称</view>
<uni-easyinput v-model="goods.query.name" placeholder="请输入商品名称" />
</view>
<view class="u-flex-1 u-p-l-16">
<view class="u-m-b-12">商品分类</view>
<view class=" ">
<uni-data-picker :clear-icon="false" :map="{text:'name',value:'id'}" placeholder="请选择分类"
popup-title="请选择分类" :localdata="category" v-model="goods.query.categoryId">
<view class="u-flex u-font-28" >
<text class=" u-line-1"
style="max-width: 100rpx;">{{goods.query.categoryId||'分类' }}</text>
<up-icon name="arrow-down" size="16"></up-icon>
</view>
</uni-data-picker>
</view>
<view class="u-flex-1 u-p-l-16">
<up-search @custom="getGoods" v-model="goods.query.name" placeholder="请输入商品名称" @search="getGoods" @clear="getGoods"></up-search>
</view>
</view>
<view class="u-m-t-32 u-flex u-row-right">
<view class="u-flex-1 u-p-r-16">
<my-button type="cancel" plain @tap="resetQuery">重置</my-button>
</view>
<view class="u-flex-1 u-p-l-16">
<my-button @tap="getGoods">查询</my-button>
</view>
</view>
</view>
<scroll-view :scroll-x="false" scroll-y="true" :style="computedStyle()">
<view class="u-p-l-30 u-p-r-30">
<view class="u-flex u-row-between no-wrap">
<view class="u-p-l-30 u-p-r-30 table">
<view class="u-flex u-row-between no-wrap title">
<view>
<my-radio @change="radioAllChange" v-model="goods.allChecked" shape="square"
:size="20"></my-radio>
@@ -41,22 +37,26 @@
<view>销量/库存</view>
<view>分类名称</view>
</view>
<view class="u-m-t-12 u-flex u-row-between" v-for="(item,index) in goods.list" :key="index">
<my-radio @change="radioChange" v-model="item.checked" shape="square" :size="20"></my-radio>
<view class="u-flex u-flex-col u-row-center u-col-center">
<image lazy-load class="coverImg" :src="item.coverImg" mode=""></image>
<view class="u-m-t-10">{{item.name}}</view>
<view @click="changeChecked(item)" class="u-m-t-12 u-flex u-p-24 u-row-between row" v-for="(item,index) in goods.list" :key="index">
<view class="">
<my-radio @change="radioChange($event,item)" v-model="item.checked" shape="square" :size="20"></my-radio>
</view>
<view>
<view class="u-text-left u-flex-1 u-p-l-20">
<!-- <view class="u-flex">
<image lazy-load class="coverImg" :src="item.coverImg" mode=""></image>
</view> -->
<view class="">{{item.name}}</view>
</view>
<view class="u-flex-1">
{{item.typeEnum}}
</view>
<view>
<view class="u-flex-1">
{{ item.lowPrice }}
</view>
<view>
<view class="u-flex-1">
{{ item.realSalesNumber }}/{{ item.stockNumber }}
</view>
<view>
<view class="u-flex-1">
{{item.categoryName}}
</view>
</view>
@@ -83,7 +83,7 @@
import myRadio from '@/components/my-components/my-radio';
import myPagination from '@/components/my-components/my-pagination'
import {
$tbProductV2
$tbProductList
} from '@/http/yskApi/goods.js';
import {
reactive,
@@ -98,7 +98,7 @@
},
height: {
type: [Number, String],
default: '40vh'
default: '50vh'
},
category: {
type: Array,
@@ -107,26 +107,39 @@
}
}
})
function changeChecked(item){
item.checked=!item.checked
if(!item.checked&&$selGoodsMap[item.id]){
delete $selGoodsMap[item.id]
}else{
$selGoodsMap[item.id]=item
}
goods.allChecked = goods.list.filter(v => v.checked).length != 0
}
function nullFunction() {
}
const show = ref(props.modelValue)
function open(arr) {
let selArr=[]
let $selGoodsMap={}
async function open(arr) {
show.value = true
if (arr) {
for (let i in goods.list) {
console.log(arr.includes(goods.list[i].id));
goods.list[i].checked = arr.includes(goods.list[i].id)
}
selArr=arr
console.log(arr);
for(let i in arr){
$selGoodsMap[arr[i].proId
]=arr[i]
}
getGoods()
}
function close() {
show.value = false
resetQuery()
$selGoodsMap={}
}
@@ -162,38 +175,58 @@
...$quey
}
})
getGoods()
function getGoods() {
$tbProductV2(goods.query).then(res => {
const arr=selArr
$tbProductList(goods.query).then(res => {
let selLen=0;
goods.list = res.content.map(v => {
const checked=$selGoodsMap[v.id]?true:false
selLen+=(checked?1:0)
return {
...v,
checked: false
checked
}
})
goods.allChecked = false
goods.allChecked = selLen==res.content.length?true:false
goods.totalElements = res.totalElements
})
}
getGoods()
function pageChange(page) {
goods.query.page = page - 1
getGoods()
}
function radioChange(newval) {
function radioChange(newval,item) {
if(!newval&&$selGoodsMap[item.id]){
delete $selGoodsMap[item.id]
}else{
$selGoodsMap[item.id]=item
}
goods.allChecked = goods.list.filter(v => v.checked).length != 0
}
function radioAllChange(newval) {
goods.list.forEach(i => {
i.checked = newval
if($selGoodsMap[i.id]&&!newval){
delete $selGoodsMap[i.id]
}else{
$selGoodsMap[i.id]=i
}
})
}
function confrim() {
const arr = goods.list.filter(v => v.checked)
for(let i in goods.list){
const item=goods.list[i]
if($selGoodsMap[item.id]&&!item.checked){
delete $selGoodsMap[item.id]
}
}
console.log($selGoodsMap);
const arr = Object.values($selGoodsMap)
emits('confirm', arr)
}
defineExpose({
@@ -203,6 +236,9 @@
</script>
<style lang="scss" scoped>
.bg-fff{
border-radius: 24rpx 24rpx 0 0 ;
}
.mask {
background: rgba(51, 51, 51, 0.5);
z-index: 900;
@@ -213,4 +249,20 @@
height: 60rpx;
border-radius: 10rpx;
}
.table {
background: #F9F9F9;
border-radius: 8rpx;
overflow: hidden;
.title {
padding: 12rpx 24rpx 12rpx 24rpx;
background: #AEBAD2;
border-radius: 8rpx 8rpx 0rpx 0rpx;
color: #fff;
}
.row:nth-of-type(2n+1) {
background: #F0F0F0;
}
}
</style>

View File

@@ -0,0 +1,164 @@
<template>
<view class="mask u-fixed position-all u-flex u-col-bottom u-font-28" v-if="show" @tap="close">
<view class="bg-fff w-full " @tap.stop="nullFunction">
<view class="u-p-30">
<view class="font-bold u-text-center">选择规格</view>
</view>
<scroll-view :scroll-x="false" scroll-y="true" :style="computedStyle()">
<view class="u-p-l-30 u-p-r-30 ">
<view class="u-flex u-flex-wrap w-full gap-10 u-col-top">
<view class="skd" v-for="(item,index) in skuList" :key="index"
:class="{active:sel.id==item.id}"
@click="guigeClick(item,index)">
<text>{{item.specSnap||item.name}}</text>
<view class="tag-primary tag" v-if="item.isGrounding">上架中</view>
<view class="tag-gray tag" v-if="item.isPauseSale">已售罄</view>
<view class="tag-gray tag" v-if="!item.isGrounding">已下架</view>
</view>
</view>
</view>
</scroll-view>
<view class="u-p-30">
<view class="u-m-t-20 u-flex">
<view class="u-flex-1 u-p-r-16">
<my-button type="cancel" plain @tap="close">取消</my-button>
</view>
<view class="u-flex-1 u-p-l-16">
<my-button @tap="confrim">确定</my-button>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import {
$tbProduct
} from '@/http/yskApi/goods.js';
import {
reactive,
onMounted,
ref,
watch
} from 'vue';
import infoBox from '@/commons/utils/infoBox.js'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
}
})
let skuList=ref([])
function nullFunction() {
}
const show = ref(props.modelValue)
async function open(arr,sel) {
show.value = true
skuList.value=arr
}
function close() {
show.value = false
sel.value={}
}
let sel=ref({
})
function guigeClick(item,index){
if(sel.value.id==item.id){
return sel.value={}
}
sel.value=item
}
function computedStyle() {
return `height:${typeof props.height==='string'?props.height:props.height+'rpx'};`
}
const emits = defineEmits(['update:modelValue', 'confirm'])
function confrim() {
if(!sel.value.id){
return infoBox.showToast('请选择选择一个规格')
}
emits('confirm', sel.value)
close()
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.bg-fff{
border-radius: 24rpx 24rpx 0 0 ;
}
.mask {
background: rgba(51, 51, 51, 0.5);
z-index: 900;
}
.skd {
padding: 10rpx 38rpx 8rpx 40rpx;
background: #F0F2F5;
border-radius: 4rpx;
position: relative;
color: #666;
overflow: hidden;
margin-bottom: 10rpx;
font-size: 24rpx;
border: 1px solid transparent;
&.active{
border-color: $my-main-color;
}
.tag {
position: absolute;
right: 0;
top: 0;
font-size: 12rpx;
height: 18rpx;
line-height: 18rpx;
right: 0;
border-radius: 0rpx 2rpx 2rpx 8rpx;
}
.tag-primary {
background-color: $my-main-color;
color: #fff;
}
.tag-gray {
background-color: rgb(144, 147, 153);
color: #fff;
}
}
.coverImg {
width: 60rpx;
height: 60rpx;
border-radius: 10rpx;
}
.table {
background: #F9F9F9;
border-radius: 8rpx;
overflow: hidden;
.title {
padding: 12rpx 24rpx 12rpx 24rpx;
background: #AEBAD2;
border-radius: 8rpx 8rpx 0rpx 0rpx;
color: #fff;
}
.row:nth-of-type(2n+1) {
background: #F0F0F0;
}
}
</style>

View File

@@ -0,0 +1,105 @@
<template>
<view class="u-flex number-box">
<view class="u-flex u-flex-1">
<up-input @blur="priceFormat" border="none" v-model="number" type="number" :placeholder="placeholder"></up-input>
</view>
<view class="u-flex u-flex-col right">
<view class="u-flex-1 u-p-l-8 u-p-r-8" @click="changeNumber('add')">
<up-icon name="arrow-up" color="#E5E5E5"></up-icon>
</view>
<view class="line"></view>
<view class="u-flex-1 u-p-l-8 u-p-r-8" @click="changeNumber('reduce')">
<up-icon name="arrow-down" color="#E5E5E5"></up-icon>
</view>
</view>
</view>
</template>
<script setup>
import {
formatPrice
} from "@/commons/utils/format.js";
import {
ref,
watch,nextTick
} from 'vue';
const props = defineProps({
modelValue: {
type: [String, Number]
},
min: {
type: Number,
default: 0
},
max: {
type: Number,
default: 999999999
},
step: {
type: Number,
default: 1
},
placeholder: {
type: String,
default: '请输入'
}
})
let number = ref(props.modelValue)
function changeNumber(type) {
const newval = number.value + props.step * (type == 'add' ? 1 : -1)
if (newval < props.min) {
number.value = props.min
}
if (newval > props.max) {
number.value = props.max
}
priceFormat(newval)
}
function priceFormat(e) {
nextTick(() => {
const min = props
.min;
const max = props.max;
if (e === '') {
return
}
const newval = formatPrice(e, min, max, true)
if (typeof newval !== 'number') {
number.value = newval.value
uni.showToast({
title: `请输入${min}${max}范围内的数字`,
icon: 'none'
})
} else {
number.value = newval
}
})
}
watch(() => props.modelValue, (newval) => {
number.value = newval
})
const emits = defineEmits(['update:modelValue'])
watch(() => number.value, (newval) => {
emits('update:modelValue', newval)
})
</script>
<style lang="scss" scoped>
.line {
width: 100%;
height: 1px;
background-color: #E5E5E5;
}
.number-box {
border: 1px solid #E5E5E5;
border-radius: 8rpx 8rpx 8rpx 8rpx;
padding-left: 24rpx;
.right {
border-left: 1px solid #E5E5E5;
}
}
</style>