first commit

This commit is contained in:
wtq
2025-11-13 10:16:36 +08:00
commit cbdb6758a0
394 changed files with 57767 additions and 0 deletions

View File

@@ -0,0 +1,305 @@
.skuName-cell {
box-sizing: border-box;
background: white;
padding: 19rpx;
border-radius: 10rpx;
margin: 15rpx 15rpx;
box-shadow: 0rpx 2rpx 10rpx rgb(207, 207, 207);
.skuName {
display: flex;
align-items: flex-start;
position: relative;
.skuName-img {
position: relative;
view {
position: absolute;
z-index: 9;
left: 0;
top: 0;
width: 182rpx;
height: 182rpx;
border-radius: 10rpx;
background: rgba(0, 0, 0, .4);
text-align: center;
line-height: 182rpx;
color: #fff;
font-weight: bold;
}
.imglabel {
width: 180rpx;
height: 180rpx;
border-radius: 10rpx;
border: 1rpx solid rgb(218, 218, 218);
}
}
.skuName-info {
display: flex;
flex-direction: column;
justify-content: space-between;
margin-left: 17rpx;
width: 100%;
height: 180rpx;
.input-price {
position: absolute;
border: 1rpx solid rgba(0, 0, 0, 0.1);
background: white;
border-radius: 8rpx;
padding: 20rpx;
top: 60rpx;
color: #333;
}
.skuName-name {
color: #333;
font-size: 32rpx;
line-height: 1;
display: flex;
align-items: flex-start;
}
.skuName-monthsale {
font-size: 28rpx;
color: #999;
line-height: 1;
margin-top: 7rpx;
}
.skuName-failsync {
font-size: 28rpx;
color: #F60D58;
line-height: 1;
margin-top: 7rpx;
margin-bottom: -30rpx;
}
.skuName-price {
display: flex;
color: #F60D58;
line-height: 1;
.icon-modify {
margin-left: 15rpx;
}
}
.skuName-tips {
font-size: 28rpx;
line-height: 1;
color: #333;
}
.red {
color: #F60D58;
}
.green {
color: $jx-primary;
}
}
}
// sku
.skus {
border-top: 1rpx dashed #ccc;
display: flex;
flex-flow: row wrap;
// justify-content: flex-end;
// padding-top: 16rpx;
// overflow: hidden;
margin-top: 10rpx;
align-items: flex-start;
.sku-cell:nth-of-type(even) {
margin-left: 10rpx;
}
}
// 价格审核中样式
.check-display {
color: #f44;
font-size: 24rpx;
font-weight: bold;
animation: rubberBand 1s infinite alternate;
}
}
.skus-wrapper-new {
border-top: 1rpx solid rgb(219, 219, 219);
.error {
text-align: center;
font-size: 24rpx;
font-weight: bold;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.mt {
color: #f29a40;
}
.eb {
color: #51a7fc;
}
.sku-cell2 {
padding: 10rpx 0 0 0;
border-bottom: 1rpx solid rgb(219, 219, 219);
position: relative;
}
.isSale2 {}
.cell-top {
display: flex;
align-items: center;
.price {
color: #333;
width: 23%;
flex-shrink: 0;
text-align: center;
display: flex;
justify-content: center;
flex-flow: column;
.sku-spec {
font-size: 26rpx;
}
.sku-price {
font-size: 26rpx;
}
}
.promotion-price {
color: #F60D58;
}
.btn-group {
width: 78%;
flex-shrink: 0;
display: flex;
justify-content: space-around;
.btn {
box-sizing: border-box;
flex: 1;
height: 80rpx;
text-align: center;
font-size: 24rpx;
border-radius: 10rpx;
color: $jx-primary;
border: 2rpx solid $jx-primary;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
margin-right: 8rpx;
}
.tmpSaleNo {
box-sizing: border-box;
display: flex;
flex-direction: column;
font-size: 28rpx;
line-height: 1.02;
margin: 0;
}
.onActive {
background: $jx-primary;
color: white;
}
}
}
.cell-bottom {
font-size: 28rpx;
text-align: center;
color: #666;
padding: 5rpx 0;
}
.sku-autoSaleAt {
font-size: 26rpx;
text-align: center;
color: #F25340;
padding: 5rpx 0;
text {
font-weight: bold;
}
}
}
.checkBoxWrap {
display: flex;
width: 100%;
justify-content: flex-end;
padding-top: 15rpx;
}
.aduit-type {
font-size: 28rpx;
text-align: center;
color: $jx-primary;
}
.aduit-btn-group {
display: flex;
align-items: center;
justify-content: center;
padding-top: 20rpx;
.btn {
font-size: 28rpx;
padding: 20rpx 40rpx;
background: $jx-primary;
border-radius: 10rpx;
color: white;
margin: 0 20rpx;
}
.refuse {
background: #f44;
}
}
@keyframes rubberBand {
from {
transform: scale3d(1, 1, 1);
}
30% {
transform: scale3d(1.01, 0.75, 1);
}
40% {
transform: scale3d(0.95, 1.25, 1);
}
50% {
transform: scale3d(1.02, 0.85, 1);
}
65% {
transform: scale3d(0.98, 1.05, 1);
}
75% {
transform: scale3d(1.01, 0.95, 1);
}
to {
transform: scale3d(1, 1, 1);
}
}

View File

@@ -0,0 +1,177 @@
import shopping from "@/api/https/shopping"
import { store } from "@/store"
import { getStorage } from "@/utils/storage"
import toast from "@/utils/toast"
import { jx_throttles, timeFormatD } from "@/utils/tools"
import { computed } from "@vue/reactivity"
import configCms from "@/utils/configCms"
interface propType {
skuName: AnyObject
isAudit: boolean
isHot: boolean
showMountTab: boolean
}
/*************************************************
* 商品列表
*/
function goodsListFn(props: Readonly<propType>) {
/*************************************************
* 列表判断条件
*/
const isNewPriceDisplay: any = computed(() => {
store.getters['storeInfo/isNewPriceDisplay']
})
/*************************************************
* 获取userType
*/
const userType = computed(() => {
return +getStorage('userType')
})
/*************************************************
* 修改商品售卖状态
*/
function updateSaleStatus(sku: AnyObject, skuName: AnyObject, status: number) {
if (+sku.storeSkuStatus === +status && !sku.autoSaleAt) return false
let data = {
sku,
skuName,
status
}
updateSaleStatusThrottles(data)
}
const updateSaleStatusThrottles = jx_throttles({
time: 500,
success: async (e: AnyObject) => {
let data = {
storeIDs: JSON.stringify([getStorage('storeID')]),
payload: JSON.stringify([
{
nameID: e.skuName.id,
skus: [
{
skuID: e.sku.id,
isSale: +e.status === 0 ? -1 : +e.status,
},
],
},
]),
}
let res = await shopping.update_stores_skus(data)
if (res.code == 0) {
changeSaleStatus(e.sku, e.skuName, e.status)
if (e.sku.autoSaleAt) e.sku.autoSaleAt = 0
} else {
toast('修改失败', 2)
}
},
fail: (t: number) => {
toast(`操作太快`)
}
})
/*************************************************
* 修改商品临时不可售
*/
function tmpSaleNo(sku: AnyObject, skuName: AnyObject) {
if (sku.storeSkuStatus === 0 && sku.autoSaleAt) return false
let data = {
sku,
skuName,
}
tmpSaleNoThrottles(data)
}
const tmpSaleNoThrottles = jx_throttles({
time: 500,
success: async (e: AnyObject) => {
let autoSaleAt = computedAutoSaleAt()
let data = {
storeIDs: JSON.stringify([getStorage('storeID')]),
payload: JSON.stringify([{
skuID: e.sku.id,
isSale: -1,
isAsync: false,
}]),
autoSaleAt: autoSaleAt
}
let res = await shopping.update_stores_skus_sale(data)
// let res:AnyObject = {
// code: "-105",
// desc: "本地数据修改成功,但同步失败,请根据错误提示处理!,同步错误信息:[{\"商品ID\":0,\"分类名\":\"\",\"门店ID\":804947,\"平台名\":\"抖店平台\",\"平台商品ID\":\"\",\"商品nameID\":0,\"平台价\":0,\"同步类型\":\"异常同步错误\",\"错误信息\":\"门店:804947修改没有创建的商品:22807\"},{\"商品ID\":22807,\"分类名\":\"\",\"门店ID\":804947,\"平台名\":\"京东到家\",\"平台商品ID\":\"2792687352\",\"商品nameID\":8643,\"平台价\":0,\"同步类型\":\"更新商品状态\",\"错误信息\":\"未查询到到家商品编码\"}]",
// data: ""
// }
// if (res.code === '0') {
// changeSaleStatus(e.sku, e.skuName, 0)
// e.sku.autoSaleAt = autoSaleAt
// } else {
// if(res.code === '-105' && res.desc.includes('本地数据修改成功,但同步失败,请根据错误提示处理')) toast(res.desc)
// else toast('修改失败', 2)
// }
if (res.code == 0) {
changeSaleStatus(e.sku, e.skuName, 0)
e.sku.autoSaleAt = autoSaleAt
} else {
let findIndex = res.desc.indexOf('[')
let str = ''
if(findIndex!== -1){
JSON.parse(res.desc.substring(findIndex)).forEach((element:AnyObject) => {
str = str.length >0 ? str + '\n' + element['平台名'] + ':【' + element['错误信息'] + '】': str + element['平台名'] +':【' + element['错误信息']+'】'
});
}
if(res.code === '-105' && res.desc.includes('本地数据修改成功,但同步失败,请根据错误提示处理')) toast(`${str}`)
else toast('修改失败', 2)
}
},
fail: (t: number) => {
toast(`操作太快`)
}
})
/*************************************************
* 计算自动可售时间
*/
function computedAutoSaleAt() {
let now = +new Date()
let autoTime = +new Date(`${timeFormatD(+new Date())} ${autoSaleAt.value}`)
if (now < autoTime) {
return `${timeFormatD(+new Date())} ${autoSaleAt.value}`
} else {
return `${timeFormatD(+new Date() + 24 * 3600 * 1000)} ${autoSaleAt.value}`
}
}
// 自动可售时间
const autoSaleAt = computed(() => {
let { autoSaleAt = '' } = configCms.serveInfo
return autoSaleAt
})
/*************************************************
* 修改商品可售状态
*/
async function changeSaleStatus(sku: AnyObject, skuName: AnyObject, status: number) {
if (props.isAudit) return false
sku.storeSkuStatus = +status
// 计算可售不可售图标
if (skuName.skus.some((item: AnyObject) => item.storeSkuStatus)) {
skuName.skuAllnoSale = false
} else {
skuName.skuAllnoSale = true
}
}
return {
isNewPriceDisplay, // 列表判断条件
userType, // 获取userType
updateSaleStatus, // 修改商品售卖状态
tmpSaleNo, // 临时不可售
}
}
export default goodsListFn

View File

@@ -0,0 +1,263 @@
<template>
<view class="skuName-cell">
<!-- skuName -->
<view class="skuName">
<!-- 图片 -->
<view class="skuName-img" @tap="previewImg(skuName)">
<view v-show="skuName.skuAllnoSale">不可售</view>
<image
class="imglabel"
mode="scaleToFill"
lazy-load
:src="skuName.img"
/>
</view>
<!-- 图片 -->
<!-- 其他信息 -->
<view class="skuName-info">
<!-- 名称 -->
<view class="skuName-name">
{{ skuName.prefix ? '[' + skuName.prefix + ']' : ''
}}{{ skuName.name }}
</view>
<view @tap.stop="copyInfo(''+ skuName.id, '复制nameID成功')" style="color:#818181;">
<text>{{ skuName.id }}</text>
<jx-icon icon="fuzhi" color="#818181" style="margin-left: 10rpx;"></jx-icon>
</view>
<!-- 价格 改价 -->
<view class="skuName-price" @tap="openPriceDialog(skuName)">
<jx-price
:price="skuName.unitPrice"
color="#F60D58"
sizeM="22rpx"
sizeN="38rpx"
/>
<view class="icon-modify" v-if="!isAudit">
<jx-icon icon="shuxie" color="#999" :size="38" />
</view>
</view>
<!-- 提示信息 -->
<view>
<view class="skuName-tips" v-if="skuName.unit === '份'">
该价格为每斤价格
</view>
<view class="skuName-tips green" v-if="skuName.unit !== '份'">
该价格为每{{ skuName.unit }}价格
</view>
<!-- 审核状态 -->
<view class="check-display" v-show="skuName.auditUnitPrice">
审核中价格<jx-price
:price="skuName.auditUnitPrice"
color="#f44"
size="24rpx"
/>
</view>
</view>
</view>
<!-- 其他信息 -->
</view>
<!-- skuName -->
<!-- sku2 -->
<view class="skus-wrapper-new" v-if="!isAudit">
<view v-for="(sku, j) in skuName.skus" :key="j" class="sku-cell2">
<!-- 异常商品 -->
<view
class="error mt"
v-if="
sku.mtwmID !== '' &&
(sku.mtwmSyncStatus & 16) === 16 &&
(sku.mtwmSyncStatus & 2) !== 2 &&
(sku.mtwmSyncStatus & 4) !== 4
"
>美团:该商品无法修改价格,请联系运营修改</view
>
<view
class="error eb"
v-if="
sku.ebaiID !== '' &&
(sku.ebaiSyncStatus & 16) === 16 &&
(sku.ebaiSyncStatus & 2) !== 2 &&
(sku.ebaiSyncStatus & 4) !== 4
"
>饿佰:该商品无法修改价格,请联系运营修改</view
>
<view class="cell-top">
<view class="price">
<view class="sku-spec">
{{ sku.specQuality }}{{ sku.specUnit }}
</view>
<view
class="sku-price"
:class="{ 'promotion-price': !isNewPriceDisplay && sku.actPrice }"
>
<jx-price
:price="sku.comparePrice"
sizeM="22rpx"
sizeN="28rpx"
color="#000"
/>
</view>
</view>
<view class="btn-group">
<view
class="btn"
:class="{ onActive: sku.storeSkuStatus }"
@tap="updateSaleStatus(sku, skuName, 1)"
>
可售
</view>
<view
class="btn"
:class="{ onActive: !sku.storeSkuStatus && !sku.autoSaleAt }"
@tap="updateSaleStatus(sku, skuName, 0)"
>
不可售
</view>
<view
v-if="!isHot"
class="btn tmpSaleNo"
:class="{ onActive: !sku.storeSkuStatus && sku.autoSaleAt }"
@tap="tmpSaleNo(sku, skuName)"
>
<text>临时</text>
<text>不可售</text>
</view>
</view>
</view>
<!-- 库存以及位置码 -->
<view style="display: flex;color:#666;margin-top:20rpx;justify-content: center">
<view @tap="openDialog(skuName,sku,'stock')">
库存:{{sku.stock}}
<jx-icon icon="shuxie" color="#999" :size="24" />
</view>
<view style="margin-left: 20rpx;" @tap="openDialog(skuName,sku,'locationCode')">
货架码:{{sku.locationCode && sku.locationCode !== 'EMPTY_VALUE' ? sku.locationCode : ''}}
<jx-icon icon="shuxie" color="#999" :size="24" />
</view>
</view>
<view class="cell-bottom">{{ sku.comment ? sku.comment : '' }}</view>
<view class="sku-autoSaleAt" v-if="sku.autoSaleAt">
该规格将在 <text>{{ sku.autoSaleAt }}</text> 可售
</view>
</view>
<view v-if="showMountTab" class="checkBoxWrap">
<label @tap="handleSale(skuName)">
<checkbox color="#4eb331" style="transform: scale(0.8)" />选中商品
</label>
</view>
</view>
<!-- 待审核商品操作面板 -->
<view class="aduit-wall" v-else>
<view class="aduit-type">{{
skuName.type === 1 ? '【价格】正在审核中' : '【新建】正在审核中'
}}</view>
<!-- ((userType & 4) === 4) 运营 -->
<view class="aduit-btn-group" v-if="(+getStorage('userType') & 4) === 4">
<view class="btn refuse" @tap="toExamine(skuName, -1)">拒绝</view>
<view class="btn" @tap="toExamine(skuName, 1)">批准</view>
</view>
<view class="aduit-btn-group" v-else>
<view class="btn" @tap="phoneCall(storeInfo.marketManName)"
>联系运营加快审核</view
>
</view>
</view>
</view>
</template>
<script lang="ts" setup >
import useGlobalFunc from '@/composables/useGlobalFunc'
import { store } from '@/store'
import { getStorage } from '@/utils/storage'
import { computed } from 'vue'
import goodsListFn from './right-main'
import useOrderInfo from '@/composables/useOrderInfo'
const { copyInfo } = useOrderInfo()
const { phoneCall } = useGlobalFunc()
/*************************************************
* 接收数据
*/
interface propType {
skuName: AnyObject
isAudit: boolean
isHot: boolean
showMountTab: boolean
}
const prop = defineProps<propType>()
const {
isNewPriceDisplay, // 列表判断条件
updateSaleStatus, // 修改商品售卖状态
tmpSaleNo, // 临时不可售
} = goodsListFn(prop)
const {
previewImage, // 预览图片
} = useGlobalFunc()
const emit = defineEmits<{
(e: 'handleSale', data: AnyObject): void
(e: 'openPriceDialog', data: AnyObject): void
(e: 'openDialog', data: AnyObject): void
(e: 'toExamine', data: AnyObject): void
}>()
function handleSale(skuName: AnyObject) {
emit('handleSale', skuName)
}
/*************************************************
* 预览图片
*/
function previewImg(skuName:AnyObject) {
let arr = [skuName.img,skuName.img2,skuName.img3,skuName.img4,skuName.img5].filter(item => { return item && item.length > 0 })
previewImage(arr)
}
/*************************************************
* 修改操作
*/
function openPriceDialog(skuName: AnyObject) {
if (prop.isAudit) return
emit('openPriceDialog', skuName)
}
function openDialog(skuName: AnyObject,sku:AnyObject,type:String) {
let payload = {
nameID:skuName.id,
Skus:[{
skuID:sku.id,
stock:sku.stock,
locationCode:sku.locationCode && sku.locationCode !== 'EMPTY_VALUE' ? sku.locationCode : ''
}]
}
emit('openDialog', {payload,type,name:skuName.name})
}
/*************************************************
* 拒绝改价
*/
function toExamine(skuName: AnyObject, type: any) {
let data = {
status: type,
nameID: skuName.nameID,
storeID: getStorage('storeID'),
auditPrice: skuName.auditUnitPrice,
}
emit('toExamine', data)
}
/***************************************************************
* 获取门店信息
*/
const storeInfo = computed(() => {
return store.state.storeInfo.allStoreInfo
})
</script>
<style lang="scss" scoped>
@import './right-main.scss';
</style>

View File

@@ -0,0 +1,217 @@
<template>
<view class="filter-root"
:class="{'filter-fill':isCheckoutFilter}">
<template v-if="isCheckoutFilter">
<view
class="item item-fill"
:class="{ active: filteActive2 == item.lable }"
v-for="(item, index) in filterData2"
:key="index"
@tap="fliterFn2(item.lable, item.status)"
>{{ item.title }}</view
>
</template>
<template v-else>
<view
class="item item-fill"
:class="{ active: filteActive == item.lable }"
v-for="(item, index) in filterData"
:key="index"
@tap="fliterFn(item, item.status)"
>{{ item.title }}</view
>
</template>
</view>
</template>
<script lang="ts" setup >
import { ref, watch } from 'vue'
import { jx_throttles } from '@/utils/tools'
import toast from '@/utils/toast'
interface propsType {
isCheckoutFilter: boolean
updateStateOk: boolean
}
const props = defineProps<propsType>()
/*************************************************
* 参数1
*/
const filterData = ref<Array<AnyObject>>([
{
title: '全部',
name:'all',
lable: 2,
status: -1,
},
{
title: '可售',
name:'status',
lable: 1,
status: 1,
},
{
title: '不可售',
name:'status',
lable: 0,
status: 0,
},
{
title: '有库存', //库存 1 全查~ 2 有~ 3 无~
name:'stock',
lable:4,
status: 2,
},
{
title: '无库存',
name:'stock',
lable:5,
status: 3,
},
// {
// title: '有货架码',
// name:'locationCode',
// lable:6,
// status:false // 实际上是全部查,感觉没用
// },
{
title: '无货架码',
name:'locationCode',
lable:7,
status:true
},
{
title: '批量',
name:'',
lable: 3,
status: -1,
},
])
/*************************************************
* 参数2
*/
const filterData2 = ref<Array<AnyObject>>([
{
title: '全部',
lable: 2,
status: -1,
},
{
title: '批量',
lable: 3,
status: -1,
},
])
/*************************************************
* emit
*/
const emit = defineEmits<{
(e: 'updateIsSale', data: AnyObject): void
(e: 'updateTowFilter', data: AnyObject): void
}>()
/*************************************************
* 筛选1
*/
const filteActive = ref<number>(2)
function fliterFn(item: AnyObject, status: number) {
if (filteActive.value == item.lable) return
fliterFnThrottles(item, status)
}
const fliterFnThrottles = jx_throttles({
time: 2000,
success: (item: AnyObject, status: number | boolean) => {
filteActive.value = item.lable
filteActive2.value = item.lable
let data = {
status: status,
lable: item.lable,
name:item.name
}
emit('updateIsSale', data)
},
fail: () => {
toast('老板要点慢点哦')
},
})
/*************************************************
* 筛选2
*/
const filteActive2 = ref<number>(2)
function fliterFn2(lable: number, status: number) {
if (filteActive2.value == lable) return
filteActive2.value = lable
filteActive.value = lable
let data = {
status: status,
lable: lable,
}
emit('updateTowFilter', data)
}
watch(
() => props.updateStateOk,
(val) => {
if (val) {
filteActive2.value = 2
filteActive.value = 2
}
}
)
</script>
<style lang="scss" scoped>
.filter-root {
display: flex;
overflow-x: auto; /* 允许水平方向滚动 */
white-space: nowrap; /* 防止子元素换行 */
align-items: center;
// justify-content: space-around;
box-sizing: border-box;
width: 100%;
border-left: 1rpx solid rgb(223, 223, 223);
height: 90rpx;
background-color: #fff;
box-shadow: 10rpx 0rpx 10rpx rgb(207, 207, 207);
border-bottom: 1rpx solid rgb(224, 224, 224);
padding: 0 10rpx;
.filter-fill{
justify-content: space-around;
}
.item {
flex: 1;
height: 60rpx;
border-radius: 10rpx;
border: 2rpx solid $jx-primary;
color: $jx-primary;
padding: 0 10rpx;
line-height: 60rpx;
text-align: center;
transition: all 0.2s;
margin-left: 10rpx;
}
.item-filter{
width: fit-content;
}
.item-fill{
flex: 1;
}
.item:nth-child(1) {
margin: 0 !important;
}
.active {
background-color: $jx-primary;
color: #fff;
}
}
</style>

View File

@@ -0,0 +1,129 @@
<template>
<view class="search-root">
<view class="add-shopping" @tap="createGoods">
<jx-icon icon="jiahao" color="#4eb331" />
<text>新建</text>
</view>
<uni-search-bar
cancelButton="none"
clearButton="auto"
placeholder="请输入关键字 例如:精华液"
@confirm="confirm"
@input="input"
@clear="clear"
v-model="text"
/>
</view>
</template>
<script lang="ts" setup >
import toast from '@/utils/toast'
import { jx_trembling } from '@/utils/tools'
import { ref, watch } from 'vue'
/*************************************************
* props
*/
interface propsType {
isFilter: string | number
}
const props = defineProps<propsType>()
const text = ref<string>('')
let isInput = false
watch(
() => props.isFilter,
(val) => {
if (val == 'hot' || val == 'audit') {
isInput = true
} else {
isInput = false
}
}
)
/*************************************************
* emit
*/
const emit = defineEmits<{
(e: 'serachShopping', data: string): void
(e: 'clearInpu', data: string): void
(e: 'createGoods'): void
}>()
/*************************************************
* 确定搜索
*/
let watchTimer: any = null
function confirm(e: AnyObject) {
if (isInput) {
let watchTimer = setTimeout(() => {
text.value = ''
clearTimeout(watchTimer)
}, 500)
return toast('该分类暂不支持搜索')
}
emit('serachShopping', e.value)
}
/*************************************************
* 输入框加载
*/
function input(e: string) {
if (isInput) {
let watchTimer = setTimeout(() => {
text.value = ''
clearTimeout(watchTimer)
}, 500)
return toast('该分类暂不支持搜索')
}
trembling(e)
}
const trembling = jx_trembling((e: string) => {
emit('serachShopping', e)
}, 1000)
/*************************************************
* 清空输入框
*/
function clear(e: AnyObject) {
if (isInput) return
emit('clearInpu', e.value)
}
/*************************************************
* 新建商品
*/
function createGoods() {
emit('createGoods')
}
</script>
<style lang="scss">
.search-root {
box-sizing: border-box;
background-color: $jx-primary;
padding: 0 20rpx 2rpx 20rpx;
height: 92rpx;
display: flex;
align-items: center;
justify-content: space-between;
.add-shopping {
width: 80rpx;
height: 80rpx;
background-color: #fff;
font-size: 20rpx;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
border-radius: 50%;
color: $jx-primary;
line-height: 1;
padding: 0;
}
}
</style>

View File

@@ -0,0 +1,45 @@
// 一级
.one-item {
background-color: #fff;
height: 90rpx;
overflow: hidden;
transition: all 0.4s;
border-bottom: 1rpx solid #f1f1f1;
// 二级名字
.one-item-name {
display: flex;
height: 90rpx;
line-height: 90rpx;
.iconfont {
display: inline-block;
transform: rotate(0deg);
transition: all 0.2s;
}
.noe-seat {
display: inline-block;
width: 30rpx;
height: 90rpx;
border-left: 7rpx solid transparent;
transition: none;
}
}
// 二级内容
.tow-item {
display: flex;
height: 90rpx;
line-height: 90rpx;
transition: none;
// 二级左侧竖杠
.tow-seat {
display: inline-block;
width: 30rpx;
height: 90rpx;
border-left: 7rpx solid transparent;
}
}
}

View File

@@ -0,0 +1,83 @@
import { getStorage } from "@/utils/storage"
import { onLoad } from "@dcloudio/uni-app"
import { ref } from "vue"
import shopping from "@/api/https/shopping"
/*************************************************
* 商品分类栏目
*/
function leftBarFn(props: AnyObject) {
onLoad(async () => {
await GetStoreCategoryMap()
})
/*************************************************
* @info 左侧tab数据
* 获取列表数据
*/
const cat = ref<Array<AnyObject>>([])
async function GetStoreCategoryMap() {
let data = {
storeID: getStorage('storeID'),
parentID: -1,
}
let res = await shopping.getStore_category_map(data)
if (res.code == 0) {
handledata(res.data, true)
} else {
cat.value = []
handleOriginData()
}
}
/*************************************************
* 处理列表数据
*/
const _isMap = ref<boolean>(false)
function handledata(curdeData: Array<AnyObject>, isMap: boolean) {
let data = curdeData
let catData: Array<AnyObject> = []
data.forEach((item: AnyObject) => {
if (item.name !== '3件9.9') {
item.name = item.name.slice(0, 4)
}
catData.push(item)
})
//把sku分类过滤掉
if (!isMap) catData = catData.filter((item) => item.type === 0)
let catLevel1 = catData.filter((item) => item.level === 1)
let catLevel2 = catData.filter((item) => item.level === 2)
// 组合分类
let catData2: Array<AnyObject> = []
let catData3: AnyObject
catLevel1.forEach((level1: AnyObject) => {
isMap
? (catData3 = catLevel2.filter(
(level2) => level2.parentID === level1.categoryID
))
: (catData3 = catLevel2.filter((level2) => level2.parentID === level1.id))
level1.children = catData3
_isMap.value = isMap
catData2.push(level1)
})
let lastData = props.dafauleData.concat(catData2)
cat.value = lastData
}
async function handleOriginData() {
let res = await shopping.get_categories()
if (res.code == 0) {
handledata(res.data, false)
}
}
return {
cat, // 右侧菜单数据
GetStoreCategoryMap, // 获取数据
}
}
export default leftBarFn

View File

@@ -0,0 +1,139 @@
<template>
<view
v-for="(item, index) in cat"
:key="index"
class="one-item"
:class="{
'one-item-active':
index == oneActive && item.children.length != 0 && isOpen,
'item-name': oneActive == index && item.children.length == 0,
}"
>
<!-- 第一层 -->
<view
:class="{
'never-pen-active': index == oneActive && item.children.length != 0,
}"
class="one-item-name"
@tap="oneItemFn(item, index)"
>
<text class="iconfont icon-jiantou1" v-if="item.children.length != 0" />
<text class="noe-seat noe-seat-active" v-else></text>
<text>{{ item.name }}</text>
</view>
<!-- 第二层 -->
<view
class="tow-item"
:class="{ 'tow-item-active': towActive == childIndex }"
v-for="(childItem, childIndex) in item.children"
:key="childIndex"
@tap="towItemFn(childItem, childIndex)"
>
<text class="tow-seat tow-seat-active"></text>{{ childItem.name }}</view
>
</view>
</template>
<script lang="ts" setup >
import { Ref, ref } from 'vue'
import leftBarFn from './left-bar'
/*************************************************
* 接收数据
*/
interface propsType {
dafauleData: Array<AnyObject>
}
const props = defineProps<propsType>()
const {
cat, // 右侧菜单数据
GetStoreCategoryMap, // 获取数据
} = leftBarFn(props)
/*************************************************
* emit
*/
const eimt = defineEmits<{
(e: 'oneMenuClick', data: string | number): void
}>()
/*************************************************
* 一级菜单点击
*/
const isOpen = ref<boolean>(false)
const oneActive = ref<number>(0) // 高亮
const oneHeight: Ref<string> = ref('0px') // 高度
function oneItemFn(item: AnyObject, index: number) {
if (oneActive.value == index) {
isOpen.value = !isOpen.value
} else {
isOpen.value = true
}
oneActive.value = index // 一级菜单
oneHeight.value = `${(item.length + 1) * 90}rpx` // 动态高度
towActive.value = -1 // 默认二级菜单
if (!isOpen.value) return
eimt('oneMenuClick', item.categoryID)
}
/*************************************************
* 二级菜单点击
*/
const towActive = ref<number>(-1) // 高亮
function towItemFn(childItem: AnyObject, index: number) {
if (towActive.value == index) return
towActive.value = index
eimt('oneMenuClick', childItem.categoryID)
}
/*************************************************
* 导出子组件方法
*/
defineExpose({
GetStoreCategoryMap,
})
</script>
<style lang="scss" scoped>
@import './left-bar.scss';
// 一级展开动画
.one-item-active {
height: v-bind(oneHeight) !important;
background-color: rgb(236, 252, 233);
// 三角图标动画
.icon-jiantou1 {
display: inline-block;
transform: rotate(90deg) !important;
}
}
// 一级选中高亮
.never-pen-active {
background-color: $jx-primary;
color: #fff;
border-bottom: 1rpx solid #fff;
}
// 一级展开背景
.item-name {
color: $jx-primary;
.noe-seat-active {
border-left: 7rpx solid $jx-primary !important;
}
}
// 二级高亮
.tow-item-active {
color: $jx-primary;
background-color: #fff;
// 二级选中高亮
.tow-seat-active {
border-left: 7rpx solid $jx-primary !important;
}
}
</style>

View File

@@ -0,0 +1,132 @@
.status_bar {
height: var(--status-bar-height);
width: 100%;
background-color: $jx-primary;
}
.MountWrap {
display: flex;
z-index: 99;
box-sizing: border-box;
justify-content: space-between;
position: fixed;
width: 100%;
bottom: 0;
left: 0;
background-color: $jx-primary;
padding: 20rpx 25rpx;
.onActive {
width: 44%;
background-color: #fff;
color: $jx-primary;
text-align: center;
border-radius: 10rpx;
padding: 10rpx 0;
}
}
.createGoods {
padding: 10rpx 80rpx;
border-radius: 10rpx;
background-color: $jx-primary;
color: #fff;
text-align: center;
margin-top: 20rpx;
}
.jx-popup-update {
box-sizing: border-box;
padding: 20rpx;
background-color: #fff;
border-radius: 0 0 15rpx 15rpx;
.text {
display: block;
width: 100%;
text-align: center;
margin-bottom: 25rpx;
padding-bottom: 10rpx;
border-bottom: 2rpx solid rgb(234, 234, 234);
}
.tip-new{
padding: 2rpx 0;
height: 66rpx;
line-height: 66rpx;
text-align: left;
border-bottom:1px dashed #999;
.money {
color: $jx-primary;
}
}
.ipt-box{
margin: 10rpx 0;
// height: 86rpx;
// line-height: normal;
}
.tip {
padding: 10rpx 0;
.money {
color: $jx-primary;
}
}
.ipt {
border: 2rpx solid rgb(209, 209, 209);
text-align: center;
border-radius: 5rpx;
height: 86rpx;
line-height: normal;
// -webkit-appearance: none;
// border-radius: 0
}
.title {
width: 100%;
text-align: center;
margin-bottom: 20rpx;
}
.btn-root {
display: flex;
justify-content: space-between;
margin: 40rpx 0 0rpx 0;
.btn-esc,
.btn-ok {
text-align: center;
width: 100%;
padding: 15rpx 0;
border-radius: 10rpx;
border: 2rpx solid $jx-primary;
color: $jx-primary;
}
.btn-ok {
background-color: $jx-primary;
color: #fff;
margin-left: 10rpx;
}
.btn-esc {
margin-right: 10rpx;
}
}
.delete {
margin-top: 50rpx;
margin-bottom: 20rpx;
background-color: $jx-warring;
text-align: center;
color: #fff;
padding: 20rpx;
border-radius: 15rpx;
}
}

View File

@@ -0,0 +1,714 @@
import { onLoad, onShow } from '@dcloudio/uni-app'
import { getStorage } from '@/utils/storage'
import { computed, onBeforeUnmount, ref } from 'vue'
import { store } from '@/store'
import { timeFormatD, timeFormatHMS } from '@/utils/tools'
import toast from '@/utils/toast'
import merchant from "@/api/https/merchant"
import shopping from "@/api/https/shopping"
/*************************************************
* 商品管理
*/
function shoppingMangerFn() {
/*************************************************
* 数据归位,元神显现
*/
function dataHoming() {
skuNames.value = []
page.value = 1
totalCount.value = 0
}
/*************************************************
* 下拉刷新数据重现
*/
const leftBarRef = ref<any>(null)
let onPullDownRefreshTimer: any = ''
const triggered = ref<boolean>(false)
function refresherrefresh() {
triggered.value = true
clearTimeout(onPullDownRefreshTimer)
onPullDownRefreshTimer = setTimeout(() => {
triggered.value = false
dataHoming()
oneMenuClick(isFilter.value)
if (isFilter.value == 'hot') return
leftBarRef.value.GetStoreCategoryMap()
}, 500)
}
/*************************************************
* 获取数据
*/
let oldStoreID: any = 0
onShow(() => {
if (oldStoreID != getStorage('storeID') && oldStoreID != 0) {
triggered.value = true
}
oldStoreID = getStorage('storeID')
})
/*************************************************
* 默认数据
*/
const dafauleData: Array<AnyObject> = [
{
categoryID: 'all',
children: [],
level: 1,
name: '所有分类',
},
{
categoryID: 'act',
children: [],
level: 1,
name: '活动商品',
},
{
categoryID: 'hot',
children: [],
level: 1,
name: '畅销推荐',
},
{
categoryID: 'audit',
children: [],
level: 1,
name: '待审商品',
},
]
/*************************************************
* @info 右侧主体数据
* 获取商品数据
*/
const statusBarHeight = ref<string>('0px') // 状态栏高度
onLoad(async () => {
// 获取状态栏高度
uni.getSystemInfo({
success: (res: any) => {
statusBarHeight.value = res.statusBarHeight + 'px'
}
})
await getSkuNames()
})
const keyword = ref<string>('')
const categoryID = ref<string>('') // 分类id
const isAct = ref<boolean>(false) // 是否是活动商品
const totalCount = ref<number>(0) // 总条数
const skuNames = ref<Array<AnyObject>>([]) // 商品数据
const isAudit = ref<boolean>(false) // 商品列表判断条件
const isHot = ref<boolean>(false) // 商品列表判断条件
async function getSkuNames() {
isLoad.value = true
let data:AnyObject = {
categoryID: categoryID.value,
isFocus: true,
keyword: keyword.value,
offset: pageSize.value * (page.value - 1),
pageSize: pageSize.value,
isAct: isAct.value,
status: -1,
storeID: getStorage('storeID')
}
if(allSaleStatus.value && allSaleStatus.value.name !== 'all') data[allSaleStatus.value.name] = allSaleStatus.value.status
keyFilter(data)
let res = await shopping.get_stores_skus_for_store(data)
if (res.code == 0) {
totalCount.value = res.data.totalCount
let filterData = mapskuName(res.data.skuNames || [])
skuNames.value = skuNames.value.concat(filterData)
if (filterData.length == 0) return toast('该分类未找到商品')
} else {
dataHoming()
toast('分类查询错误')
}
isLoad.value = false
}
/*************************************************
* 过滤对象空字段
*/
function keyFilter(obj: AnyObject) {
for (let key in obj) {
if ('' + obj[key] == "" || '' + obj[key] == undefined) {
delete obj[key]
}
}
return obj
}
/*************************************************
* 列表判断条件
*/
const isNewPriceDisplay = ref<boolean>(
store.getters['storeInfo/isNewPriceDisplay']
)
/*************************************************
* 格式化数据
*/
function mapskuName(skuNames: Array<AnyObject>) {
let arr = skuNames.map((skuName) => {
skuName.auditUnitPrice = skuName.auditUnitPrice
// 是否全部不可售
if (skuName.skus.some((item: AnyObject) => item.storeSkuStatus)) {
skuName.skuAllnoSale = false
} else {
skuName.skuAllnoSale = true
}
// skus
skuName.skus = skuName.skus.map((sku: AnyObject) => {
// 价格异常请参考老版本或者在git进行查看
sku.comparePrice = sku.price
sku.autoSaleAt =
+new Date(sku.autoSaleAt) > 0 ? timeFormatHMS(sku.autoSaleAt) : 0
return sku
})
return skuName
})
return arr
}
/*************************************************
* 页面触底,加载更多数据
*/
const page = ref<number>(1) // 第几页
const pageSize = ref<number>(20) // 每页条数
const isLoad = ref<boolean>(false) // 加载图
function scrolltolower() {
page.value++
if (pageSize.value * (page.value - 1) > totalCount.value || totalCount.value < pageSize.value) {
isLoad.value = false
} else {
if (isLoad.value) return
if (isFilter.value == 'hot') return
if (isFilter.value == 'audit') return GetStoreSkuAudit()
getSkuNames()
}
}
/*************************************************
* 点击菜单获取数据
*/
const isCheckoutFilter = ref<boolean>(false)
const isFilter = ref<string | number>('')
function oneMenuClick(catID: string | number) {
isAct.value = false
isHot.value = false
isCheckoutFilter.value = false
categoryID.value = ''
keyword.value = ''
isFilter.value = catID
isAudit.value = false
dataHoming()
if (catID == 'all') { // 全部商品
isAct.value = false
getSkuNames()
} else if (catID == 'act') { // 活动商品
isAct.value = true
getSkuNames()
} else if (catID == 'hot') { // 畅销商品
isHot.value = true
isCheckoutFilter.value = true
getTopSkus()
} else if (catID == 'audit') { // 待审核商品
isCheckoutFilter.value = true
isAudit.value = true
GetStoreSkuAudit()
} else {
categoryID.value = catID as string
getSkuNames()
}
}
/*************************************************
* 畅销推荐
*/
async function getTopSkus() {
isLoad.value = true
let data = {
storeID: getStorage('storeID')
}
let res = await shopping.get_top_skus_by_city_code(data)
if (res.code == 0) {
let data = res.data || []
if (data.length > 0) {
totalCount.value = data.length
skuNames.value = mapskuName(data)
} else {
dataHoming()
toast('暂无畅销商品')
}
} else {
toast('分类查询错误')
dataHoming()
}
isLoad.value = false
}
/*************************************************
* 待审核商品
*/
async function GetStoreSkuAudit() {
isLoad.value = true
let data = {
storeIDs: JSON.stringify([getStorage('storeID')]),
applyTimeStart: timeFormatD(+new Date() - 1000 * 60 * 60 * 24 * 30) + ' 00:00:00',
applyTimeEnd: timeFormatD(+new Date()) + ' 23:59:59',
statuss: JSON.stringify([0]),
types: JSON.stringify([1, 2]),
offset: pageSize.value * (page.value - 1),
pageSize: pageSize.value,
}
let res = await shopping.get_store_sku_audit(data)
if (res.code == 0) {
totalCount.value = res.data.totalCount
let newData = res.data.data || []
let filterSkuName = mapAuditSkuName(newData)
skuNames.value = skuNames.value.concat(filterSkuName)
if (newData.length == 0) return toast('暂无待审核商品')
} else {
toast('分类查询错误')
dataHoming()
}
isLoad.value = false
}
/*************************************************
* 过滤待审核商品
*/
function mapAuditSkuName(data: AnyObject) {
let arr = data.map((item: any) => ({
auditUnitPrice: item.unitPrice,
unitPrice: item.originPrice,
skuAllnoSale: false,
nameID: item.nameID,
img: item.img,
unit: item.unit,
type: item.type,
prefix: item.prefix,
name: item.skuName,
}))
return arr
}
/*************************************************
* @info 过滤类数据
*/
const showMountTab = ref<boolean>(false)
/*************************************************
* 头部数据过滤
*/
const allSaleStatus = ref<AnyObject>()
async function updateIsSale(data: AnyObject) {
allSaleStatus.value = data
data.lable == 3
? (showMountTab.value = true)
: (showMountTab.value = false)
if (data.lable == 3) return
dataHoming()
await getSkuNames()
}
async function updateTowFilter(data: AnyObject) {
if (data.lable == 3) {
return showMountTab.value = true
} else {
showMountTab.value = false
}
if (isFilter.value == 'hot') {
oneMenuClick(isFilter.value)
}
if (isFilter.value == 'audit') {
oneMenuClick(isFilter.value)
}
}
/*************************************************
* @info 搜索框操作
*/
async function serachShopping(text: string) {
dataHoming()
keyword.value = text
await getSkuNames()
}
async function clearInpu() {
dataHoming()
keyword.value = ''
await getSkuNames()
}
/*************************************************
* 批量操作
*/
let picked: Array<AnyObject> = []
function handleSale(skuName: AnyObject) {
if (picked && picked.some((item) => item.nameID == skuName.id)) {
picked = picked.filter((item) => {
return item.nameID != skuName.id
})
} else {
picked.push({ nameID: Number(skuName.id) })
picked = distinct(picked, 'nameID')
}
}
function distinct(arr: any, key: string) {
let set = new Set()
return arr.reduce(
(p: any, c: any) => (set.has(c[key]) ? p : (set.add(c[key]), [...p, c])),
[]
)
}
/*************************************************
* 批量可售与不可售
*/
let handleChangeSaleTimer: any = null
const updateStateOk = ref<boolean>(false)
async function handleChangeSale(type: number) {
if (picked.length == 0) return toast('未选择商品')
picked.forEach((item: AnyObject) => {
item['isSale'] = type
})
let data = {
storeIDs: JSON.stringify([getStorage('storeID')]),
payload: JSON.stringify(picked),
}
let res = await shopping.update_stores_skus(data)
if (res.code == 0) {
toast('修改成功')
let data = {
status: -1,
lable: 2,
}
updateStateOk.value = true
handleChangeSaleTimer = setTimeout(() => {
updateIsSale(data)
clearTimeout(handleChangeSaleTimer)
}, 1000)
} else {
toast('修改失败', 2)
updateStateOk.value = false
}
}
/*************************************************
* 去创建商品
*/
function createGoods() {
uni.navigateTo({ url: '/subPages/shoppingChild/createGoods/createGoods' })
}
/*************************************************
* 改价还是折扣从vuex 里面读取)
*/
const isPointStore = computed(() => {
return store.getters['storeInfo/isPointStore']
})
// skuName
const skuName = ref<AnyObject>({
unit: '',
auditUnitPrice: ''
})
function openPriceDialog(skuNames: AnyObject) {
skuName.value = skuNames
popup.value.open()
}
// 当前修改价格
const currentValue = ref<string>('')
// 审核价格
const auditUnitPrice = computed(() => {
let price = skuName.value.auditUnitPrice
if (price) {
return +((price * 1) / 100).toFixed(2)
} else {
return +'0.00'
}
})
// 弹窗实例
const popup = ref<any>(null)
// 确定修改价格
function handleConfirm() {
if (isNaN(+currentValue.value)) return toast('请输入格式正确的价格')
if (+currentValue.value < 0.01) return toast('请输入大于等于1分钱的价格')
if (
currentValue.value.toString().indexOf('.') > -1 &&
currentValue.value.toString().split('.')[1].length > 2
) {
return toast('最多只能有两位小数')
}
let updatePrice = Math.round(+currentValue.value * 100)
if (updatePrice == skuName.value.unitPrice) {
return toast('没有修改价格')
}
if (updatePrice > skuName.value.unitPrice * 1.3 ||
updatePrice < skuName.value.unitPrice * 0.7) {
uni.jxConfirm({
title: '提示',
content: `修改幅度超过30%,新价格为¥${(updatePrice / 100).toFixed(2)},是否确定修改?`,
success: () => {
updatePriceFn(updatePrice, skuName.value.id, skuName.value.unitPrice)
}
})
} else {
updatePriceFn(updatePrice, skuName.value.id, skuName.value.unitPrice)
}
}
/**
* 验证通过开始修改价格
*/
let updatePriceFnTimer: any = null
async function updatePriceFn(price: string | number, id: number, originalUnitPrice: number) {
let data = {
storeIDs: JSON.stringify([+getStorage('storeID')]),
payload: JSON.stringify([
{
nameID: skuName.value.id,
unitPrice: price,
},
]),
}
popup.value.close() // 关闭 popup
// 修改价格
let updatePrice = await shopping.update_stores_skus(data)
if (updatePrice.code == 0) {
// 获取单个门店serverSkuName
let storeData = {
storeIDs: JSON.stringify([+getStorage('storeID')]),
isFocus: true,
nameIDs: JSON.stringify([id]),
fromStatus: 0,
toStatus: 1,
}
const serverSkuName = await merchant.get_stores_skus(storeData)
if (serverSkuName.code == 0) {
let skuNameData = serverSkuName.data.skuNames || []
if (skuNameData.length > 0) {
let index = skuNames.value.findIndex((item: any) => item.id == id)
if (index !== -1) skuNames.value[index] = mapskuName(skuNameData)[0]
}
}
// 判断提示
if (isPointStore.value || store.state.storeInfo.allStoreInfo.storeLevel == 'E') {
toast('修改成功')
currentValue.value = ''
} else {
currentValue.value = ''
toast(
+price < originalUnitPrice ? '修改成功' : '提交成功, 请等待审核'
)
}
} else {
toast('修改失败', 2)
}
}
/*************************************************
* 修该库存或货架码
*/
const popupDialog = ref<any>(null)
const skuNameItem = ref<AnyObject>({
payload:{
nameID:0,
Skus:[],
},
name:'',
type:'price'
})
const stockOrCode = ref('')
// 打开dialog 修改库存以及货架码
function openDialog(payload: AnyObject) {
skuNameItem.value = payload
const sku = payload.payload.Skus[0]
if(payload.type === 'stock') stockOrCode.value = sku.stock + ''
if(payload.type === 'locationCode') stockOrCode.value = sku.locationCode
popupDialog.value.open()
}
async function sureStockOrCode() {
const nameID = skuNameItem.value.payload.nameID
const skuID = skuNameItem.value.payload.Skus[0].skuID
let data:AnyObject = {
storeIDs: JSON.stringify([+getStorage('storeID')])
}
if(skuNameItem.value.type === 'stock') {
data.payload = JSON.stringify([{
nameID: nameID,
Skus:[
{
skuID: skuID,
stock: +stockOrCode.value
}
]
}])
}else{
data.payload = JSON.stringify([{
nameID: nameID,
Skus:[
{
skuID:skuID,
locationCode: ''+ stockOrCode.value ? '' + stockOrCode.value : 'EMPTY_VALUE'
}
]
}])
}
popupDialog.value.close() // 关闭 popupDialog
let res = await shopping.update_stores_skus(data)
if (res.code == 0) {
let storeData = {
storeIDs: JSON.stringify([+getStorage('storeID')]),
isFocus: true,
nameIDs: JSON.stringify([skuNameItem.value.payload.nameID]),
fromStatus: 0,
toStatus: 1,
}
const serverSkuName = await merchant.get_stores_skus(storeData)
if (serverSkuName.code == 0) {
let skuNameData = serverSkuName.data.skuNames || []
if (skuNameData.length > 0) {
let index = skuNames.value.findIndex((item: any) => item.id == skuNameItem.value.payload.nameID)
if (index !== -1) {
skuNames.value[index] = mapskuName(skuNameData)[0]
}
}
}
toast('修改成功')
}
}
/*************************************************
* 删除商品
*/
function handleDelete() {
uni.jxConfirm({
title: '提示',
content: `确认删除《${skuName.value.name}》吗?`,
confirmText: '确认',
success: async () => {
popup.value.close() // 关闭 popup
let data = {
storeIDs: JSON.stringify([getStorage('storeID')]),
payload: JSON.stringify([{ nameID: skuName.value.id, isFocus: -1 }]),
}
let res = await shopping.update_stores_skus(data)
if (res.code == 0) {
toast('删除成功')
skuNames.value = skuNames.value.filter((item) => item.id != skuName.value.id)
if (skuNames.value.length == 0) return dataHoming()
} else {
toast('删除失败', 2)
}
}
})
}
const toExamineData = ref<AnyObject>({}) // 审核商品数据
const toExaminePopup = ref<any>(null) // 审核弹窗实例
const toExamineValue = ref<string | number>('') // 审核输入框
/*************************************************
* 审核商品
*/
function toExamine(res: AnyObject) {
toExamineData.value = res
if (res.status == 1) {
toExamineValue.value = res.auditPrice / 100
} else {
toExamineValue.value = ''
}
toExaminePopup.value.open()
}
/*************************************************
* 改价审核操作
*/
async function toExamineConfirm() {
toExaminePopup.value.close()
let data = {
status: toExamineData.value.status,
isAsync: false,
isContinueWhenError: true,
payload: JSON.stringify([{
storeID: toExamineData.value.storeID,
nameID: toExamineData.value.nameID,
...(toExamineData.value.status == 1
? { auditPrice: Math.floor(+toExamineValue.value * 100) }
: { remark: toExamineValue.value })
}])
}
let res = await shopping.store_sku_price_audit(data)
if (res.code == 0) {
toast('操作成功')
skuNames.value = skuNames.value.filter((item) => item.nameID != toExamineData.value.nameID)
if (skuNames.value.length == 0) return dataHoming()
} else {
toast(res.desc || res.data)
}
}
/*************************************************
* 善后工作
*/
onBeforeUnmount(() => {
clearTimeout(handleChangeSaleTimer)
clearTimeout(updatePriceFnTimer)
clearTimeout(onPullDownRefreshTimer)
})
return {
dafauleData, // 左侧tablist 数据
totalCount, // 商品总条数
skuNames, // 商品列表数据
skuNameItem, // 商品数据2
isAudit, // 商品列表判断条件
isHot, // 商品列表判断条件
showMountTab, // 批量操作判断
scrolltolower, // 页面触底
isLoad, // 加载动画
updateIsSale, // 过滤筛选
updateTowFilter, // 过滤筛选
serachShopping, // 搜索框确定
clearInpu, // 清空搜索框
oneMenuClick, // 一级菜单点击事件
isCheckoutFilter, // 切换过滤选项
isFilter, // 判断条件
handleSale, // 批量可售与不可售
handleChangeSale, // 批量可售与不可售
updateStateOk, // 批量操作成功
createGoods, // 去创建商品
isPointStore, // 判断扣点还是折扣
skuName, // 当前修改的价格
auditUnitPrice, // 正在审核的价格
currentValue, // 当前修改价格
popup, // 弹窗实例
stockOrCode, // stock locationCode
popupDialog, // 弹窗 stock locationCode
handleConfirm, // 确定修改价格
openPriceDialog, // 获取修改数据
openDialog, // 获取修改数据2
sureStockOrCode, // 确认框 stock locationCode
handleDelete, // 删除商品
refresherrefresh, // 下拉刷新
triggered, // 下拉刷新标识
leftBarRef, // 右侧导航栏实例
statusBarHeight, // 状态栏高度
toExamine, // 审核商品
toExamineData, // 审核商品数据
toExaminePopup, // 改价审核实例
toExamineConfirm, // 确定操作审核
toExamineValue, // 审核输入内容
}
}
export default shoppingMangerFn

View File

@@ -0,0 +1,252 @@
<template>
<view class="status_bar"></view>
<view>
<shopping-search
class="shopping-cel"
:isFilter="isFilter"
@serachShopping="serachShopping"
@clearInpu="clearInpu"
@createGoods="createGoods"
/>
</view>
<view class="main-center-root">
<view class="left-bar-root">
<scroll-view
scroll-y
:style="{ height: `calc(100vh - 92rpx - ${statusBarHeight})` }"
>
<left-bar
ref="leftBarRef"
:dafauleData="dafauleData"
@oneMenuClick="oneMenuClick"
/>
</scroll-view>
</view>
<view class="right-center">
<shopping-filter
@updateIsSale="updateIsSale"
@updateTowFilter="updateTowFilter"
:isCheckoutFilter="isCheckoutFilter"
:updateStateOk="updateStateOk"
/>
<scroll-view
scroll-y
:style="{ height: `calc(100vh - 182rpx - ${statusBarHeight})` }"
refresher-enabled
:refresher-triggered="triggered"
@scrolltolower="scrolltolower"
@refresherrefresh="refresherrefresh"
>
<right-main
v-for="item in skuNames"
:key="item.id"
:skuName="item"
:isAudit="isAudit"
:isHot="isHot"
:showMountTab="showMountTab"
@handleSale="handleSale"
@openPriceDialog="openPriceDialog"
@openDialog="openDialog"
@toExamine="toExamine"
/>
<jx-load-more
v-show="totalCount >= 4"
:isLoad="isLoad"
tip="没有更多商品了"
/>
<jx-empty
class="jx-empty"
v-show="totalCount == 0"
title="该分类暂无商品"
>
<view class="createGoods" @tap="createGoods">去创建商品</view>
</jx-empty>
</scroll-view>
</view>
</view>
<view v-if="showMountTab" class="MountWrap">
<view class="btn onActive" @tap="handleChangeSale(1)">可售</view>
<view class="btn onActive" @tap="handleChangeSale(-1)">不可售</view>
</view>
<!-- 修改价格 -->
<uni-popup ref="popup" type="top">
<view class="status_bar"></view>
<view class="jx-popup-update">
<text class="text">{{ isPointStore ? '改价' : '提交改价审核' }}</text>
<view class="tip">
当前价格每<text style="color: #f60d58">{{
skuName.unit === '份' ? '斤' : skuName.unit
}}</text
>:
<jx-price :price="skuName.unitPrice" color="#4eb331" />
</view>
<view class="tip audit" v-if="auditUnitPrice > 0">
正在审核¥<text>{{ auditUnitPrice }}</text>
</view>
<input
class="ipt"
type="digit"
v-model="currentValue"
placeholder="请输入修改价格"
/>
<view class="btn-root">
<view class="btn-esc" @tap="popup.close()">取消</view>
<view class="btn-ok" @tap="handleConfirm">确定修改</view>
</view>
<view class="delete">
<view class="delete-text" @tap="handleDelete"
>删除{{ skuName.name }}</view
>
</view>
</view>
</uni-popup>
<!-- 审核商品 -->
<uni-popup ref="toExaminePopup" type="top">
<view class="status_bar"></view>
<view class="jx-popup-update">
<text class="text">{{
toExamineData.status == 1 ? '同意申请' : '拒绝申请'
}}</text>
<view class="tip audit"
>商户申请价格¥{{ toExamineData.auditPrice / 100 }}</view
>
<input
class="ipt"
type="digit"
v-model="toExamineValue"
:placeholder="
toExamineData.status == 1 ? '请输入审核价格' : '请输入拒绝原因'
"
/>
<view class="btn-root">
<view class="btn-esc" @tap="toExaminePopup.close()">取消</view>
<view class="btn-ok" @tap="toExamineConfirm">确定</view>
</view>
</view>
</uni-popup>
<!-- 修改库存以及商品 -->
<uni-popup ref="popupDialog" type="top">
<view class="jx-popup-update" >
<view class="text">
{{ skuNameItem.type === 'stock' ? '修改库存' : '修改货架码' }}
</view>
<view class="tip-new">{{ skuNameItem.name }}</view>
<view class="ipt-box">
<input
class="ipt"
:type="skuNameItem.type === 'stock' ? 'number' : 'text'"
v-model="stockOrCode"
:placeholder="
skuNameItem.type === 'stock' ? '请输入商品库存' : '请输入商品货架码'
"
/>
</view>
<view class="btn-root">
<view class="btn-esc" @tap="popupDialog.close()">取消</view>
<view class="btn-ok" @tap="sureStockOrCode">确定</view>
</view>
</view>
</uni-popup>
<!-- 公共组件 -->
<jx-loading />
<jx-login-empty title="马上登录,管理商品" />
</template>
<script lang="ts" setup>
import shoppingSearch from './childPages/shopping-search/shopping-search.vue'
import leftBar from './component/left-bar/left-bar.vue'
import rightMain from './childPages/right-main/right-main.vue'
import shoppingFilter from './childPages/shopping-filter/shopping-filter.vue'
import shoppingMangerFn from './main'
const {
dafauleData, // 左侧tablit数据
totalCount, // 商品总条数
skuNames, // 商品列表数据
skuNameItem, // 商品数据2
isAudit, // 商品列表判断条件
isHot, // 商品列表判断条件
showMountTab, // 批量操作判断
scrolltolower, // 页面触底
isLoad, // 加载动画
updateIsSale, // 过滤筛选
updateTowFilter, // 过滤筛选
serachShopping, // 搜索框确定
clearInpu, // 清空搜索框
oneMenuClick, // 一级菜单点击事件
isCheckoutFilter, // 切换过滤选项
isFilter, // 判断条件
handleSale, // 批量可售与不可售
handleChangeSale, // 批量可售与不可售
updateStateOk, // 批量操作成功
createGoods, // 去创建商品
isPointStore, // 判断扣点还是折扣
skuName, // 当前修改的价格
auditUnitPrice, // 正在审核的价格
currentValue, // 当前修改价格
popup, // 弹窗实例
stockOrCode, // stock locationCode
popupDialog, // 弹窗 stock locationCode
handleConfirm, // 确定修改价格
openPriceDialog, // 获取修改数据
openDialog, // 获取修改数据2
sureStockOrCode, // 确认框 stock locationCode
handleDelete, // 删除商品
refresherrefresh, // 下拉刷新
triggered, // 下拉刷新标识
leftBarRef, // 右侧导航栏实例
statusBarHeight, // 状态栏高度
toExamine, // 审核商品
toExamineData, // 审核商品数据
toExaminePopup, // 改价审核实例
toExamineConfirm, // 确定操作审核
toExamineValue, // 审核输入内容
} = shoppingMangerFn()
</script>
<style lang="scss">
page {
background-color: #fff;
}
</style>
<style lang="scss" scoped>
@import './main.scss';
.shopping-cel {
:deep(.uni-searchbar) {
padding: 0 !important;
width: 610rpx !important;
height: 70rpx;
.uni-searchbar__box {
border-radius: 7rpx !important;
height: auto !important;
}
}
}
.main-center-root {
box-sizing: border-box;
display: flex;
justify-content: space-between;
.left-bar-root {
width: 200rpx;
box-shadow: 0rpx 10rpx 10rpx rgb(207, 207, 207);
}
.right-center {
width: calc(100vw - 200rpx);
}
}
</style>