This commit is contained in:
wtq
2025-11-13 14:42:30 +08:00
commit 7d879ff3bc
349 changed files with 54558 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,264 @@
<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>
<!-- skuNameID -->
<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,218 @@
<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-filter"
: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 toast from '@/utils/toast'
import { jx_throttles } from '@/utils/tools'
import { ref, watch } from 'vue'
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 | boolean) {
if (filteActive.value == item.lable) return
fliterFn_throttles(item, status)
}
const fliterFn_throttles = 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,128 @@
<template>
<view class="search-root">
<view class="add-shopping" @tap="createGoods">
<jx-icon icon="jiahao" color="#4eb331"></jx-icon>
<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 shopping from "@/api/https/shopping"
import { getStorage } from "@/utils/storage"
import { onLoad } from "@dcloudio/uni-app"
import { ref } from "vue"
/*************************************************
* 商品分类栏目
*/
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,124 @@
.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(209, 209, 209);
}
.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,710 @@
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 shopping from '@/api/https/shopping'
import merchant from '@/api/https/merchant'
/*************************************************
* 商品管理
*/
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 右侧主体数据
* 获取商品数据
*/
onLoad(async () => {
if (!getStorage('token')) return
store.commit('storeInfo/jxLoadingFn', true)
await getSkuNames()
store.commit('storeInfo/jxLoadingFn', false)
})
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, // 右侧导航栏实例
toExamine, // 审核商品
toExamineData, // 审核商品数据
toExaminePopup, // 改价审核实例
toExamineConfirm, // 确定操作审核
toExamineValue, // 审核输入内容
}
}
export default shoppingMangerFn

View File

@@ -0,0 +1,244 @@
<template>
<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)">
<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)"
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="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="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, // 右侧导航栏实例
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>

189
src/pages/merchant/index.ts Normal file
View File

@@ -0,0 +1,189 @@
import { computed, onBeforeUnmount, ref } from 'vue'
import { timeFormatD } from '@/utils/tools'
import { getStorage, setStorage } from '@/utils/storage'
import { onLoad, onPullDownRefresh } from '@dcloudio/uni-app'
import merchant from '@/api/https/merchant'
import { store } from '@/store'
import useGlobalFunc from '@/composables/useGlobalFunc'
import log from '@/utils/log'
/*************************************************
* 商家中心
*/
const merchantFn = function () {
const { getMtStoreIMStatus } = useGlobalFunc()
/*************************************************
* 下拉刷新
*/
let onPullDownRefreshTimer: any = null
onPullDownRefresh(() => {
clearTimeout(onPullDownRefreshTimer)
onPullDownRefreshTimer = setTimeout(async () => {
if (!getStorage('token')) return
store.commit('storeInfo/jxLoadingFn', true)
await getSaleInfo()
await getStoreVendorMaps(-1)
await getMtStoreIMStatus() // 获取美团门店的IM状态
uni.stopPullDownRefresh()
clearTimeout(onPullDownRefreshTimer)
store.commit('storeInfo/jxLoadingFn', false)
}, 500)
})
/*************************************************
* 页面加载获取数据
*/
const popup = ref<any>(null)
onLoad(async (params) => {
// 订单管理-新订单通知
let logData = {
'日志记录时间': Date(),
'启动类型': '订单管理',
'启动参数': params,
'用户TOKEN': getStorage('token') ? getStorage('token') : '未获取到用户TOKEN',
'用户手机号': getStorage('mobile') ? getStorage('mobile') : '未获取到用户手机号',
'店铺ID': getStorage('storeID') ? getStorage('storeID') : '未获取到店铺ID',
'店铺名字': getStorage('storeName') ? getStorage('storeName') : '未获取到店铺名字',
}
log.info(JSON.stringify(logData))
if (!getStorage('token')) return
store.commit('storeInfo/jxLoadingFn', true)
await getStoreVendorMaps(-1)
await getSaleInfo()
store.commit('storeInfo/jxLoadingFn', false)
if (getStorage('isDownApp') != 'true') {
setTimeout(() => {
popup.value.open()
}, 800)
}
})
const saleInfo = ref({
earningPrice: 0,
count: 0,
})
/*************************************************
* 获取今日营业数据
*/
async function getSaleInfo() {
let fromTime = `${timeFormatD()} 00:00:00`
let toTime = `${timeFormatD()} 23:59:59`
let data = {
storeIDs: `[${parseInt(getStorage('storeID'))}]`,
fromTime,
toTime,
statuss:JSON.stringify([110]) // 只统计已完成的订单,不包含取消单
}
await merchant.get_store_order_sale_info(data).then((res) => {
let json = {
earningPrice: 0,
shopPrice: 0,
realEarningPrice: 0,
count: 0,
actualPayPrice: 0,
actualFee: 0
}
if (res.code == 0) {
let data = res.data || []
data.forEach((item: AnyObject) => {
if (item.status === 110) {
json.actualPayPrice += item.actualPayPrice
json.realEarningPrice += item.realEarningPrice
json.earningPrice += item.earningPrice
json.shopPrice += item.shopPrice
json.count += item.count
json.actualFee += item.actualFee
}
})
json.actualFee = +(json.actualFee / 100).toFixed(2)
saleInfo.value = json
} else {
saleInfo.value = json
}
})
}
/*************************************************
* 获取门店信息数据
*/
const _vendorPayPercentage = ref<number>(0)
const isNotQuote = ref<boolean>(true)
const isZero = ref<boolean>(true)
const isUpperfif = ref<boolean>(false)
const isShowShop = ref<boolean>(false)
async function getStoreVendorMaps(vendorID: number) {
let data = {
storeID: getStorage('storeID'),
vendorID: vendorID
}
let res = await merchant.get_store_vendor_maps(data)
if (res.code == 0) {
let data = res.data
_vendorPayPercentage.value = data[0].vendorPayPercentage
isNotQuote.value = true
isNotQuote.value = data.some((item: AnyObject) => {
return item.vendorPayPercentage != 0 && item.vendorPayPercentage <= 50
})
isZero.value = data.every((item: AnyObject) => {
return item.vendorPayPercentage == 0
})
isUpperfif.value = data.every((item: AnyObject) => {
return item.vendorPayPercentage > 50
})
if (isNotQuote.value) {
isShowShop.value = data.every((v: AnyObject) => {
return v.vendorPayPercentage > 50
})
}
} else {
_vendorPayPercentage.value = 0
}
}
function getMsg() {
uni.removeTabBarBadge({ index: 3 })
store.commit('storeInfo/setIsNewMessage', false)
uni.navigateTo({
url: '/subPages/merchantChild/message/message',
})
}
const isMsg = computed(() => {
return store.state.storeInfo.isNewMessage
})
function closeDownApp() {
setStorage('isDownApp', 'true')
popup.value.close()
}
/*************************************************
* 做收尾工作
*/
onBeforeUnmount(() => {
clearTimeout(onPullDownRefreshTimer)
})
return {
saleInfo, // 今日订单数据
_vendorPayPercentage, // 供应商百分比
isShowShop, // 是否为出售金额
isNotQuote,
isZero,
isUpperfif,
getMsg,
isMsg,
popup,
closeDownApp,
}
}
export default merchantFn

View File

@@ -0,0 +1,156 @@
<template>
<!-- 用户信息 -->
<user-info />
<!-- 实时订单数据 -->
<order-data
:showMore="true"
:saleInfo="saleInfo"
:_vendorPayPercentage="_vendorPayPercentage"
:isShowShop="isShowShop"
:isNotQuote="isNotQuote"
:isZero="isZero"
:isUpperfif="isUpperfif"
:isOut="true"
/>
<!-- 选项内容 -->
<options />
<!-- 新信息 -->
<view class="new-msg" v-if="isMsg" @tap="getMsg">
<image
class="img"
src="https://image.jxc4.com/image/e17593d0abe4daa160a9d67f46732f7b.png"
mode="scaleToFill"
/>
<view class="msg">
<view class="title">新系统消息</view>
<view>请查收您的系统信息</view>
</view>
<view class="btn">立即查看</view>
</view>
<!-- 下载App -->
<uni-popup ref="popup" type="center">
<view class="down-app-root">
<uni-title type="h2" title="菜市商家App上线" align="center" />
<view class="info"
>京西菜市商家版App全新上架系统更加稳定功能更加齐全订单通知更加及时请往手机自带应用商店搜索京西菜市进行安装使用</view
>
<uni-title type="h3" title="苹果搜索【京西菜市】" />
<image
class="img"
src="https://image.jxc4.com/image/a9879811d0bebfa8fd9d25240fccbcde.tem.jpg"
mode="widthFix"
/>
<uni-title type="h3" title="安卓搜索【京西菜市】" />
<image
class="img"
src="https://image.jxc4.com/image/b77cc513274b89da69a9119fef43c24e.tem.jpg"
mode="widthFix"
/>
<view class="btn" @tap="closeDownApp">我知道了</view>
</view>
</uni-popup>
<!-- 公共组件 -->
<jx-loading />
<!-- 未登录无网络 -->
<jx-login-empty title="马上登录,开始使用" />
</template>
<script lang="ts" setup>
import userInfo from './userInfo/userInfo.vue'
import orderData from './orderData/orderData.vue'
import options from './options/options.vue'
import merchantFn from './index'
const {
saleInfo, //今日订单数据
_vendorPayPercentage, // 供应商百分比
isShowShop, // 是否为出售金额
isNotQuote,
isZero,
isUpperfif,
getMsg,
popup,
isMsg,
closeDownApp,
} = merchantFn()
</script>
<style lang="scss" scoped>
.new-msg {
position: fixed;
bottom: 20rpx;
left: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10rpx 20rpx;
width: 710rpx;
overflow: hidden;
background-color: rgba($color: #000000, $alpha: 0.65);
color: #fff;
border-radius: 15rpx;
box-sizing: border-box;
.img {
width: 120rpx;
height: 120rpx;
}
.msg {
.title {
font-weight: bold;
font-size: 35rpx;
}
}
.btn {
padding: 7rpx 22rpx;
border-radius: 50rpx;
background-image: linear-gradient(135deg, #f72a6c, #f16692);
animation: btnANimation 1s ease-in-out infinite alternate;
}
}
@keyframes btnANimation {
0% {
transform: scale(0.95);
}
100% {
transform: scale(1.05);
}
}
.down-app-root {
background-color: #fff;
width: 600rpx;
box-sizing: border-box;
padding: 25rpx 35rpx;
border-radius: 20rpx;
.info {
font-size: 28rpx;
color: rgb(146, 146, 146);
}
.img {
width: 100%;
margin-bottom: 25rpx;
border-radius: 5rpx;
box-shadow: 0 0 5rpx rgb(199, 199, 199);
}
.btn {
background: linear-gradient(0, #7ecf68, #51b535);
color: #fff;
padding: 15rpx 0;
border-radius: 100rpx;
text-align: center;
}
}
</style>

View File

@@ -0,0 +1,106 @@
.options-root {
background-color: #fff;
margin-top: 20rpx;
box-sizing: border-box;
padding: 20rpx;
:deep(.popupAnimation) {
z-index: 1000000;
}
.grid-item-box {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.image {
width: 75rpx;
height: 75rpx;
margin-bottom: 10rpx;
}
.text {
font-size: 26rpx;
}
// ************* 角标内容 ************
// 我的账单
.bill-new {
position: absolute;
top: 15rpx;
right: 15rpx;
background-color: $jx-warring;
color: #fff;
font-size: 24rpx;
padding: 0 15rpx 5rpx 15rpx;
border-radius: 20rpx;
animation: bill-new-animation 0.5s ease-in-out infinite alternate;
}
// 消息通知
.msg-count {
position: absolute;
top: 20rpx;
right: 35rpx;
color: #fff;
background-color: $jx-warring;
font-size: 24rpx;
min-width: 18rpx;
min-height: 20rpx;
line-height: 20rpx;
text-align: center;
line-height: 1;
padding: 4rpx 8rpx 5rpx 8rpx;
border-radius: 100rpx;
}
// 差评管理
.evaluate-number {
position: absolute;
top: 20rpx;
right: 30rpx;
color: #fff;
background-color: $jx-warring;
font-size: 24rpx;
line-height: 1;
padding: 4rpx 8rpx 5rpx 8rpx;
border-radius: 100rpx;
min-width: 30rpx;
text-align: center;
max-height: 35rpx;
}
// 发现新版本
.newVarsion {
position: absolute;
top: 15rpx;
right: 30rpx;
display: inline-block;
background-color: $jx-warring;
width: 30rpx;
height: 30rpx;
border-radius: 50%;
font-size: 24rpx;
line-height: 30rpx;
text-align: center;
color: #fff;
padding: 5rpx;
animation: bill-new-animation 0.5s ease-in-out infinite alternate;
}
}
}
@keyframes bill-new-animation {
0% {
transform: scale(0.95);
}
100% {
transform: scale(1.05);
}
}

View File

@@ -0,0 +1,316 @@
/**
* options 选项
*/
import toast from "@/utils/toast"
import { store } from '@/store'
import { timeFormatD } from "@/utils/tools"
import { getStorage } from "@/utils/storage"
import { onLoad, onPullDownRefresh } from "@dcloudio/uni-app"
import { Ref, ref, onBeforeUnmount,watch } from "vue"
import merchant from '@/api/https/merchant'
import order from "@/api/https/order"
function options() {
/**
* 进入群聊
*/
function getGroupChat() {
uni.navigateTo({
url: '/subPages/merchantChild/enterGroupChat/enterGroupChat',
})
}
/**
* 注册时间
*/
async function getSelfInfo() {
let infoRes = await merchant.get_self_info()
if (infoRes.code == 0) {
let time1 = Math.floor((+new Date() - +new Date(infoRes.data.createdAt)) / 1000 / 24 / 3600)
let time2 = Math.floor((+new Date() - +new Date(store.state.storeInfo.createStoreTimer)) / 1000 / 24 / 3600)
uni.jxAlert({
title: '回首过往',
content: `门店ID:${getStorage('storeID')}\r\n\r\n您注册于${timeFormatD(infoRes.data.createdAt)}\r\n来到京西${time1}\r\n\r\n门店创建于${timeFormatD(store.state.storeInfo.createStoreTimer)}\r\n在平台经营${time2}`
})
} else toast('注册数据异常', 2)
}
/**
* 物料申请
*/
function moveToWM() {
if (store.state.storeInfo.allStoreInfo.packageSwitch === 1) {
let tel = store.state.storeInfo.allStoreInfo.marketManPhone ? store.state.storeInfo.allStoreInfo.marketManPhone : "18048531223"
uni.jxAlert({
title: '物料申请',
content: `非常抱歉,无法进入物料商城,请联系运营${tel}`
})
return
}
if (store.state.storeInfo.storeStatus === -2) {
uni.jxAlert({
title: '物料申请',
content: '门店已被禁用,请联系运营'
})
}
uni.navigateToMiniProgram({
appId: 'wx4b5930c13f8b1170',
path: `pages/index/index?storeID=666666&fromStoreID=${getStorage('storeID')}&storeType=c4`,
success: (res) => {
toast('即将进入京西菜市')
},
fail: (err) => {
toast('取消申请')
},
})
}
/**
* 查询是否有新账单
*/
onLoad(async () => {
await handleMsg()
await tmpGetJxBadCommentsNo()
await getMessageData()
})
// 处理新账单
const billUrl: Ref<string | undefined | object[]> = ref('')
const newBillShow: Ref<boolean> = ref(false) // 是否有新帐单
function handleMsg() {
getStoreBills((url: Array<object> | string) => {
if (typeof (url) == 'string') {
billUrl.value = url
// 保存新账单url地址 存vuex
store.commit('serveInfo/setOrderUrl', url)
// 查询本地账单地址
if (getStorage('newBillUrl') !== billUrl.value) {
// 有新账单
newBillShow.value = true
} else {
// 没有新帐单
newBillShow.value = false
}
}
})
}
// 查询新订单
interface BillListType {
billName: string
billTitle: string
date: string
id: number
shopName: string
storeId: string
url: string
}
const billList = ref<Array<BillListType>>([])
async function getStoreBills(fn: Function) {
let data = {
storeID: getStorage('storeID')
}
let newOrderRes = await merchant.get_store_bills(data)
if (newOrderRes.code == 0) {
let newMsg: Array<object> | string = []
if (newOrderRes.data != null) {
billList.value = newOrderRes.data.slice(0, 5)
billList.value = billList.value.map((bill) => {
let time = bill.date
bill.date = timeFormatD(time)
return bill
})
newMsg = newOrderRes.data[0].url.trim()
fn && fn(newMsg)
} else {
billList.value = []
fn && fn(newMsg)
}
} else {
toast('账单数据异常', 2)
}
}
/**
* 是否有新信息
*/
const msgCount = ref<number>(0)
async function getMessageData() {
let data = {
storeIDs: JSON.stringify([+getStorage('storeID')]),
fromReadCount: 0,
toReadCount: 0,
offset: 0,
pageSize: -1,
fromTime:
timeFormatD(+new Date() - 7 * 24 * 60 * 60 * 1000) + ' 00:00:00',
toTime: timeFormatD(+new Date()) + ' 23:59:59',
}
let msgRes = await merchant.Get_store_message_statuses(data)
if (msgRes.code == 0) {
msgCount.value = msgRes.data.totalCount
} else {
toast('消息数据异常')
}
}
/**
* 获取差评数量
*/
let badCommentCount: Ref<number | string> = ref(0)
async function tmpGetJxBadCommentsNo() {
let data = {
jxStoreId: getStorage('storeID')
}
let evaluateMRes = await merchant.tmp_get_jx_bad_comments_no(data)
if (evaluateMRes.code == 0) {
badCommentCount.value = evaluateMRes.data ? evaluateMRes.data : 0
} else toast('差评数据异常', 2)
}
watch(() => store.getters['storeInfo/imMtStatus'],
(val) => {
if(val && val.length>0) getInvoiceInfo('mt')
})
/**
* 发票数量 美团
*/
const invoiceCount:Ref<number | string> = ref(0)
async function getInvoiceInfo(type:string,ebStore?:any) {
if(type === 'mt'){
// 美团们门店
if(store.getters['storeInfo/imMtStatus'] && store.getters['storeInfo/imMtStatus'].length>0) {
let data:AnyObject = {
storeId: getStorage('storeID'),
startTime: `${timeFormatD(+new Date() - 7 * 24 * 60 * 60 * 1000)}` + ' 00:00:00',
endTime:`${timeFormatD()}` + ' 23:59:59',
pageSize: -1,
offset: 0
}
let res = await order.query_mt_invoice(data)
if(res.code === '0') invoiceCount.value = res.data.totalCount
}
}else if(type === 'eb'){
// console.log('get_invoice_info',ebStore)
// let res = await order.get_invoice_info({
// vendorId:'3',
// vendorStoreID:'' + ebStore.vendorStoreID,
// storeID:+ebStore.storeID
// })
// console.log('饿百数据列表信息',res.data)
// if(res.code === '0') invoiceCount.value = Number(invoiceCount.value) + res.total_count
}
}
/**
* 打开 app or 小程序
*/
function openAppOrApplet() {
uni.jxAlert({
title: '提示',
content: '下载链接复制成功,请到浏览器进行下载,或者到手机应用商店搜索【京西菜市】进行下载!',
success: () => {
uni.setClipboardData({
data: 'https://www.jxc4.com/managerApp/downApp.html'
})
}
})
}
/*************************************************
* 下拉刷新
*/
let onPullDownRefreshTimer: any = null
onPullDownRefresh(() => {
clearTimeout(onPullDownRefreshTimer)
onPullDownRefreshTimer = setTimeout(async () => {
await tmpGetJxBadCommentsNo()
await getMessageData()
await handleMsg()
await getInvoiceInfo('mt')
// await getInvoiceInfo('eb')
uni.stopPullDownRefresh()
}, 1000)
})
/**
* 版本更新
*/
const isVarsion = ref<boolean>(false)
onBeforeUnmount(() => {
clearTimeout(onPullDownRefreshTimer)
})
/*************************************************
* 微信小程序版本更新
*/
function autoUpdate() {
// 获取小程序更新机制兼容
if (uni.canIUse('getUpdateManager')) {
const updateManager = uni.getUpdateManager()
// 检查是否有新版本发布
updateManager.onCheckForUpdate(function (res) {
if (res.hasUpdate) {
isVarsion.value = true
//小程序有新版本,则静默下载新版本,做好更新准备
updateManager.onUpdateReady(function () {
uni.jxAlert({
title: '更新提示',
content: '应用已经升级到新版本了请及时更新',
success: () => {
updateManager.applyUpdate()
}
})
})
// 新的版本下载失败
updateManager.onUpdateFailed(function () {
uni.showModal({
title: '温馨提示',
content: '新版本已经上线,请您删除当前小程序,重新搜索打开',
})
})
} else {
isVarsion.value = false
toast('已经是最新版')
}
})
} else {
// 提示用户在最新版本的客户端上体验
uni.showModal({
title: '温馨提示',
content: '当前微信版本过低,可能无法使用该功能,请升级到最新版本后重试。'
})
}
}
return {
getGroupChat, // 跳转加入群聊
getSelfInfo, // 获取门店来京西的时间
moveToWM, // 物料申请
newBillShow, // 是否有新订单
billList, // 账单列表
msgCount, // 是否有信息
badCommentCount, // 获取差评数量
invoiceCount, // 发票数量
openAppOrApplet, // app or 小程序
isVarsion, // 是否现实新版本
autoUpdate, // 检查微信更新
}
}
export default options

View File

@@ -0,0 +1,280 @@
<template>
<view class="options-root">
<uni-grid :column="4" :highlight="true" :showBorder="false">
<!-- grid -->
<uni-grid-item
v-for="(item, i) in newGrindListData"
:key="i"
@tap="grindData(item.id)"
>
<view class="grid-item-box">
<image :src="item.icon" mode="scaleToFill" class="image" />
<text class="text">{{ item.title }}</text>
<!-- ***** 角标内容 ****** -->
<!-- 我的账单 -->
<text class="bill-new" v-if="item.id == 4 && newBillShow">new</text>
<!-- 信息通知 -->
<text class="msg-count" v-if="item.id == 6 && msgCount > 0">{{ msgCount }}</text>
<!-- 差评管理 -->
<text
class="evaluate-number"
v-if="item.id == 5 && +badCommentCount > 0"
>
{{ +badCommentCount < 99 ? badCommentCount : '99+' }}
</text>
<!-- 发票数量 -->
<text
class="evaluate-number"
v-if="item.id == 22 && +invoiceCount > 0"
>
{{ +invoiceCount < 99 ? invoiceCount : '99+' }}
</text>
<!-- 发现新版本 -->
<text v-if="item.id == 17 && isVarsion" class="newVarsion"></text>
</view>
</uni-grid-item>
</uni-grid>
</view>
</template>
<script lang="ts" setup>
import options from './options'
import { computed, ref } from 'vue'
const {
getGroupChat, // 跳转加入群聊
getSelfInfo, // 获取门店来京西的时间
moveToWM, // 物料申请
newBillShow, // 是否有新订单
msgCount, // 是否有信息
billList, // 账单列表
badCommentCount, // 差评数量
invoiceCount, // 发票数量数量
openAppOrApplet, // app or 小程序
isVarsion, // 是否现实新版本
autoUpdate, // 检查微信更新
} = options()
// 不用展示信息的图标
// 未下载微信不展示
const grindListData = ref([
{
id: 1,
icon: '/static/merchant-icon/1.png',
title: '加入群聊',
},
{
id: 2,
icon: '/static/merchant-icon/2.png',
title: '营业资质',
},
{
id: 3,
icon: '/static/merchant-icon/3.png',
title: '平台管理',
},
{
id: 4,
icon: '/static/merchant-icon/4.png',
title: '我的账单',
},
{
id: 5,
icon: '/static/merchant-icon/5.png',
title: '差评管理',
},
{
id: 6,
icon: '/static/merchant-icon/6.png',
title: '消息通知',
},
{
id: 7,
icon: '/static/merchant-icon/7.png',
title: '物料申请',
},
{
id: 16,
icon: '/static/merchant-icon/8.png',
title: '扫码进店',
},
{
id: 8,
icon: '/static/merchant-icon/9.png',
title: '待配商品',
},
{
id: 9,
icon: '/static/merchant-icon/10.png',
title: '门店评分',
},
{
id: 10,
icon: '/static/merchant-icon/11.png',
title: '帮助中心',
},
{
id: 11,
icon: '/static/merchant-icon/12.png',
title: '官方客服',
},
{
id: 12,
icon: '/static/merchant-icon/13.png',
title: '设置',
},
{
id: 13,
icon: '/static/merchant-icon/14.png',
title: '活动信息',
},
{
id: 14,
icon: '/static/merchant-icon/15.png',
title: '注册时间',
},
{
id: 15,
icon: '/static/merchant-icon/16.png',
title: '下载App',
},
{
id: 18,
icon: '/static/merchant-icon/19.png',
title: '个人信息',
},
{
id: 20,
icon: '/static/merchant-icon/21.png',
title: '收款信息',
},
{
id: 22,
icon: '/static/merchant-icon/22.png',
title: '发票管理',
},
{
id: 19,
icon: '/static/merchant-icon/20.png',
title: '配送余额',
},
{
id: 17,
icon: '/static/merchant-icon/18.png',
title: '检查更新',
},
])
/*************************************************
* 格式化数据,没有下载微信有些数据不展示
*/
const newGrindListData = computed(() => {
return grindListData.value
})
function grindData(title: number) {
switch (title) {
case 1:
getGroupChat() // 加入群聊
break
case 2:
uni.navigateTo({
url: '/subPages/merchantChild/businessLicense/businessLicense',
}) // 营业资质
break
case 3:
uni.navigateTo({
url: '/subPages/merchantChild/platformM/platformM'
}) // 平台管理
break
case 4:
uni.navigateTo({
url: `/subPages/merchantChild/bill/bill?billList=${JSON.stringify(
billList.value
)}`,
}) // 我的账单
break
case 5:
uni.navigateTo({
url: `/subPages/merchantChild/evaluateM/evaluateM?number=${badCommentCount.value}`,
}) // 差评管理
break
case 6:
uni.navigateTo({
url: '/subPages/merchantChild/message/message',
}) // 消息通知
break
case 7:
moveToWM()
break
case 8:
uni.navigateTo({
url: '/subPages/merchantChild/waitGoods/waitGoods',
}) // 待配商品
break
case 9:
uni.navigateTo({
url: '/subPages/merchantChild/storeScore/storeScore',
}) // 门店评分
break
case 10:
uni.navigateTo({
url: '/subPages/merchantChild/helpCenter/helpCenter'
}) // 帮助中心
break
case 11:
uni.navigateTo({
url: '/subPages/orderChild/getPhone/getPhone'
}) // 联系客服
break
case 12:
uni.navigateTo({
url: '/subPages/merchantChild/setUp/setUp'
}) // 设置
break
case 13:
uni.navigateTo({
url: '/subPages/merchantChild/activity/activity'
}) // 活动信息
break
case 14:
getSelfInfo() // 注册时间
break
case 15:
openAppOrApplet() // 打开 app or 小程序
break
case 16:
uni.navigateTo({
url: '/subPages/merchantChild/shareStore/shareStore'
}) // 扫码进店
break
case 17:
autoUpdate()
break
case 18:
uni.navigateTo({
url: '/subPages/merchantChild/useInfo/useInfo'
}) // 个人信息
break
case 19:
uni.navigateTo({
url: '/subPages/merchantChild/accountBalance/accountBalance',
}) // 配送余额
break
case 20:
uni.navigateTo({
url: '/subPages/merchantChild/payeeInfo/payeeInfo'
}) // 收款信息
break
case 22:
uni.navigateTo({
url: '/subPages/merchantChild/invoice/invoice'
}) // 发票信息
break
}
}
</script>
<style lang="scss" scoped>
@import './options.scss';
</style>

View File

@@ -0,0 +1,49 @@
.order-data-root {
box-sizing: border-box;
background-color: #fff;
padding: 20rpx;
}
.title-root {
display: flex;
justify-content: space-between;
margin-bottom: 10rpx;
.see-more {
color: #cfcccc;
}
.text {
margin-right: 15rpx;
}
}
.order-money {
display: flex;
justify-content: space-between;
.money,
.order {
border-right: 1rpx solid #e0e0e0;
}
.money,
.order,
.order1 {
width: 354rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.number {
font-weight: bold;
font-size: 40rpx;
padding: 30rpx 0;
}
}
.afterNumber::before{
content: '-';
}
}

View File

@@ -0,0 +1,151 @@
<template>
<view class="order-data-root" @tap="getOrderRealTime">
<view class="title-root">
<view>
<text class="text">今日完成实时数据</text>
<jx-icon
icon="wenhaoxiao"
color="#cfcccc"
@tap.stop="explain"
></jx-icon>
</view>
<view class="see-more" v-if="showMore">
<text>更多</text>
<jx-icon icon="gengduo" color="#cfcccc"></jx-icon>
</view>
</view>
<view class="order-money">
<view class="money">
<text class="number">{{ price }}</text>
<text>{{ vendorTitle }}</text>
</view>
<view class="order">
<text class="number">{{ count }}</text>
<text>有效订单量</text>
</view>
<view class="order1">
<text class="number" :class="{'afterNumber': +actualFee > 0}">{{ actualFee }}</text>
<text>运费</text>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import jxIcon from '@/components/jx-icon/jx-icon.vue'
import { computed, Ref } from 'vue'
import useGlobalFunc from '@/composables/useGlobalFunc'
import { store } from '@/store'
const { wholeCalcPrice, singleCalcPrice } = useGlobalFunc()
interface propsType {
saleInfo: AnyObject
showMore?: boolean
_vendorPayPercentage: number | string
isNotQuote: boolean
isShowShop?: boolean
isZero: boolean
isUpperfif: boolean
isOut?: boolean
isAllplatfrom?: boolean
}
const props = defineProps<propsType>()
/*************************************************
* 判断门店是否为折扣
*/
const isPointStore = computed(() => {
return store.getters['storeInfo/isPointStore']
})
/*************************************************
* 收入说明
*/
const vendorTitle = computed(() => {
if (isPointStore.value) {
return '实际支付'
} else {
if (props._vendorPayPercentage != 0 && +props._vendorPayPercentage < 50) {
//扣点
return '实际支付'
} else {
//报价
return '实际收入'
}
}
})
/*************************************************
* 订单数量
*/
const count: Ref<number | string> = computed(() => {
return props.saleInfo.count || '0'
})
/*************************************************
* 扣除运费
*/
const actualFee: Ref<number | string> = computed(() => {
return props.saleInfo.actualFee || '0'
})
/*************************************************
* 门店收益
*/
const price = computed(() => {
if (count.value) {
if (props.isAllplatfrom || props.isOut) {
//全平台
let calcData = {
vendorPayPercentage: props._vendorPayPercentage,
actualPayPrice: props.saleInfo.actualPayPrice,
shopPrice: props.saleInfo.shopPrice,
isPointStore: isPointStore.value,
isUpperfif: props.isUpperfif,
isZero: props.isZero,
isNotQuote: props.isNotQuote,
earningPrice: props.saleInfo.earningPrice,
}
return wholeCalcPrice(calcData)
} else {
//单平台
let calcData = {
vendorPayPercentage: props._vendorPayPercentage,
actualPayPrice: props.saleInfo.actualPayPrice,
shopPrice: props.saleInfo.shopPrice,
isPointStore: isPointStore.value,
earningPrice: props.saleInfo.earningPrice,
}
return singleCalcPrice(calcData)
}
} else {
return 0
}
})
/**
* 打开说明窗口
*/
function explain() {
uni.jxAlert({
title: '说明',
content: `实际收入: 从当日0时到当前时间的实际获得不再扣点已扣除售后退款\r\n有效订单: 统计时间内,已支付订单中未被取消的订单数量`,
})
}
/**
* 跳转到营业数据
*/
function getOrderRealTime() {
if (props.showMore != true) return
uni.navigateTo({
url: `/subPages/merchantChild/orderRealTime/orderRealTime?_vendorPayPercentage=${props._vendorPayPercentage}&isShowShop=${props.isShowShop}&isNotQuote=${props.isNotQuote}&isZero=${props.isZero}&isUpperfif=${props.isUpperfif}`,
})
}
</script>
<style lang="scss" scoped>
@import './orderData.scss';
</style>

View File

@@ -0,0 +1,127 @@
.merchant-root {
box-sizing: border-box;
background-color: #4eb331;
position: sticky;
top: 0;
z-index: 1;
.store-timer {
text-align: center;
color: #fcff06;
height: 0rpx;
line-height: 50rpx;
overflow: hidden;
transition: all 0.5s;
.text {
margin-left: -60rpx;
}
}
.store-active {
height: 50rpx;
}
.info-root {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0rpx 20rpx 20rpx 20rpx;
.user-infor-root {
display: flex;
align-items: center;
image {
width: 140rpx;
height: 140rpx;
border-radius: 50%;
background-color: #fff;
border: 4rpx solid #fff;
}
.isHeight {
height: 190rpx;
}
.storeName-style-timer {
display: flex;
flex-direction: column;
color: #fff;
width: 380rpx;
margin-left: 25rpx;
.timer-root {
margin-top: 5rpx;
line-height: 1;
padding: 10rpx 10rpx;
background-color: rgba(0, 0, 0, 0.15);
border-radius: 10rpx;
}
.name,
.state,
.timer-root {
display: inline-block;
max-width: 380rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 10rpx;
}
.state,
.timer {
font-size: 28rpx;
}
.state {
display: flex;
align-items: center;
.tip{
display: flex;
line-height: 46rpx;
.title{
font-size: 24rpx;
}
}
.do-business-root {
display: flex;
align-items: center;
justify-content: center;
background: #3e8f27;
border-radius: 80rpx;
color: #cccccc;
margin-right: 10rpx;
transition: all 0.4s;
.text {
padding: 0 15rpx;
}
}
.business-active {
background-color: #ffad49;
color: #fff;
}
}
}
}
.esc-switchSoter {
width: 152rpx;
button {
font-size: 24rpx;
color: #4eb331;
white-space: nowrap;
}
.swithcStore-btn {
margin-top: 20rpx;
}
}
}
}

View File

@@ -0,0 +1,229 @@
/**
* 用户信息类
*/
import { getStorage, setStorage } from "@/utils/storage";
import { onPullDownRefresh, onShow, onLoad } from "@dcloudio/uni-app";
import { computed, onBeforeUnmount, ref } from "vue";
import { timeFormatD } from "@/utils/tools";
import useGlobalFunc from "@/composables/useGlobalFunc";
import { listenOrder } from '@/utils/bluetoothPrinter/printerOrder'
import { store } from "@/store";
function userInfo() {
const { newMessage, logOutFn, isTxd, getMtStoreIMStatus } = useGlobalFunc()
/**
* 门店自动营业时间
*/
const newAutoEnableAt = ref<string>('')
const switchOpenTime = computed(() => {
if (newAutoEnableAt.value) {
let now = +new Date(timeFormatD(+new Date()));
let time = +new Date(timeFormatD(newAutoEnableAt.value));
let num = time - now;
if (num < 0) {
return "营业时间错误请联系运营";
} else {
let day = num / 1000 / 3600 / 24;
if (day < 1) return "门店将在今天自动营业";
else if (day >= 1 && day < 2) return "门店将在明天自动营业";
else if (day >= 2 && day < 3) return "门店将在后天自动营业";
else return `将在 ${timeFormatD(newAutoEnableAt.value)} 自动营业`;
}
} else {
return "";
}
})
/**
* 下拉刷新
*/
let onPullDownRefreshTimer: any = null
onPullDownRefresh(async () => {
await getStores()
clearTimeout(onPullDownRefreshTimer)
onPullDownRefreshTimer = setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
})
/**
* 用户头像、门店名字
*/
interface StoreInfoType {
avatar: string;
storeName: string;
}
const storeInfo = ref<StoreInfoType>({
avatar: "https://image.jxc4.com/image/70143fcf48aefe74537533f35a0a8153.jpg",
storeName: "",
});
let oldStoreID: any = 0
onShow(async () => {
getStores()
await listenOrder()
storeInfo.value.storeName = getStorage("storeName")
if (oldStoreID != getStorage('storeID') && oldStoreID != 0) {
uni.startPullDownRefresh({})
}
oldStoreID = getStorage('storeID')
});
onLoad(async () => {
await getStores()
await newMessage() // 商家中心有新信息了
})
const businessHours = ref<AnyObject>({})
async function getStores() {
await store.dispatch('storeInfo/getOneStore',getStorage("storeID"))
businessHours.value = store.getters['storeInfo/businessHours'] // 营业时间
newAutoEnableAt.value = store.getters['storeInfo/newAutoEnableAt'] // 手机门店休息时间
await getMtStoreIMStatus() // 获取门店的IM单聊开关状态
await newMessage() // 商家中心有新信息了
await listenOrder() // 小程序监听新订单
}
/*************************************************
* 去修改营业时间
*/
function setTime() {
uni.navigateTo({ url: `/subPages/merchantChild/setBusinessTime/setBusinessTime` })
}
/**
* 退出登录
*/
let goLoginTime: any = null
function goLogin() {
uni.vibrateShort({})
uni.showActionSheet({
title: '退出后不会删除任何数据',
itemColor: '#e70808',
itemList: ['退出登录'],
popover: {
width: 5000,
},
success: function (res) {
logOutFn()
// uni.navigateTo({ url: '/subPages/login/wxLogin/wxLogin' })
uni.reLaunch({ url: '/subPages/login/wxLogin/wxLogin' })
},
fail: () => {
console.log('ZSW-取消退出');
},
complete: () => {
uni.vibrateShort({})
}
});
}
function downApp() {
uni.jxAlert({
title: '提示',
content: '下载链接复制成功,请到浏览器进行下载,或者到手机应用商店搜索【京西菜市】进行下载!',
success: () => {
uni.setClipboardData({
data: 'https://www.jxc4.com/managerApp/downApp.html'
})
}
})
}
/**
* 切换门店
*/
function switchStore() {
uni.navigateTo({ url: "/subPages/switchStore/switchStore" });
// 图片转pdf https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=88093251_52_hao_pg&wd=uniapp%E5%B0%8F%E7%A8%8B%E5%BA%8F%20%E5%A6%82%E4%BD%95%E5%B0%86%E5%9B%BE%E7%89%87%E7%9A%84%E4%B8%B4%E6%97%B6%E6%96%87%E4%BB%B6%E8%BD%AC%E6%88%90pdf%E6%A0%BC%E5%BC%8F&oq=%25E9%2587%258D%25E6%25B8%25A9%25E6%2580%25BB%25E4%25B9%25A6%25E8%25AE%25B0%25E5%25AF%25B9%25E6%25B5%25B7%25E5%258D%2597%25E5%25AF%2584%25E4%25BA%2588%25E7%259A%2584%25E5%258E%259A%25E6%259C%259B&rsv_pq=cd144c82001083ca&rsv_t=4715G1mkyqJr%2FhraQB%2FDsJO62gnA57BCAmiAx1S%2Be4x%2FRVjoViQTJa15XVpww3QSg2jkH32EJe6s&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=25&rsv_n=2&rsv_sug1=18&rsv_sug7=100&rsv_btype=t&inputT=14749&rsv_sug4=14838
// import jsPDF from 'jspdf';
// const ctx = uni.createCanvasContext('myCanvas', this);
// // 假设你有一个图片的临时路径例如从uni.chooseImage获取
// const imagePath = '/path/to/your/image.jpg';
// ctx.drawImage(imagePath, 0, 0, 300, 300); // 绘制图片到Canvas上
// ctx.draw(false, () => {
// // 获取Canvas内容并转换为PDF
// const contentWidth = 300; // Canvas宽度
// const contentHeight = 300; // Canvas高度
// const pdf = new jsPDF('p', 'pt', [contentWidth, contentHeight]); // A4 size page format
// pdf.addImage(imagePath, 'JPEG', 0, 0, contentWidth, contentHeight); // 这里需要确保图片路径正确或者在绘制后使用ctx.toTempFilePath获取图片路径
// pdf.save('converted-image.pdf'); // 保存PDF文件
// });
// ctx.toTempFilePath({
// success: (res) => {
// console.log('图片路径:', res.tempFilePath);
// },
// fail: (err) => {
// console.error('导出失败:', err);
// }
// });
// uni.chooseImage({
// count: 1, // 默认9设置图片的最大数量为1张
// sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有
// sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
// success: function(res) {
// // 获取到图片的本地文件路径列表
// const tempFilePaths = res.tempFilePaths;
// // 获取文件管理器
// console.log('获取临时文件路径',res)
// const fs = uni.getFileSystemManager();
// // 读取文件内容并转换为Base64
// fs.readFile({
// filePath: tempFilePaths[0], // 选择图片返回的本地文件路径列表的第一个路径
// encoding: 'base64', // 指定编码格式为base64
// success: function(res) {
// const base64Data = res.data; // 读取到的Base64数据
// console.log(base64Data,'二进制文件base64格式'); // 输出或使用base64Data
// },
// fail: function(err) {
// console.error('读取文件失败', err);
// }
// });
// // fs.readFile({
// // filePath: tempFilePaths[0], // 第一个图片的路径
// // encoding: 'binary', // 以二进制形式读取
// // success: function(res) {
// // console.log(res,'图片的二进制数据:', res.data); // 这里的res.data就是图片的二进制数据
// // }
// // })
// }
// })
}
/**
* 跳转到设置营业状态页面
*/
function jumpBusinessStatus() {
uni.navigateTo({
url: '/subPages/merchantChild/setBusinessStatus/setBusinessStatus'
})
}
/*************************************************
* 做收尾工作
*/
onBeforeUnmount(() => {
clearTimeout(onPullDownRefreshTimer)
clearTimeout(goLoginTime)
})
return {
storeInfo, // 用户信息
businessHours, // 营业时间段
goLogin, // 退出登录
switchStore, // 切换门店
switchOpenTime, // 门店休息到那天
setTime, // 去修改营业时间
jumpBusinessStatus, // 跳转到营业状态页面
downApp,
store
}
}
export default userInfo

View File

@@ -0,0 +1,97 @@
<template>
<view class="merchant-root">
<view class="store-timer" :class="{ 'store-active': switchOpenTime != '' }">
<text class="text">{{ switchOpenTime }}</text>
</view>
<view class="info-root">
<view class="user-infor-root">
<image
@longtap=""
:src="storeInfo.avatar"
mode="scaleToFill"
@tap="previewImage(storeInfo.avatar)"
/>
<view class="storeName-style-timer">
<view class="name">{{ storeInfo.storeName }}</view>
<view class="state">
<view class="tip" @tap="jumpBusinessStatus">
<view class="title">营业状态</view>
<view
class="do-business-root business-active"
>
<text class="text">{{store.getters['storeInfo/storeStatus'] === 1?'营业中':store.getters['storeInfo/storeStatus'] === 0?'临时休息':store.getters['storeInfo/storeStatus'] === -1?'休息':store.getters['storeInfo/storeStatus'] === -2?'禁用':'未知'}}</text>
<jx-icon icon="shuxie" color="#fff"/>
</view>
</view>
</view>
<!-- 营业时间段 -->
<view
class="timer-root"
v-if="
businessHours.timer1 != '00:00' && businessHours.timer2 != '00:00'
"
@tap="setTime"
>
<view class="timer">
营业时间段{{ businessHours.timer1 }}至{{ businessHours.timer2 }}
<jx-icon icon="shuxie" color="#fff" />
</view>
<view
class="timer"
v-if="
businessHours.timer3 != '00:00' &&
businessHours.timer4 != '00:00'
"
>
营业时间段{{ businessHours.timer3 }}至{{ businessHours.timer4 }}
<jx-icon icon="shuxie" color="#fff" />
</view>
</view>
</view>
</view>
<!-- 退出登录 -->
<view class="esc-switchSoter">
<button class="esc-btn" @tap="goLogin">退出登录</button>
<button class="swithcStore-btn" @tap="switchStore">切换门店</button>
</view>
</view>
<!-- app下载通告栏 -->
<uni-notice-bar
:speed="50"
show-icon
scrollable
background-color="#fcefee"
color="#e70808"
moreColor="#e70808"
showGetMore
@click="downApp"
text="京西菜市商家版App全新上架系统更加稳定功能更加齐全订单通知更加及时请往手机自带应用商店搜索【京西菜市】进行安装使用"
/>
</view>
</template>
<script lang="ts" setup>
import userInfo from './userInfo'
import useGlobalFunc from '@/composables/useGlobalFunc'
const { previewImage } = useGlobalFunc()
const {
storeInfo, // 用户信息
businessHours, // 营业时间段
goLogin, // 退出登录
switchStore, // 切换门店
switchOpenTime, // 门店休息到哪天
setTime, // 去修改营业时间
jumpBusinessStatus, // 跳转到营业状态页面
downApp,
store
} = userInfo()
</script>
<style lang="scss" scoped>
@import './userInfo.scss';
</style>

209
src/pages/message/index.ts Normal file
View File

@@ -0,0 +1,209 @@
import toast from '@/utils/toast'
import { onPullDownRefresh, onShow } from '@dcloudio/uni-app'
import { computed, ref } from 'vue'
import { store } from "@/store"
import { Decrypt, timeFormatDHM } from '@/utils/tools'
import message from '@/api/https/message'
import order from '@/api/https/order'
import useGlobalFunc from "@/composables/useGlobalFunc"
// import { msgInfo } from '@/api/mockData/index'
/*************************************************
* IM消息
*/
const messageFn = function () {
// vuex
const { getMtStoreIMStatus } = useGlobalFunc()
// 用户列表
const userListData = ref<Array<AnyObject>>([])
/*************************************************
* 获取用户列表
*/
onShow(() => {
if (isFirstLogin.value) return
userListData.value = []
getMTUserList()
})
const isFirstLogin = computed(() => {
// 是否没有登陆的状态
return store.state.serveInfo.isFirstLogin
})
/**
* 整理参数信息,避免请求出错
*/
async function dataParams() {
let arr:any = []
let brr = store.getters['storeInfo/platformInfo']
if(brr && brr.length>0){
let mtVendorIDInfo = brr.find((item:AnyObject) => item.vendorID === 1)
let ebVendorIDInfo = brr.find((item:AnyObject) => item.vendorID === 3)
if(mtVendorIDInfo){
arr.push({
vendorStoreID: mtVendorIDInfo.vendorStoreID,
vendorID: "1",
appID: mtVendorIDInfo.vendorOrgCode
})
}
if(ebVendorIDInfo){
arr.push({
vendorStoreID: ebVendorIDInfo.vendorStoreID,
vendorID: "3",
appID: ebVendorIDInfo.vendorOrgCode
})
}
}
return arr
}
// 获取美团IM
async function getMTUserList(time?:number) {
let arr = await dataParams()
if(!(arr && arr.length>0)) return
let data = {
payLoad:JSON.stringify(arr)
}
let venderIDInfo = arr.find((item:AnyObject) => item.vendorID === '1')
let ebStore = arr.find((item:AnyObject) => item.vendorID === '3')
let res = await message.get_IM_user_list(data)
// res = msgInfo.userList // 模拟数据
// console.log('获取用户列表', res)
if (res.code == 0) {
if (JSON.stringify(res.data) === '{}') return
let platformID = venderIDInfo.appID // 美团账号
let newArr: any = []
// let fmtUserList = res.data[`${venderIDInfo.appID}:${venderIDInfo.vendorStoreID}:1`] || [] // 美团
let fmtUserList = venderIDInfo ? res.data[`${venderIDInfo.appID}:${venderIDInfo.vendorStoreID}:1`] || [] : [] // 美团
let febUserList = ebStore && JSON.stringify(ebStore) !== '{}' ? res.data[`${ebStore.appID}:${ebStore.vendorStoreID}:3`] || [] : [] // 饿百
let reverseList = fmtUserList && fmtUserList.length > 0 ? fmtUserList.reverse() : []
let reverseListEb = febUserList && febUserList.length > 0 ? febUserList.reverse() : []
reverseList.map((element: any) => {
let resData = JSON.parse(element)
// 判断是否需要显示列表
if (Decrypt(resData.latestMsg, platformID).indexOf('[自动回复]') == -1) {
let latestMsgHandler = ''
try {
JSON.parse(Decrypt(resData.latestMsg, platformID))
latestMsgHandler = typeof JSON.parse(Decrypt(resData.latestMsg, platformID)) === 'number' ? Decrypt(resData.latestMsg, platformID) : '【订单】'
} catch (error) {
let msg = Decrypt(resData.latestMsg, platformID)
if (msg == '') latestMsgHandler = '【商品】'
else {
if (!(msg.includes('https') || msg.includes('http'))) latestMsgHandler = msg
else latestMsgHandler = msg.indexOf('.amr') == -1 ? '【图片】' : '【语音】'
}
}
// if (resData.userID != 0) {
// 只展示6个小时以内的消息
let userList = {
...resData,
latestMsg: latestMsgHandler,
latestTime: timeFormatDHM(+new Date(resData.latestTime * 1000)),
orderInfo: {},
orderDesc: ''
}
newArr.push(userList)
// 获取订单信息
// if (userList.orderID) getOrderInfo(userList.orderID, index)
if (userList.orderID) getOrderInfo(userList.orderID, newArr.length - 1)
// }
}
})
// 饿百
reverseListEb.map((element: any, index: number) => {
let resData = JSON.parse(element)
let latestMsgHandler = JSON.parse(resData.latestMsg).text ? JSON.parse(resData.latestMsg).text : JSON.parse(resData.latestMsg).duration ? '【语音】' : '【图片】'
// let isAddUserLiset = JSON.parse(resData.latestMsg).data && JSON.parse(JSON.parse(resData.latestMsg).data).title.includes('本次服务已经完成,会话暂停') ? false : true
if (JSON.parse(resData.latestMsg).elements && JSON.parse(resData.latestMsg).elements.length > 0) {
let findItem = JSON.parse(resData.latestMsg).elements.filter((item: { elementType: number }) => item.elementType === 1)
latestMsgHandler = findItem && findItem.length > 0 ? JSON.parse(findItem[0].elementContent).text.replace('@商家', '') : latestMsgHandler
}
// 饿百参考文档https://open-retail.ele.me/#/apidoc/me.ele.retail:im.message.send-3?aopApiCategory=IM_all&type=api_menu resData.userID != 0 && isAddUserLiset
if ((new Date().getTime() - resData.latestTime * 1000) <= (3600 * 1000 * 12 * 1) ) {
let userList = {
...resData,
latestMsg: latestMsgHandler,
latestTime: timeFormatDHM(+new Date(resData.latestTime * 1000)),
orderInfo: {},
orderDesc: ''
}
newArr.push(userList)
// 获取订单信息
// if (userList.orderID) getOrderInfo(userList.orderID, index)
if (userList.orderID) getOrderInfo(userList.orderID, newArr.length - 1 )
}
})
userListData.value = newArr
} else {
toast('获取信息异常')
userListData.value.length == 0
}
}
/**
* 获取订单信息
*/
async function getOrderInfo(orderId: string, index: number) {
let res = await order.get_orders({ vendorOrderID: orderId })
if (res.code === '0') {
userListData.value[index].orderInfo = res.data.data[0]
userListData.value[index].orderDesc = ` #${res.data.data[0].orderSeq}`
}
}
/**
* 下拉刷新
*/
let onPullDownRefreshTimer: any = null
onPullDownRefresh(() => {
clearTimeout(onPullDownRefreshTimer)
onPullDownRefreshTimer = setTimeout(async () => {
getMtStoreIMStatus() // 刷新美团门店IM单聊状态
// 获取用户列表
getMTUserList()
clearTimeout(onPullDownRefreshTimer)
}, 500)
})
/*************************************************
* 进入聊天页面
*/
function charItem(item: AnyObject) {
item.latestMsg = ""
uni.navigateTo({
url: `/subPages/messageChild/msgChat/msgChat?data=${JSON.stringify(item)}`,
})
}
/*************************************************
* 进入设置页面
*/
function jumpToSetUp() {
uni.navigateTo({
url: `/subPages/merchantChild/setUp/setUp`,
})
}
return {
charItem, // 进入聊天页面
userListData, // 用户列表
jumpToSetUp, // 跳到设置页面
store
}
}
export default messageFn

View File

@@ -0,0 +1,76 @@
<template>
<!-- 美团授权已过期 -->
<!-- <jx-empty v-if="imOnlineStatus === -1" title="美团授权已过期" /> -->
<!-- IM状态 -->
<view v-if="store.getters['storeInfo/imStatus']" style="position:absolute;bottom:0;width:100%">
<view class="notice" @tap="jumpToSetUp">
<text>{{store.getters['storeInfo/imStatus']}}IM单聊状态未设置</text>
<jx-icon icon="gengduo" color="red"></jx-icon>
</view>
</view>
<!-- v-else -->
<!-- <template > -->
<!-- 用户列表 -->
<template v-if="userListData.length > 0">
<uni-list :border="false">
<view class="chat-border" v-for="item in userListData" :key="item.userID">
<uni-list-chat
:clickable="true"
@click="charItem(item)"
:avatar-circle="true"
:title="
item.vendorID == 1
? `【美团${item.orderDesc}】${item.userID === '0' ? '群发消息' : item.userID}`
: `【饿了么】${item.userID}`
"
:avatar="
item.vendorID == 1
? 'https://image.jxc4.com/image/75654ab606494a0efdb0cf7d7ad060d9.png'
: 'https://image.jxc4.com/image/6c2f1dfd95890df8ef5e27bde15c4e7f.png'
"
:note="item.latestMsg"
:time="item.latestTime"
badge-positon="left"
:badge-text="item.NewMessageNum"
/>
</view>
</uni-list>
</template>
<jx-empty v-else title="暂无消息" />
<!-- </template> -->
<!-- 公共组件 -->
<jx-login-empty title="马上登录,查看消息" />
<jx-loading />
</template>
<script lang="ts" setup >
import messageFn from './index'
const {
charItem, // 聊天详情
userListData, // 用户列表
jumpToSetUp, // 跳到设置页面
store
} = messageFn()
</script>
<style lang="scss" scoped>
.chat-border:nth-child(1) {
border-top: 2rpx solid rgb(230, 230, 230);
}
.chat-border {
border-bottom: 2rpx solid rgb(230, 230, 230);
}
.notice {
display: flex;
justify-content: space-between;
box-sizing: border-box;
width: 100%;
padding: 20rpx;
background-color: #fef5f6;
color: #e91d37;
font-size: 28rpx;
}
</style>

View File

@@ -0,0 +1,76 @@
.position-relative {
position: relative;
}
.abnormal-order {
background-color: #fff;
padding-bottom: 1rpx;
.address {
display: flex;
justify-content: space-between;
padding: 15rpx;
font-size: 32rpx;
border-bottom: 2rpx dashed #e7e7e7;
color: #999999;
font-size: 28rpx;
}
.abnormal-op {
height: 90rpx;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 32rpx;
padding: 0 20rpx;
background: linear-gradient(to right, #e8edd8, white);
.title {
color: #333;
font-weight: bold;
}
.op {
display: flex;
align-items: center;
.btn-cancel,
.btn-confirm {
color: #fff;
background-color: $jx-primary;
padding: 7rpx 30rpx;
border-radius: 7rpx;
}
.btn-cancel {
margin-right: 40rpx;
background-color: #F60D58;
}
}
}
.abnormal-dealed {
height: 90rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
background: white;
color: #333;
text {
font-weight: bold;
display: inline-block;
margin-left: 10rpx;
}
.agree {
color: green;
}
.refuse {
color: red;
}
}
}

View File

@@ -0,0 +1,80 @@
<template>
<view
class="abnormal-order position-relative"
@tap="orderDetail(item.vendorOrderID, item.vendorID)"
>
<order-list-top :item="item" />
<order-money :item="item" />
<!-- 地址 -->
<view class="address">地址{{ item.consigneeAddress }}</view>
<view v-if="item.lockStatus === -5">
<view class="abnormal-op" @tap.stop v-if="(item.flag & 6) === 0">
<view class="title">用户申请取消订单</view>
<view class="op">
<view class="btn-cancel" @tap.stop="fnCancelOrder(0)">拒绝</view>
<view class="btn-confirm" @tap.stop="fnCancelOrder(1)">同意</view>
</view>
</view>
<view class="abnormal-dealed" v-if="(item.flag & 6) !== 0">
用户申请取消订单
<text class="agree" v-if="(item.flag & 6) === 2">[已同意]</text>
<text class="refuse" v-if="(item.flag & 6) === 6">[已拒绝]</text>
</view>
</view>
<template v-if="item.status === 17 && item.lockStatus === 0">
<bottom-component
title="骑手申请取消配送"
@handleCancel="fnCancelDeliver(0)"
@handleConfirm="fnCancelDeliver(1)"
confirmText="同意"
:order="item"
/>
</template>
<template v-if="item.status === 18 && item.lockStatus === 0">
<bottom-component
title="骑手取货失败"
@handleConfirm="fnCallNew"
confirmText="重新召唤骑手"
:order="item"
/>
</template>
<template v-if="item.status === 22 && item.lockStatus === 0">
<bottom-component
title="用户拒收"
@handleConfirm="fnGoodsReturn()"
confirmText="确认退回"
:order="item"
/>
</template>
</view>
</template>
<script lang="ts" setup >
import orderListTop from '../component/orderListTop.vue'
import orderMoney from '../component/orderMoney.vue'
import bottomComponent from './bottom-component.vue'
import abonmrmalFn from './abonmrmal-order'
import useOrderInfo from '@/composables/useOrderInfo'
interface PropsType {
item: AnyObject
}
const props = defineProps<PropsType>()
const {
fnCancelOrder, // 是否统一用户取消订单
fnCancelDeliver, // 骑手申请取消
fnCallNew, // 重新召唤骑手
fnGoodsReturn, // 是否退回商品
} = abonmrmalFn(props)
const {
orderDetail, // 订单详情
} = useOrderInfo()
</script>
<style lang="scss" scoped>
@import './abnormal-order.scss';
</style>

View File

@@ -0,0 +1,158 @@
import { store } from "@/store"
import toast from "@/utils/toast"
import order from '@/api/https/order'
/*************************************************
* 异常订单处理
*/
function abonmrmalFn(props: { item: AnyObject }) {
// 是否拒绝用户退款
function fnCancelOrder(num: number) {
let data = {
vendorOrderID: props.item.vendorOrderID,
vendorID: props.item.vendorID,
acceptIt: Boolean(num),
reason: '理由暂无'
}
// let data = {
// afsOrderID: props.item.afsOrderID,
// vendorID: props.item.vendorID,
// approveType: num,
// reason: '理由暂无'
// }
// 拒绝退款
if (num == 0) {
uni.jxConfirm({
title: '提示',
content: '是否拒绝用户取消该订单',
success: async () => {
let res = await order.agree_or_refuse_cancel(data)
// let res = await order.agree_orRefuse_refund(data)
if (res.code == 0) {
toast('拒绝成功', 1)
store.commit('serveInfo/setUpdateOrder', Date.now())
} else {
toast('决绝失败', 2)
}
}
})
}
// 同意退款
if (num == 1) {
uni.jxConfirm({
title: '提示',
content: '是否同意用户取消该订单',
success: async () => {
let res = await order.agree_or_refuse_cancel(data)
// let res = await order.agree_orRefuse_refund(data)
if (res.code == 0) {
toast('同意成功', 1)
store.commit('serveInfo/setUpdateOrder', Date.now())
} else {
toast('同意失败', 2)
}
}
})
}
}
/*************************************************
* 骑手申请取消
*/
function fnCancelDeliver(num: number) {
let data = {
vendorOrderID: props.item.vendorOrderID,
vendorID: props.item.vendorID,
acceptIt: Boolean(num),
reason: '理由暂无'
}
//
if (num == 0) {
uni.jxConfirm({
title: '提示',
content: '拒绝后只能由该配送员完成本订单',
success: async () => {
let res = await order.accept_or_refuse_failed_get_order(data)
if (res.code == 0) {
store.commit('serveInfo/setUpdateOrder', Date.now())
fnCallNew()
} else {
toast('操作失败', 2)
}
}
})
}
if (num == 1) {
uni.jxConfirm({
title: '提示',
content: '是否同意取消该订单配送',
success: async () => {
let res = await order.accept_or_refuse_failed_get_order(data)
if (res.code == 0) {
store.commit('serveInfo/setUpdateOrder', Date.now())
fnCallNew()
} else {
toast('操作失败', 2)
}
}
})
}
}
// 重新召唤骑手
async function fnCallNew() {
let data = {
vendorOrderID: props.item.vendorOrderID,
vendorID: props.item.vendorID
}
uni.jxConfirm({
title: '提示',
content: '是否重新召唤骑手进行配送',
success: async () => {
let res = await order.callP_m_courier(data)
if (res.code == 0) {
toast('操作成功')
store.commit('serveInfo/setUpdateOrder', Date.now())
} else {
toast('操作失败')
}
}
})
}
/*************************************************
* 是否收到退货
*/
function fnGoodsReturn() {
let data = {
vendorOrderID: props.item.vendorOrderID,
vendorID: props.item.vendorID,
}
uni.jxConfirm({
title: '提示',
content: '确认已经收到退回的货物',
success: async () => {
let res = await order.confirm_receive_goods(data)
if (res.code == 0) {
toast('操作成功')
store.commit('serveInfo/setUpdateOrder', Date.now())
} else {
toast('操作失败')
}
}
})
}
return {
fnCancelOrder, // 是否统一用户取消订单
fnCancelDeliver, // 骑手申请取消
fnCallNew, // 重新召唤骑手
fnGoodsReturn, // 是否退回商品
}
}
export default abonmrmalFn

View File

@@ -0,0 +1,124 @@
<template>
<view>
<view
class="abnormal-op"
v-if="
(order.status === 17 && (order.flag & 24) === 0) ||
(order.status === 22 && (order.flag & 32) === 0) ||
(order.status === 18 && (order.flag & 64) === 0)
"
>
<view class="title">{{ title }}</view>
<view class="op">
<view class="btn-confirm" @click.stop="handleConfirm">{{
confirmText
}}</view>
</view>
</view>
<view
class="abnormal-dealed"
v-if="
(order.status === 17 && (order.flag & 24) !== 0) ||
(order.status === 22 && (order.flag & 32) !== 0) ||
(order.status === 18 && (order.flag & 64) !== 0)
"
>
{{ title }}
<text class="agree" v-if="(order.flag & 24) === 8">[已同意]</text>
<text class="refuse" v-if="(order.flag & 24) === 24"
>[已拒绝]请联系:18048531223</text
>
<text
class="agree"
v-if="(order.flag & 32) === 32 || (order.flag & 64) === 64"
>[已发单]</text
>
<text><slot></slot></text>
</view>
</view>
</template>
<script lang="ts" setup>
interface propsType {
title: string
confirmText: string
order: AnyObject
}
defineProps<propsType>()
const emits = defineEmits<{
(e: 'handleCancel'): void
(e: 'handleConfirm'): void
}>()
// 点击取消
function handleCancel() {
emits('handleCancel')
}
// 点击确定
function handleConfirm() {
emits('handleConfirm')
}
</script>
<style lang="scss" scoped>
.abnormal-op {
height: 90rpx;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 32rpx;
padding: 0 20rpx;
background: linear-gradient(to right, #e8edd8, white);
.title {
color: #333;
font-weight: bold;
}
.op {
display: flex;
align-items: center;
.btn-cancel,
.btn-confirm {
color: #fff;
background-color: $jx-primary;
padding: 7rpx 30rpx;
border-radius: 7rpx;
}
.btn-cancel {
margin-right: 40rpx;
background-color: #f60d58;
}
}
}
.abnormal-dealed {
height: 90rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
background: white;
color: #333;
text {
font-weight: bold;
display: inline-block;
margin-left: 10rpx;
}
.agree {
color: green;
}
.refuse {
color: red;
}
}
</style>

View File

@@ -0,0 +1,199 @@
%globalStyle {
border-bottom: 2rpx dashed #e7e7e7;
padding: 15rpx;
}
.position-relative {
position: relative;
}
.after-sales-root {
background-color: #fff;
.top-title {
@extend %globalStyle;
border-bottom: 6rpx dashed #e7e7e7;
display: flex;
justify-content: space-between;
font-size: 30rpx;
padding: 25rpx 15rpx;
.order {
color: #000;
}
.success {
color: $jx-primary;
}
.warring {
color: $jx-warring;
}
}
.platform {
display: flex;
align-items: center;
@extend %globalStyle;
.brand {
display: inline-block;
width: 40rpx;
height: 40rpx;
flex-shrink: 0;
background-size: 100%;
background-repeat: no-repeat;
border-radius: 15rpx;
}
.orderSeq{
color: #999;
}
.num {
font-size: 30rpx;
color: #999;
margin-left: 15rpx;
}
}
.detaile-text {
@extend %globalStyle;
color: $jx-warring;
background-image: linear-gradient(90deg, #fffefd, #fceceb);
}
.after-text {
@extend %globalStyle;
font-size: 34rpx;
color: #666;
.mode {
font-weight: bold;
}
}
.created-time {
@extend %globalStyle;
font-size: 32rpx;
color: #999;
}
.shopping-count {
@extend %globalStyle;
font-size: 32rpx;
color: #999;
}
.detail-btn {
box-sizing: border-box;
padding: 20rpx;
margin: 15rpx;
text-align: center;
color: #fff;
background-image: linear-gradient(135deg, #4eb331, #85c972);
border-radius: 10rpx;
}
.tips-text {
text-align: center;
color: #f72a6c;
text {
display: inline-block;
padding: 15rpx;
}
}
.operation-btn {
@extend %globalStyle;
.text {
color: #f72a6c;
font-size: 24rpx;
width: 100%;
}
.btn-root {
display: flex;
justify-content: space-between;
box-sizing: border-box;
.reject,
.resolve {
width: 100%;
padding: 20rpx;
text-align: center;
border-radius: 10rpx;
margin-top: 15rpx;
color: #fff;
}
.reject {
margin-right: 15rpx;
background-image: linear-gradient(135deg, #85c972, $jx-primary);
}
.resolve {
margin-left: 15rpx;
background-image: linear-gradient(135deg, #ff6a20, #ec903f);
}
}
}
.confirm-goods {
@extend %globalStyle;
.text {
padding: 20rpx;
text-align: center;
background-image: linear-gradient(135deg, #f72a6c, #f16692);
border-radius: 10rpx;
color: #fff;
}
}
}
.reson-root {
padding: 15rpx;
background-color: #fff;
width: 600rpx;
border-radius: 10rpx;
.title {
text-align: center;
}
.textarea {
box-sizing: border-box;
margin: 15rpx 0;
width: 100%;
max-height: 200rpx;
background-color: rgb(243, 243, 243);
padding: 10rpx;
border-radius: 10rpx;
}
.btn-root {
width: 100%;
display: flex;
justify-content: space-between;
border-top: 1rpx solid rgb(223, 223, 223);
.cancal,
.confirm {
width: 100%;
padding: 20rpx 0 10rpx 0;
text-align: center;
}
.cancal {
color: #666;
}
.confirm {
border-left: 1rpx solid rgb(223, 223, 223);
}
}
}

View File

@@ -0,0 +1,285 @@
<template>
<view
class="after-sales-root position-relative"
@tap="afterSalesDetaile(item.afsOrderID)"
>
<!-- 订单号 -->
<view class="top-title">
<view
class="order"
@tap.stop="copyInfo(item.afsOrderID, '售后单号复制成功')"
>
<text>售后单号{{ item.afsOrderID }}</text>
</view>
<text :class="item.status == 180 ? 'success' : 'warring'">{{
status
}}</text>
</view>
<!-- 订单平台 -->
<view
class="platform"
@tap.stop="copyInfo(item.vendorOrderID, '订单号复制成功')"
>
<text class="brand" :class="`icon-${item.vendorID}`"></text>
<text class="orderSeq">#{{ item.orderSeq }}</text>
<text class="num">订单号{{ item.vendorOrderID }}</text>
<jx-icon icon="fuzhi" color="#818181"></jx-icon>
</view>
<!-- 原因描述 -->
<view class="detaile-text">{{ item.reasonDesc || '用户未评价' }}</view>
<!-- 售后方式 -->
<view class="after-text">
<text>售后方式</text>
<text class="mode">{{ afsAppealTypeName }}</text>
</view>
<!-- 申请时间 -->
<view class="created-time"
>申请时间{{ timeFormatHMS(item.afsCreatedAt) || '无' }}</view
>
<!-- 商品数量 -->
<view class="shopping-count" v-if="item.skus != null"
>{{ item.skus.length || 0 }}&nbsp;{{ count }}件商品</view
>
<!-- 驳回理由 -->
<view class="shopping-count" v-if="item.refuseReason">
<text>驳回理由:&emsp;{{ item.refuseReason }}</text>
</view>
<!-- 操作按钮 -->
<view
class="operation-btn"
v-if="item.status === 155 && (item.flag & 3) === 0"
>
<text class="text"
>若退款造成的损失较大建议联系顾客自行上门取回退货</text
>
<view class="btn-root">
<view class="reject" @tap.stop="popup.open()">驳回</view>
<view class="resolve" @tap.stop="handleAgree">同意</view>
</view>
</view>
<!-- 商家确认收到货 -->
<view
class="confirm-goods"
v-if="item.status === 165 && (item.flag & 4) === 0"
>
<view class="text" @tap.stop="confirmGoods">商家确认收货</view>
</view>
<!-- 查看详情 -->
<view class="detail-btn">查看详情</view>
<!-- 提示内容 -->
<view class="tips-text">
<!-- 已进行审核操作 -->
<template v-if="item.status === 155 && (item.flag & 3) !== 0">
<text v-if="(item.flag & 3) === 1">同意售后</text>
<text v-if="(item.flag & 3) === 3">驳回售后</text>
</template>
<!-- 确认收货 -->
<template v-if="item.status === 165 && (item.flag & 4) !== 0">
<text>已确认收货</text>
</template>
</view>
</view>
<uni-popup ref="popup" type="center">
<view class="reson-root">
<view class="title">驳回原因</view>
<textarea
class="textarea"
v-model.trim="reason"
placeholder="请输入驳回原因"
></textarea>
<view class="btn-root">
<view class="cancal" @tap="cancel">取消</view>
<view class="confirm" @tap="handleRefuse">确定</view>
</view>
</view>
</uni-popup>
</template>
<script lang="ts" setup>
import useOrderInfo from '@/composables/useOrderInfo'
import toast from '@/utils/toast'
import { timeFormatHMS } from '@/utils/tools'
import { computed, ref } from 'vue'
import { store } from '@/store'
import order from '@/api/https/order'
const {
copyInfo, // 复制内容
afterSalesDetaile, // 售后详情
} = useOrderInfo()
interface PropsType {
item: any
}
const props = defineProps<PropsType>()
/**
* 售后状态
*/
const status = computed(() => {
switch (props.item.status) {
case 155:
return '待审核'
case 160:
return '已审核'
case 165:
return '退货待确认'
case 167:
return '退货已收到'
case 180:
return '售后成功'
case 190:
return '售后失败'
default:
return '未知'
}
})
/**
* 售后方式
*/
const afsAppealTypeName = computed(() => {
switch (props.item.appealType) {
case 1:
return '仅退款'
case 2:
return '退货退款'
case 3:
return '重发商品'
default:
return '未知'
}
})
/**
* 商品数量
*/
const count = computed(() => {
let num = 0
props.item.skus.forEach((item: any) => {
num += item.count
})
return num
})
/**
* 驳回售后
*/
const popup = ref<any>(null)
const reason = ref<string>('')
async function agreeOrRefuse(type: number) {
// if (props.item.vendorID == 3) {
// // 饿百订单
// if (type === 1) {
// // 同意
// let data = {
// vendorOrderID: props.item.vendorOrderID,
// vendorID: props.item.vendorID,
// acceptIt: Boolean(1), // 拒绝为0同意为1
// reason: type === 1 ? '同意退款' : reason.value,
// }
// let res = await order.agree_or_refuse_cancel(data)
// judge(res)
// } else {
// // type ===3 驳回
// let data = {
// vendorOrderID: props.item.vendorOrderID,
// vendorID: props.item.vendorID,
// acceptIt: Boolean(1), // 拒绝为0同意为1
// reason: type === 1 ? '同意退款' : reason.value,
// }
// let res = await order.agree_or_refuse_cancel(data)
// judge(res)
// }
// } else {
// // 非饿百订单
// 退货退款
let data = {
afsOrderID: props.item.afsOrderID,
vendorID: props.item.vendorID,
approveType: type,
reason: type === 1 ? '同意退款' : reason.value,
}
let res = await order.agree_orRefuse_refund(data)
judge(res)
// }
}
async function handleRefuse() {
if (reason.value) {
agreeOrRefuse(3)
} else {
toast('请输入驳回原因')
}
}
/**
* 判断驳回信息
*/
function judge(res: AnyObject) {
if (res.code == 0) {
toast('操作成功', 1)
cancel()
// 刷新订单
store.commit('serveInfo/setUpdateOrder', Date.now())
} else {
uni.jxAlert({
title: '错误',
content: res.desc,
})
}
}
/**
* 关闭驳回弹窗
*/
function cancel() {
reason.value = ''
popup.value.close()
}
/**
* 同意售后
*/
function handleAgree() {
uni.jxConfirm({
title: '提示',
content: '是否同意处理该售后单',
success: () => {
agreeOrRefuse(1)
},
})
}
/**
* 商家确认收货
*/
function confirmGoods() {
uni.jxConfirm({
title: '提示',
content: '是否已经收到货',
success: async () => {
let data = {
afsOrderID: props.item.afsOrderID,
vendorID: props.item.vendorID,
}
let res = await order.confirm_received_return_goods(data)
judge(res)
},
})
}
</script>
<style lang="scss" scoped>
@import './after-sales-order.scss';
</style>

View File

@@ -0,0 +1,120 @@
<template>
<view
class="compelet-root position-relative"
@tap="orderDetail(item.vendorOrderID, item.vendorID)"
>
<order-list-top :item="item" />
<order-money :item="item" />
<!-- 地址 -->
<view class="address">地址{{ item.buyerComment.includes('支付方式') ? store.getters['storeInfo/storeAddress'] : item.consigneeAddress }}</view>
<!-- 配送信息 -->
<view class="distribution-msg" v-if="!item.buyerComment.includes('支付方式')">
<text class="vender-name">{{ wbVendor }}</text>
<text class="distribution-name">{{ item.courierName }}</text>
<text
v-if="item.courierMobile.split(',')[1]"
class="distribution-mobile"
@tap.stop="phoneCall(item.courierMobile)"
>{{
item.courierMobile.split(',')[0] +
'转' +
item.courierMobile.split(',')[1]
}}</text
>
<text
v-else
class="distribution-mobile"
@tap.stop="phoneCall(item.courierMobile)"
>{{ item.courierMobile }}</text
>
<jx-icon
v-show="item.courierMobile != ''"
icon="24gf-telephone"
color="#999999"
@tap.stop="phoneCall(item.courierMobile)"
:size="38"
style="margin-left: 10rpx"
/>
</view>
<!-- 查看详情 -->
<view class="detail-btn">查看详情</view>
</view>
</template>
<script lang="ts" setup>
import useOrderInfo from '@/composables/useOrderInfo'
import { computed } from 'vue'
import orderListTop from '../component/orderListTop.vue'
import orderMoney from '../component/orderMoney.vue'
import { store } from '@/store'
const {
orderDetail, // 订单详情
phoneCall, // 复制手机号
waybillVendor, // 转换厂商
} = useOrderInfo()
interface PropsType {
item: any
}
const props = defineProps<PropsType>()
/**
* 转换快递厂商
*/
const wbVendor = computed(() => {
return waybillVendor(
props.item.waybillVendorID,
props.item.status,
props.item.waybillStatus
)
})
</script>
<style lang="scss" scoped>
.position-relative {
position: relative;
}
.compelet-root {
background-color: #fff;
padding-bottom: 1rpx;
.address {
display: flex;
justify-content: space-between;
padding: 15rpx;
font-size: 32rpx;
color: #000000;
background-color: rgba($color: #908f96, $alpha: 0.15);
border-radius: 10rpx;
}
.detail-btn {
background-color: #fff;
padding: 20rpx;
text-align: center;
color: #fff;
background-image: linear-gradient(135deg, #4eb331, #85c972);
margin: 15rpx;
border-radius: 10rpx;
}
.distribution-msg {
padding: 15rpx;
border-bottom: 2rpx dashed #e7e7e7;
.vender-name {
color: #999999;
margin-right: 25rpx;
}
.distribution-name {
margin-right: 25rpx;
}
.distribution-mobile {
color: #999999;
}
}
}
</style>

View File

@@ -0,0 +1,514 @@
<template>
<!-- 订单信息 -->
<view class="order-info">
<view class="order">
<view class="h-number index-color">
<text class="brand" :class="`icon-${item.vendorID}`"></text>
<text class="num-root">
<text>#</text>
<text class="num">{{ item.orderSeq }}</text>
</text>
</view>
<view class="timer" v-if="!item.buyerComment.includes('支付方式')">预计{{ expectedDeliveredTime }}前送达</view>
</view>
<view v-if="isSrestPickTime" v-html="restPickTime"></view>
<view v-if="isCancelTime">
<view v-if="item.lockStatus === -5 && (item.flag & 6) === 0">
<text class="rest-time canceltime"
>{{ cancelTime
}}<text v-if="cancelTime !== '已超时'">分钟</text></text
>
</view>
<text class="order-delivered">{{ abnormalStatus }}</text>
</view>
<!-- 状态展示 -->
<text class="compelet" v-if="item.status === 110">已完成</text>
<text class="cancel" v-else-if="item.status === 115">已取消</text>
<text class="fail" v-else-if="item.status === 120">已失败</text>
</view>
<!-- 用户名字 -->
<view class="order-title" v-if="!item.buyerComment.includes('支付方式')">
<!-- <view class="title-left" > -->
<view >
<text class="name">{{ item.consigneeName.slice(0, 1) }}...</text>
<!-- <text class="name">{{ item.consigneeName }}</text> -->
<!-- <img src="https://image.jxc4.com/jxapp/z_send_normal.png" alt="" class="connectUser" @tap.stop="connectUser(item)"> -->
</view>
<!-- class="connectUser" -->
<!-- <view style="width: 38rpx;height: 38rpx;"><img src="https://image.jxc4.com/jxapp/callPhone.png" alt="" style="width: 38rpx;height: 38rpx;" @tap.stop="connectUser(item)"></view> -->
<view class="see-map" @tap.stop="callPhone(item)">拨打电话</view>
<!-- <view style="width: 38rpx;height: 38rpx;"><img src="https://image.jxc4.com/jxapp/sendMessage.png" alt="" style="width: 38rpx;height: 38rpx;" @tap.stop="connectUser(item)"></view> -->
<view class="see-map" @tap.stop="connectUser(item)">发消息</view>
<!-- <view class="phone">
<view>
<text v-if="item.consigneeMobile">临1</text>
<text
class="phone-number"
@tap.stop="phoneCall(item.consigneeMobile)"
v-if="item.consigneeMobile.split(',')[1]"
>{{
item.consigneeMobile.split(',')[0] +
'转' +
item.consigneeMobile.split(',')[1]
}}</text
>
<text
v-else
class="phone-number"
@tap.stop="phoneCall(item.consigneeMobile)"
>{{ item.consigneeMobile }}</text
>
<br />
真实号
<text v-if="item.consigneeMobile2">临2</text>
<text
class="phone-number"
@tap.stop="phoneCall(item.consigneeMobile2)"
v-if="item.consigneeMobile2.split(',')[1]"
>{{
item.consigneeMobile2.split(',')[0] +
'转' +
item.consigneeMobile2.split(',')[1]
}}</text
>
<text
v-else
:class="item.consigneeMobile2 ? 'phone-number' : ''"
@tap.stop="phoneCall(item.consigneeMobile2)"
>{{ item.consigneeMobile2 }}</text
>
</view> -->
<!-- </view> -->
<!-- </view> -->
<!-- style="height:25px;line-height:25px;margin:auto 10px;" -->
<view class="see-map" @tap.stop="seeMap">查看地图</view>
</view>
<!-- 订单号 -->
<view class="order-number" @tap.stop="copyInfo(orderNum, '复制订单号成功')">
<text class="number">订单号{{ orderNum }}</text>
<text class="copy">复制</text>
</view>
</template>
<script lang="ts" setup>
import useOrderInfo from '@/composables/useOrderInfo'
import { store } from '@/store'
import { isNullDate } from '@/utils/tools'
import { onShow } from '@dcloudio/uni-app'
import { computed, ref } from 'vue'
import { setStorage } from "@/utils/storage";
import toast from "@/utils/toast";
const {
copyInfo, // 复制内容
phoneCall, // 拨号
} = useOrderInfo()
interface PropsType {
item: any
}
let props = defineProps<PropsType>()
/**
* 拨打电话
*/
function callPhone(item:AnyObject) {
// let itemList = [item.consigneeMobile,item.consigneeMobile2]
let itemList:string[] = []
if(item.consigneeMobile) itemList.push('临1' + item.consigneeMobile.split(',')[0] +'转' + item.consigneeMobile.split(',')[1])
if(item.consigneeMobile2) itemList.push('临2' + item.consigneeMobile2.split(',')[0] +'转' + item.consigneeMobile2.split(',')[1])
if(itemList.length === 0) return toast('该订单暂无联系方式', 2)
uni.showActionSheet({
// title: '退出后不会删除任何数据',
itemColor: '#4eb331',
itemList,
popover: {
width: 5000,
},
success: function (res) {
if(res.tapIndex === 0) phoneCall(item.consigneeMobile)
if(res.tapIndex === 1) phoneCall(item.consigneeMobile2)
}
});
}
/*************************************************
* 联系用户
*/
function connectUser(item:AnyObject) {
if(item.vendorID === 1){
if(store.getters['storeInfo/imMtStatus'][0].imStatus !== 1) return toast('美团IM状态已关闭', 1)
setStorage('vendorUserInfo',{
venderOrderID:item.vendorOrderID,
orderSeq:item.orderSeq,
vendorOrgCode:item.vendorOrgCode,
vendorStoreID:item.vendorStoreID,
userID:item.vendorUserID,
vendorID:item.vendorID
})
uni.navigateTo({ url: '/subPages/messageChild/msgChat/msgChat' })
}else{
// 饿百
toast('暂不支持聊天', 2)
}
}
/*************************************************
* 查看地图
*/
function seeMap() {
uni.navigateTo({
url: `/subPages/orderChild/seeMap/seeMap?vendorOrderID=${props.item.vendorOrderID}&vendorID=${props.item.vendorID}`,
})
}
/**
* 是否显示已超时
*/
const isCancelTime = computed(() => {
return (
(props.item.status === 17 ||
props.item.status === 18 ||
props.item.status === 22) &&
props.item.lockStatus !== -5
)
})
/**
* 是否显示预计送达时间
*/
const isSrestPickTime = computed(() => {
return (
(props.item.status === 10 ||
props.item.status === 15 ||
props.item.status === 20) &&
props.item.lockStatus === 0
)
})
/**
* 订单号转换
*/
const orderNum = computed(() => {
return props.item.vendorID === 3
? props.item.vendorOrderID2
: props.item.vendorOrderID
})
/**
* 异常订单转换
*/
const abnormalStatus = computed(() => {
let order = props.item
if (order.lockStatus === -5) {
return '申请取消'
} else if (order.status === 17) {
return '待审核'
} else if (order.status === 18) {
return '取货失败'
} else if (order.status === 22) {
return '投递失败'
} else {
return '未知'
}
})
/**
* 待接单预计超时时间
*/
const cancelTime = ref<any>()
const timer = ref<any>()
onShow(() => {
if (props.item.lockStatus === -5) {
let { purchaseVendorInfo } = store.state.serveInfo.sysInfo as any
let { userApplyCancelWaitMinute } = purchaseVendorInfo[props.item.vendorID]
let cancelTime1 =
new Date(props.item.lockStatusTime).getTime() +
userApplyCancelWaitMinute * 60 * 1000 -
new Date().getTime()
cancelTime.value =
cancelTime1 > 0 ? `${new Date(cancelTime1).getMinutes()}` : '已超时'
timer.value = setInterval(() => {
cancelTime1 =
new Date(props.item.lockStatusTime).getTime() +
userApplyCancelWaitMinute * 60 * 1000 -
new Date().getTime()
cancelTime.value =
cancelTime1 > 0 ? `${new Date(cancelTime1).getMinutes()}` : '已超时'
}, 1000)
}
})
/**
* 预计送达时间
*/
const expectedDeliveredTime = computed(() => {
let timer: any = ''
if (isNullDate(props.item.expectedDeliveredTime)) {
// 预计送达时间无的情况
timer = new Date(
new Date(props.item.orderCreatedAt).getTime() + 60 * 60 * 1000
)
} else {
timer = new Date(props.item.expectedDeliveredTime)
}
return `${
timer.getHours() < 10 ? '0' + timer.getHours() : timer.getHours()
}:${timer.getMinutes() < 10 ? '0' + timer.getMinutes() : timer.getMinutes()}`
})
/**
* 剩余拣货时间 || 剩余用户自己取消订单时间
*/
const restPickTime = computed(() => {
// 拣货结束时间time1 = 预计送达时间 - 30分钟
let time1: any = ''
if (isNullDate(props.item.pickDeadline)) {
// 没有拣货时间
if (isNullDate(props.item.expectedDeliveredTime)) {
// 预计送达时间为空
time1 = new Date(props.item.orderCreatedAt).getTime() + 30 * 60 * 1000
} else {
// 预计送达时间有
time1 =
new Date(props.item.expectedDeliveredTime).getTime() - 30 * 60 * 1000
}
} else {
// 有拣货时间
time1 = new Date(props.item.pickDeadline).getTime()
}
let restTime = (time1 - new Date().getTime()) / 1000 / 60 // 分钟
restTime = Math.floor(restTime)
// 送达时间
let time2: any = ''
if (isNullDate(props.item.expectedDeliveredTime)) {
// 时间是空
time2 =
new Date(props.item.orderCreatedAt).getTime() +
60 * 60 * 1000 -
new Date().getTime()
} else {
time2 =
new Date(props.item.expectedDeliveredTime).getTime() -
new Date().getTime()
}
let arrivedTime = Math.floor(time2 / 1000 / 60)
switch (props.item.status) {
case 10:
if (props.item.lockStatus === 0) {
if (restTime < 0) {
// 超时
return `<span class="order-bg-cs">&nbsp;</span>`
} else if (restTime < 5) {
// 小于5分钟
return `<span class="rest-time picktime3">${restTime}分钟</span>`
} else if (restTime < 10) {
// 小于10分钟
return `<span class="rest-time picktime2">${restTime}分钟</span>`
} else if (restTime > 60 && props.item.businessType === 2) {
// 预订单
return `<span class="order-bg-dsd">&nbsp;</span>`
} else {
// 通常
return `<span class="rest-time picktime1">${restTime}分钟</span>`
}
} else {
return `<span class="order-delivered">已锁定</span>`
}
case 15:
return sendTime(arrivedTime)
case 20:
return sendTime(arrivedTime)
case 105:
return `<span class="order-delivered">已送达</span>`
case 110:
return `<span class="order-delivered">已送达</span>`
case 115:
return `<span class="order-delivered">已取消</span>`
case 120:
return `<span class="order-delivered">已失败</span>`
default:
return ''
}
})
function sendTime(time: any) {
if (time < 0) {
// 超时
return `<span class="order-bg-cs">&nbsp;</span>`
} else if (time < 5) {
// 小于5分钟
return `<span class="rest-time arrived3">${time}分钟</span>`
} else if (time < 20) {
// 小于20分钟
return `<span class="rest-time arrived2">${time}分钟</span>`
} else if (time > 60 && props.item.businessType === 2) {
// 预订单
return `<span class="order-bg-dsd">&nbsp;</span>`
} else {
// 通常
return `<span class="rest-time arrived1">${time}分钟</span>`
}
}
/**
* 判断是否显示已完成,预计送达文字
*/
const isShow = computed(() => {
if (
props.item.status == 110 ||
props.item.status == 115 ||
props.item.status == 120
) {
return false
} else {
return true
}
})
</script>
<style lang="scss" scoped>
// 表哦提
.order-info {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 15rpx;
background-color: #fff;
border-bottom: 6rpx dashed #e7e7e7;
.order {
.timer {
margin-top: 6rpx;
color: rgb(0, 0, 0);
}
}
.index-color {
color: rgb(0, 0, 0);
}
.h-number {
display: flex;
align-items: center;
.brand {
display: inline-block;
width: 60rpx;
height: 60rpx;
flex-shrink: 0;
background-size: 100%;
background-repeat: no-repeat;
border-radius: 15rpx;
}
.num-root {
margin-left: 5rpx;
}
.num {
font-size: 50rpx;
font-weight: bold;
}
}
.compelet {
color: #000;
font-weight: bold;
}
.cancel {
color: #f60d58;
font-weight: bold;
}
.fail {
color: $jx-warring;
font-weight: bold;
}
}
// 顶部标题
.order-title {
display: flex;
justify-content: space-between;
background-color: #ffffff;
// padding: 15rpx;
padding: 10rpx;
border-bottom: 2rpx dashed #e7e7e7;
text-align: center;
align-items: center;
.name {
color: #999999;
// margin-right: 20rpx;
}
// .title-left {
// display: flex;
// align-items: center;
// justify-content: space-between;
// width: calc(100% - 90px);
// padding: 10rpx;
// box-sizing: border-box;
// .name {
// color: #999999;
// margin-right: 20rpx;
// }
// .connectUser{
// width: 38rpx;
// height: 38rpx;
// }
// // .phone {
// // color: #999999;
// // display: flex;
// // align-items: center;
// // margin-left: 10rpx;
// // .phone-number {
// // margin-right: 10rpx;
// // }
// // }
// }
}
.order-number {
display: flex;
justify-content: space-between;
white-space: nowrap;
padding: 15rpx;
border-bottom: 2rpx dashed #e7e7e7;
color: #999999;
.copy {
font-size: 26rpx;
padding: 4rpx 12rpx;
border-radius: 5rpx;
border: 2rpx solid #999999;
color: #999999;
}
}
.see-map {
font-size: 28rpx;
padding: 7rpx 12rpx;
border-radius: 5rpx;
background-color: rgba($color: $jx-primary, $alpha: 0.15);
color: $jx-primary;
height:25px;
line-height:25px;
margin:auto 10px;
}
</style>

View File

@@ -0,0 +1,79 @@
<template>
<view class="order-totalCount">
<text class="totalCount"
>{{ item.skuCount }}{{ item.goodsCount }}件商品</text
>
<view>
{{
item.vendorPayPercentage != 0
? item.vendorPayPercentage < 50
? '实际支付:'
: '实际收入:'
: isPointStore
? '实际支付:'
: '实际收入:'
}}<jx-real-income :orderData="item" color="#585858" />
</view>
<!-- 预定单 -->
<view class="preIcon" v-if="item.businessType === 2">预订单</view>
</view>
</template>
<script lang="ts" setup>
import { store } from '@/store'
import { computed } from 'vue'
interface PropsType {
item: any
}
defineProps<PropsType>()
const isPointStore = computed(() => {
return store.getters['storeInfo/isPointStore']
})
</script>
<style lang="scss" scoped>
// 商品数量
.order-totalCount {
display: flex;
align-items: center;
padding: 15rpx;
border-bottom: 2rpx dashed #e7e7e7;
color: #585858;
.play-money {
margin-left: 30rpx;
}
.totalCount {
margin-right: 25rpx;
}
}
.preIcon {
position: absolute;
top: 0;
right: 0;
padding: 4rpx 20rpx 6rpx 20rpx;
font-size: 26rpx;
color: #fff;
border-radius: 0 0 0 10rpx;
background-color: #ff702c;
}
@keyframes enlarge {
0% {
transform: scale(0.9);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(0.9);
}
}
</style>

View File

@@ -0,0 +1,45 @@
.jx-tabs {
border-top: 1rpx solid #f8f8f8;
background-color: #fff;
border-bottom: 1rpx solid rgb(214, 214, 214);
}
.tab-scroll {
white-space: nowrap;
width: 100%;
height: 95rpx;
line-height: 94rpx;
}
.scroll-item {
position: relative;
display: inline-block;
width: 150rpx;
text-align: center;
text-align: center;
font-size: 33rpx;
transition: all 0.2s;
.badge {
position: absolute;
right: -5rpx;
top: 4rpx;
border-radius: 100rpx 100rpx 100rpx 0;
font-size: 28rpx;
min-width: 40rpx;
min-height: 40rpx;
padding: 3rpx;
box-sizing: border-box;
text-align: center;
line-height: 40rpx;
color: #fff;
background-color: #f60d58;
}
}
.jxTabsActive {
color: #4eb331;
font-weight: bold;
font-size: 37rpx;
}

View File

@@ -0,0 +1,161 @@
/**
* 订单木块方法
*/
import { Ref, ref } from "vue";
import { getStorage } from '@/utils/storage'
import { onShow } from "@dcloudio/uni-app";
import order from '@/api/https/order'
function tabFn() {
onShow(async () => {
await getOrderCount()
})
/**
* tab 菜单栏数据
*/
const currentStatus: Ref<number> = ref(2);
interface OrderStatusType {
id: number;
status: number | Array<number>;
name: string;
count: number;
}
const orderStatus = ref<OrderStatusType[]>([
{
id: 1,
status: [3],
name: "待接单",
count: 0,
},
{
id: 2,
status: [10],
name: "待拣货",
count: 0,
},
{
id: 3,
status: [15],
name: "待配送",
count: 0,
},
{
id: 4,
status: [20],
name: "配送中",
count: 0,
},
{
id: 5,
status: [5, 10, 15, 20, 17, 18, 22],
name: "异常单",
count: 0,
},
{
id: 6,
status: [155, 160, 165, 167, 180, 190],
name: "售后单",
count: 0,
},
{
id: 7,
status: [110, 115, 120],
name: "已完成",
count: 0,
},
]);
/**
* tab 点击事件
*/
const scrollLeft: Ref<number> = ref(0); // 滚动到左边距离
const record: Ref<number> = ref(1); // 记录值
function clickTab(id: number, recordID: number, e: AnyObject) {
currentStatus.value = id;
// 判断左右滑动
let { offsetLeft } = e.target;
if (id > recordID) {
scrollLeft.value = offsetLeft - 75;
} else {
scrollLeft.value = offsetLeft - 75 * 3;
}
record.value = id; // 赋值记录纸
// 更新条数
getOrderCount()
}
/**
* 请求订单数量信息
*/
async function getOrderCount() {
let orderData = {
storeID: getStorage('storeID'),
lastHours: new Date().getHours() * 1 + 48
}
// 订单数量
let orderCount = await order.Get_store_rder_count_info(orderData)
// 售后单量
let afsCount = await order.Get_store_afs_order_countinfo(orderData)
if (orderCount.code == 0 && afsCount.code == 0) {
// 没有数据默认一个空数组
let data = orderCount.data || []
let data2 = afsCount.data || []
// 判断显示条数
if (data.length > 0) {
// 3 待接单
orderStatus.value[0].count = data.filter((item: any) => item.status === 3 && item.lockStatus === 0).length > 0 ? data.filter((item: any) => item.status === 3 && item.lockStatus === 0)[0].count : 0
orderStatus.value[0].count += data.filter((item: any) => item.status === 3 && item.lockStatus === -20).length > 0 ? data.filter((item: any) => item.status === 3 && item.lockStatus === 0)[0].count : 0
// 10 待拣货
orderStatus.value[1].count = data.filter((item: any) => item.status === 10 && item.lockStatus === 0).length > 0 ? data.filter((item: any) => item.status === 10 && item.lockStatus === 0)[0].count : 0
orderStatus.value[1].count += data.filter((item: any) => item.status === 10 && item.lockStatus === -20).length > 0 ? data.filter((item: any) => item.status === 10 && item.lockStatus === 0)[0].count : 0
// 15 待配送
orderStatus.value[2].count = data.filter((item: any) => item.status === 15 && item.lockStatus === 0).length > 0 ? data.filter((item: any) => item.status === 15 && item.lockStatus === 0)[0].count : 0
orderStatus.value[2].count += data.filter((item: any) => item.status === 15 && item.lockStatus === -20).length > 0 ? data.filter((item: any) => item.status === 15 && item.lockStatus === -20)[0].count : 0
// 20 配送中
orderStatus.value[3].count = data.filter((item: any) => item.status === 20 && item.lockStatus === 0).length > 0 ? data.filter((item: any) => item.status === 20 && item.lockStatus === 0)[0].count : 0
orderStatus.value[3].count += data.filter((item: any) => item.status === 20 && item.lockStatus === -20).length > 0 ? data.filter((item: any) => item.status === 20 && item.lockStatus === -20)[0].count : 0
// 17 异常单
orderStatus.value[4].count = data.filter((item: any) => item.status === 17 && item.lockStatus === 0).length > 0 ? data.filter((item: any) => item.status === 17 && item.lockStatus === 0)[0].count : 0
// 18 已完成
orderStatus.value[4].count += data.filter((item: any) => item.status === 18 && item.lockStatus === 0).length > 0 ? data.filter((item: any) => item.status === 18 && item.lockStatus === 0)[0].count : 0
// 22 已完成
orderStatus.value[4].count += data.filter((item: any) => item.status === 22 && item.lockStatus === 0).length > 0 ? data.filter((item: any) => item.status === 22 && item.lockStatus === 0)[0].count : 0
orderStatus.value[4].count += data.filter((item: any) => item.lockStatus === -5 && item.status < 100).length > 0 ? data.filter((item: any) => item.lockStatus === -5 && item.status < 100)[0].count : 0
} else {
orderStatus.value[0].count = 0
orderStatus.value[1].count = 0
orderStatus.value[2].count = 0
orderStatus.value[3].count = 0
orderStatus.value[4].count = 0
}
// 售后单
if (data2.length > 0) {
orderStatus.value[5].count = data2.find((item: any) => item.status === 155) ? data2.find((item: any) => item.status === 155).count : 0
orderStatus.value[5].count += data2.find((item: any) => item.status === 165) ? data2.find((item: any) => item.status === 155).count : 0
} else {
orderStatus.value[5].count = 0
}
}
}
/**
* 导出数据
*/
return {
currentStatus, // 高亮初始值
orderStatus, // tab 菜单栏数据
scrollLeft, // 滚动到左边距离
record, // 记录值
clickTab, // tab 点击事件
getOrderCount, // 获取数据
}
}
export default tabFn

View File

@@ -0,0 +1,51 @@
<template>
<view class="jx-tabs">
<scroll-view
class="tab-scroll"
scroll-x="true"
scroll-with-animation
:scroll-left="scrollLeft"
>
<view
v-for="item in orderStatus"
:key="item.id"
class="scroll-item"
:class="{ jxTabsActive: currentStatus == item.id }"
@tap="tabEmit(item.id, item, record, $event)"
>{{ item.name }}
<text class="badge" v-if="item.count != 0">{{ item.count }}</text>
</view>
</scroll-view>
</view>
</template>
<script lang="ts" setup>
import tabFn from './jx-tab'
const {
currentStatus, // 高亮初始值
orderStatus, // tab 菜单栏数据
scrollLeft, // 滚动到左边距离
record, // 记录值
clickTab, // tab 点击事件
getOrderCount, // 获取数据
} = tabFn()
const emit = defineEmits<{
(e: 'jxTab', target: AnyObject): void
}>()
let recordNum: number = 2
function tabEmit(id: number, item: AnyObject, recordID: number, e: AnyObject) {
if (recordNum == id) return false
recordNum = id
emit('jxTab', item)
clickTab(id, recordID, e)
}
defineExpose({
getOrderCount,
})
</script>
<style lang="scss" scoped>
@import './jx-tab.scss';
</style>

View File

@@ -0,0 +1,56 @@
.switch-filter {
background-color: #fff;
.condition {
text-align: center;
color: #999999;
}
box-sizing: border-box;
border-radius: 10rpx;
padding: 20rpx 40rpx 10rpx 40rpx;
.item {
display: flex;
margin-top: 35rpx;
.root {
display: flex;
align-items: center;
text {
padding: 0 10rpx;
}
}
.title {
color: #999999;
}
.value {
padding: 5rpx 15rpx;
border: 1rpx solid #bebebe;
border-radius: 10rpx;
}
}
.btnRoot {
display: flex;
justify-content: space-between;
margin-top: 30rpx;
border-top: 1rpx solid #999999;
.cancel,
.confirm {
color: #696969;
text-align: center;
width: 100%;
padding: 20rpx;
}
.confirm {
color: #000;
border-left: 1rpx solid #999999;
}
}
}

View File

@@ -0,0 +1,273 @@
<template>
<uni-popup ref="popup" type="top">
<view class="switch-filter">
<view class="condition">查询条件</view>
<view class="item" v-if="orderState == 7">
<text class="title">时间类型</text>
<picker
class="value"
mode="selector"
:value="isDateFinishSearch"
:range="dateFinish"
range-key="name"
@change="pickerChange1"
>{{ dateFinish[isDateFinishSearch].name }}</picker
>
</view>
<view class="item">
<text class="title">查询时间</text>
<view class="root">
<picker
class="value"
mode="date"
:value="date1"
start="2010-01-01"
:end="endDate"
@change="dateChange1"
>{{ date1 }}</picker
>
<text></text>
<picker
class="value"
mode="date"
:value="date2"
start="2010-01-01"
:end="endDate"
@change="dateChange2"
>{{ date2 }}</picker
>
</view>
</view>
<view class="item">
<text class="title">外卖平台</text>
<picker
class="value"
mode="selector"
:value="vendor"
:range="vendorArr"
range-key="name"
@change="dateChange3"
>{{ vendorArr[vendor].name }}</picker
>
</view>
<view class="item" v-if="orderState == 7">
<text class="title">配送方式</text>
<picker
class="value"
mode="selector"
:value="waybillVendor"
:range="waybillVendorArr"
range-key="name"
@change="dateChange4"
>{{ waybillVendorArr[waybillVendor].name }}</picker
>
</view>
<view class="item" v-if="orderState == 6">
<text class="title">售后方式</text>
<picker
class="value"
mode="selector"
:value="appealTypes1"
:range="appealTypesArr"
range-key="name"
@change="dateChange5"
>{{ appealTypesArr[appealTypes1].name }}</picker
>
</view>
<view class="item" v-if="orderState == 6">
<text class="title">售后状态</text>
<picker
class="value"
mode="selector"
:value="afsStatus1"
:range="afsStatusArr"
range-key="name"
@change="dateChange6"
>{{ afsStatusArr[afsStatus1].name }}</picker
>
</view>
<view class="btnRoot">
<text class="cancel" @tap="popup.close()">取消</text>
<text class="confirm" @tap="confirm">确定</text>
</view>
</view>
</uni-popup>
</template>
<script lang="ts" setup>
import { timeFormatD } from '@/utils/tools'
import { ref,watch } from 'vue'
/**
* 筛选条件实例
*/
const popup = ref<any>(null)
interface propsType {
orderState: number,
orderStateInfo:AnyObject
}
const props = defineProps<propsType>()
/**
* 创建时间
*/
const isDateFinishSearch = ref<number>(0)
type dateFinishType = {
key: number
name: string
}
const dateFinish = ref<Array<dateFinishType>>([
{ key: 0, name: '创建时间' },
{ key: 1, name: '完成时间' },
])
function pickerChange1(e: AnyObject) {
isDateFinishSearch.value = +e.detail.value
}
/**
* 起始时间,结束时间
*/
const date1 = ref<string>(
timeFormatD(+new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 7))
// timeFormatD(+new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 2))
)
watch(
() => props.orderStateInfo,
(val) => {
if (val && val.id === 6 && val.count) {
afsStatus1.value = 1
date1.value = timeFormatD(+new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 2))
}else {
afsStatus1.value = 0
date1.value = timeFormatD(+new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 7))
}
}
)
const date2 = ref<string>(timeFormatD(+new Date()))
const endDate = ref<string>('')
function dateChange1(e: AnyObject) {
date1.value = e.detail.value
}
function dateChange2(e: AnyObject) {
date2.value = e.detail.value
}
/**
* 外卖平台
*/
const vendor = ref<number>(0)
type vendorArrType = {
id: number
name: string
}
const vendorArr = ref<Array<vendorArrType>>([
{ id: -1, name: '所有平台' },
{ id: 0, name: '京东到家' },
{ id: 1, name: '美团外卖' },
{ id: 3, name: '饿百新零售' },
{ id: 5, name: '京东商城' },
{ id: 9, name: '京西商城' },
])
function dateChange3(e: AnyObject) {
vendor.value = e.detail.value
}
/**
* 配送方式
*/
const waybillVendor = ref<number>(0)
const waybillVendorArr = ref<Array<vendorArrType>>([
{ id: -1, name: '所有配送' },
{ id: 0, name: '京东到家专送' },
{ id: 1, name: '美团专送' },
{ id: 3, name: '饿百专送' },
{ id: 101, name: '达达众包' },
{ id: 102, name: '美团配送' },
{ id: 103, name: '蜂鸟配送' },
{ id: 105, name: 'UU跑腿' },
{ id: 106, name: '顺丰同城' },
])
function dateChange4(e: AnyObject) {
waybillVendor.value = e.detail.value
}
/**
* 售后方式
*/
const appealTypes1 = ref<number>(0)
const appealTypesArr = ref<Array<vendorArrType>>([
{ id: -1, name: '全部方式' },
{ id: 1, name: '仅退款' },
{ id: 2, name: '退货退款' },
{ id: 3, name: '重发商品' },
])
function dateChange5(e: AnyObject) {
appealTypes1.value = e.detail.value
}
/**
* 售后状态
*/
const afsStatus1 = ref<number>(0)
const afsStatusArr = ref<Array<vendorArrType>>([
{ id: -1, name: '全部状态' },
{ id: 155, name: '待审核' },
{ id: 160, name: '已审核' },
{ id: 165, name: '退货待确认' },
{ id: 167, name: '退货已收到' },
{ id: 180, name: '售后成功' },
{ id: 190, name: '售后失败' },
])
function dateChange6(e: AnyObject) {
afsStatus1.value = e.detail.value
}
/**
* 确定筛选条件
*/
type emitType = {
fromTime: string
toTime: string
vendorID: number | string
waybillVendorID?: number | string
isDateFinish?: boolean
appealTypes?: number | string
afsStatus?: number | string
}
const emit = defineEmits<{
(e: 'filterData', data: emitType): void
}>()
function confirm() {
let data = {
fromTime: date1.value,
toTime: date2.value,
vendorID: vendorArr.value[vendor.value].id,
waybillVendorID: waybillVendorArr.value[waybillVendor.value].id,
isDateFinish: Boolean(isDateFinishSearch.value),
appealTypes: appealTypesArr.value[appealTypes1.value].id,
afsStatus: afsStatusArr.value[afsStatus1.value].id,
}
emit('filterData', data)
popup.value.close()
}
/**
* 打开弹窗
*/
function openRef() {
popup.value.open()
}
/**
* 导出外部访问函数
*/
defineExpose({
openRef,
})
</script>
<style lang="scss" scoped>
@import './order-filter.scss';
</style>

View File

@@ -0,0 +1,325 @@
<template>
<view
class="distribution-root position-relative"
@tap="orderDetail(item.vendorOrderID, item.vendorID)"
>
<order-list-top :item="item" />
<order-money :item="item" />
<view class="address">地址{{ item.consigneeAddress }}</view>
<view class="distribution-msg" v-if="item.courierName">
<text class="vender-name">{{ wbVendor }}</text>
<text class="distribution-name">{{ item.courierName }}</text>
<text
v-if="item.courierMobile.split(',')[1]"
class="distribution-mobile"
@tap.stop="phoneCall(item.courierMobile)"
>{{
item.courierMobile.split(',')[0] +
'转' +
item.courierMobile.split(',')[1]
}}</text
>
<text
v-else
class="distribution-mobile"
@tap.stop="phoneCall(item.courierMobile)"
>{{ item.courierMobile }}</text
>
<jx-icon
icon="24gf-telephone"
color="#999999"
@tap.stop="phoneCall(item.courierMobile)"
:size="38"
style="margin-left: 10rpx"
/>
</view>
<!-- 自提订单 -->
<view class="self-order self" v-if="item.deliveryType === 'self'"
>[自提订单]</view
>
<!-- 操作按钮 -->
<view class="operation">
<view class="printerBtn" @tap.stop="deliverManager(item.vendorOrderID)"
>配送管理</view
>
<view class="pickingBtn">查看详情</view>
</view>
<!-- 确定自提 -->
<template v-if="isSelfBtn">
<view
class="btn-picked"
v-if="item.vendorID != 9"
@tap.stop="inputTakeCode"
>输入自提码</view
>
<view class="btn-picked" v-else @tap.stop="jxSelfTake('jx')"
>完成自提</view
>
</template>
<!-- 商家自送送达按钮 status 20 -->
<template v-if="isConfrim">
<div class="btn-picked" @tap.stop="handleSelfDelivered">
确认送达
<text v-if="(item.flag & 128) === 128">[已操作]</text>
</div>
</template>
</view>
<!-- 自提码弹窗 -->
<uni-popup ref="popup" type="top">
<view class="takeCode-root">
<view class="title">自提订单</view>
<input
class="ipt"
type="text"
placeholder="请输入自提码"
v-model="takeCode"
/>
<view class="btn-root">
<view class="cancel" @tap.stop="popup.close()">取消</view>
<view class="confirm" @tap.stop="jxSelfTake('noJx')">确定</view>
</view>
</view>
</uni-popup>
</template>
<script lang="ts" setup>
import useOrderInfo from '@/composables/useOrderInfo'
import toast from '@/utils/toast'
import { computed, ref } from 'vue'
import orderListTop from '../component/orderListTop.vue'
import orderMoney from '../component/orderMoney.vue'
import { store } from '@/store'
import order from '@/api/https/order'
const {
orderDetail, // 订单详情
deliverManager, // 配送管理
phoneCall, // 拨打电话
waybillVendor, // 厂商转换
} = useOrderInfo()
interface PropsType {
item: any
type: number
}
const props = defineProps<PropsType>()
/**
* 转换快递厂商
*/
const wbVendor = computed(() => {
return waybillVendor(
props.item.waybillVendorID,
props.item.status,
props.item.waybillStatus
)
})
/**
* 计算属性展示确认送达按钮
*/
const isConfrim = computed(() => {
return (
(props.item.deliveryFlag & 3) === 3 &&
props.item.lockStatus === 0 &&
props.item.deliveryType !== 'self' &&
!props.item.vendorWaybillID
)
})
/**
* 计算属性计算出是否显示自提订单按钮
*/
const isSelfBtn = computed(() => {
return (
props.item.status === 15 &&
props.item.waybillVendorID === -1 &&
props.item.lockStatus === 0 &&
props.item.deliveryType === 'self'
)
})
/**
* 除开京西自提订单
*/
const popup = ref<any>(null)
function inputTakeCode() {
takeCode.value = ''
popup.value.open()
}
/**
* 自提订单 默认京西自提订单
*/
const takeCode = ref<any>() // 自提码
function jxSelfTake(type: string) {
if (type != 'jx' && takeCode.value == '') return toast('请输入自提码')
uni.jxConfirm({
title: '自提订单',
content: '是否完成自提(请确保用户已出示正确订单,以免引起不必要的损失)',
success: async () => {
let data = {
vendorOrderID: props.item.vendorOrderID,
vendorID: props.item.vendorID,
selfTakeCode: type === 'jx' ? '135246' : takeCode.value,
}
let res = await order.confirm_self_take(data)
if (res.code == 0) {
toast('自提成功', 1)
popup.value.close()
// 刷新订单
store.commit('serveInfo/setUpdateOrder', Date.now())
} else {
toast('自提发生异常', 2)
}
},
})
}
/**
* 确认送达
*/
function handleSelfDelivered() {
// if ((props.item.flag & 128) === 128) return false
uni.jxConfirm({
title: '提示',
content: '是否确认送达商品',
success: async () => {
let data = {
vendorOrderID: props.item.vendorOrderID,
vendorID: props.item.vendorID,
}
let res = await order.self_delivered(data)
if (res.code == 0) {
toast('送达成功', 1)
// 刷新订单
store.commit('serveInfo/setUpdateOrder', Date.now())
} else {
toast('送达异常'+ res.desc, 2)
}
},
})
}
</script>
<style lang="scss" scoped>
:deep(.order-totalCount) {
border: none !important;
}
.position-relative {
position: relative;
}
.distribution-root {
position: relative;
background-color: #fff;
padding-bottom: 20rpx;
}
.self {
padding: 15rpx;
border-bottom: 2rpx dashed #e7e7e7;
}
.address {
display: flex;
justify-content: space-between;
padding: 15rpx;
font-size: 32rpx;
color: #000000;
background-color: rgba($color: #908f96, $alpha: 0.15);
border-radius: 10rpx;
}
// 操作按钮
.operation {
display: flex;
justify-content: space-between;
box-sizing: border-box;
padding: 15rpx 20rpx 0 20rpx;
.printerBtn,
.pickingBtn {
width: 100%;
padding: 20rpx;
text-align: center;
border-radius: 10rpx;
color: #fff;
}
.printerBtn {
margin-right: 15rpx;
background-image: linear-gradient(135deg, #ec903f, #ff6a20);
}
.pickingBtn {
margin-left: 15rpx;
background-image: linear-gradient(135deg, $jx-primary, #85c972);
}
}
// 自提
.takeCode-root {
box-sizing: border-box;
background-color: rgb(255, 255, 255);
border-radius: 0 0 15rpx 15rpx;
padding: 25rpx;
.title {
width: 100%;
text-align: center;
}
.ipt {
width: 100%;
height: 80rpx;
border: 1rpx solid #cecece;
border-radius: 10rpx;
margin: 20rpx 0;
text-align: center;
}
.btn-root {
display: flex;
justify-content: space-between;
.cancel,
.confirm {
width: 100%;
padding: 15rpx 0;
text-align: center;
border-radius: 10rpx;
}
.cancel {
color: $jx-primary;
border: 1rpx solid $jx-primary;
margin-right: 15rpx;
}
.confirm {
background-color: $jx-primary;
margin-left: 15rpx;
color: #fff;
}
}
}
.distribution-msg {
padding: 15rpx;
border-bottom: 2rpx dashed #e7e7e7;
.distribution-name {
margin-right: 25rpx;
}
.distribution-mobile {
color: #999;
}
}
.vender-name {
color: #999999;
margin-right: 25rpx;
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<view
class="pending-order-root position-relative"
@tap="orderDetail(item.vendorOrderID, item.vendorID)"
>
<order-list-top :item="item" />
<!-- 商品统计 -->
<orderMoney :item="item" />
<!-- 操作按钮 -->
<view class="operation-btn">
<view
class="refuse"
@tap.stop="
AcceptOrRefuseOrder(item.vendorOrderID, item.vendorID, false)
"
>拒绝接单</view
>
<view
class="need"
@tap.stop="AcceptOrRefuseOrder(item.vendorOrderID, item.vendorID, true)"
>立即接单</view
>
</view>
</view>
</template>
<script lang="ts" setup>
import useOrderInfo from '@/composables/useOrderInfo'
import orderListTop from '../component/orderListTop.vue'
import orderMoney from '../component/orderMoney.vue'
import toast from '@/utils/toast'
import { store } from '@/store'
import order from '@/api/https/order'
const {
orderDetail, // 订单详情
} = useOrderInfo()
interface PropsType {
item: any
}
defineProps<PropsType>()
/**
* 确定接单
*/
async function AcceptOrRefuseOrder(
vendorOrderID: string,
vendorID: string,
isAccept: boolean
) {
let data = {
vendorOrderID,
vendorID,
isAccept,
}
let res = await order.accept_or_refuse_order(data)
if (res.code == 0) {
if (isAccept) {
toast('接单成功', 1)
} else {
toast('拒绝订单成功', 1)
}
// 刷新订单
store.commit('serveInfo/setUpdateOrder', Date.now())
} else {
toast('订单数据异常')
}
}
</script>
<style lang="scss" scoped>
.position-relative {
position: relative;
}
.pending-order-root {
box-sizing: border-box;
background-color: #fff;
// 操作按钮
.operation-btn {
display: flex;
align-items: center;
padding: 20rpx;
justify-content: space-evenly;
border-bottom: 2rpx dashed #e7e7e7;
.refuse,
.need {
padding: 8rpx 35rpx;
border-radius: 7rpx;
color: #fff;
font-size: 28rpx;
}
.refuse {
background-color: #f60d58;
}
.need {
background-color: $jx-primary;
}
}
}
</style>

View File

@@ -0,0 +1,119 @@
<template>
<view
class="pending-picking-root position-relative"
@tap="orderDetail(item.vendorOrderID, item.vendorID)"
>
<order-list-top :item="item" />
<view class="remarks"
>备注{{ item.buyerComment || '该订单用户未进行备注' }}</view
>
<order-money :item="item" />
<!-- 操作按钮 -->
<view class="operation" v-if="item.status === 10 && item.lockStatus === 0">
<view
class="printerBtn"
@tap.stop="printerOrder(item.vendorOrderID, item.vendorID)"
>打印订单</view
>
<view
class="pickingBtn"
@tap.stop="pickingComplete(item.vendorOrderID, item.vendorID)"
>拣货完成</view
>
</view>
<view class="self-order" v-if="item.deliveryType === 'self'"
>[自提订单]</view
>
</view>
</template>
<script lang="ts" setup>
import orderListTop from '../component/orderListTop.vue'
import orderMoney from '../component/orderMoney.vue'
import useOrderInfo from '@/composables/useOrderInfo'
import toast from '@/utils/toast'
import { store } from '@/store'
import order from '@/api/https/order'
const {
orderDetail, // 订单详情
printerOrder, // 打印订单
} = useOrderInfo()
interface PropsType {
item: any
}
defineProps<PropsType>()
/**
* 拣货完成
*/
async function pickingComplete(vendorOrderID: string, vendorID: string) {
let data = {
vendorOrderID,
vendorID,
}
let res = await order.finished_pickup(data)
if (res.code == 0) {
toast('拣货成功', 1)
// 刷新订单
store.commit('serveInfo/setUpdateOrder', Date.now())
} else {
toast('拣货异常', 2)
}
}
</script>
<style lang="scss" scoped>
:deep(.order-number) {
border: none !important;
}
.position-relative {
position: relative;
}
.pending-picking-root {
background-color: #fff;
.remarks {
padding: 15rpx;
color: #999999;
background-color: rgba($color: #908f96, $alpha: 0.15);
border-radius: 10rpx;
}
.divider {
width: 100%;
height: 7rpx;
background: white url('../../../../static/image/global/order-divider.png') 0
0 no-repeat;
background-size: 100%;
}
// 操作按钮
.operation {
display: flex;
justify-content: space-between;
box-sizing: border-box;
padding: 15rpx 20rpx;
.printerBtn,
.pickingBtn {
width: 100%;
padding: 20rpx;
text-align: center;
border-radius: 10rpx;
color: #fff;
}
.printerBtn {
margin-right: 15rpx;
background-image: linear-gradient(135deg, #85c972, $jx-primary);
}
.pickingBtn {
margin-left: 15rpx;
background-image: linear-gradient(135deg, #ff6a20, #ec903f);
}
}
}
</style>

View File

@@ -0,0 +1,407 @@
import useGlobalFunc from '@/composables/useGlobalFunc'
import { store } from '@/store'
import { getStorage } from '@/utils/storage'
import toast from '@/utils/toast'
import { timeFormatD, jx_trembling } from '@/utils/tools'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { watch, ref, onBeforeUnmount } from 'vue'
import order from '@/api/https/order'
// import merchant from '@/api/https/merchant'
/**
* 订单管理方法
* @author zhangshuwie <2966211270@qq.com>
* @param *
* @return 模板使用数据与方法详情内容请查看return
*/
function orderInfoFn() {
const { isTrades } = useGlobalFunc()
/**
* 公共复位函数
*/
function reset() {
orderDataList.value = []
page.value = 1
totalCount.value = 0
}
/**
* tab单击事件
* @param target 点击对应tab获取到的信息
*/
const status = ref<Array<number>>([10]) // 默认显示待拣货
const orderState = ref<number>(2) // 默认显示待拣货
const orderStateInfo = ref()
async function jxTabClick(target: AnyObject) {
orderStateInfo.value = target
reset()
uni.removeTabBarBadge({ index: 0 })
keyword.value = ''
// 列表展示判断条件
orderState.value = target.id
// 请求数据订单状态
status.value = target.status
// 请求数据
if (target.id == 6) {
// 获取售后单
if (orderStateInfo.value.count) {
allData.value.fromTime = timeFormatD(+new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 2))
allData.value.afsStatus = '155'
}
await getAfsOrders()
} else {
await getOrderAllInfo()
}
}
/**
* 下拉刷新
*/
let refresherrefreshTimer: any = null
const triggered = ref<boolean>(false)
function refresherrefresh() {
uni.removeTabBarBadge({ index: 0 })
triggered.value = true
clearTimeout(refresherrefreshTimer)
refresherrefreshTimer = setTimeout(async () => {
store.commit('storeInfo/jxLoadingFn', true)
reset()
refreshTab()
if (orderState.value == 5) {
totalCount.value = 0
}
if (orderState.value == 6) {
await getAfsOrders()
} else {
await getOrderAllInfo()
}
triggered.value = false
store.commit('storeInfo/jxLoadingFn', false)
}, 500)
}
/**
* 页面触底
*/
const isLoad = ref<boolean>(false)
async function scrolltolower() {
uni.removeTabBarBadge({ index: 0 })
page.value++
if (pageSize.value * (page.value - 1) > totalCount.value || totalCount.value < pageSize.value) {
isLoad.value = false
} else {
if (isLoad.value) return
if (orderState.value == 7) {
await getOrderAllInfo()
} else {
await getAfsOrders()
}
}
}
/**
* 获取订单详情数据
*/
type emitType = {
fromTime: string
toTime: string
vendorID: number | string
waybillVendorID?: number | string
isDateFinish?: boolean
appealTypes?: number | string
afsStatus?: number | string
}
let oldStoreID: any = 0
/*************************************************
* 门店休息情况
*/
const isRest = ref<boolean>(true)
const isStatu = ref<boolean>(false)
onShow(async () => {
if (!store.state.storeInfo.allStoreInfo.id) {
// console.log('ZSW-没有门店信息开始获取门店信息');
await store.dispatch('storeInfo/getOneStore',getStorage("storeID"))
}
if (oldStoreID != getStorage('storeID') && oldStoreID != 0) {
reset()
triggered.value = true
}
oldStoreID = getStorage('storeID')
isRest.value = isTrades(new Date()) // 门店是否接受预订单
let storeInfo = store.state.storeInfo.allStoreInfo
isStatu.value = storeInfo.status == 1 ? true : false
uni.removeTabBarBadge({ index: 0 })
})
onLoad(async (params) => {
store.commit('storeInfo/jxLoadingFn', true)
// 请求数据
reset()
await getOrderAllInfo()
store.commit('storeInfo/jxLoadingFn', false)
})
const pageSize = ref<number>(33) // 每一页条数
const page = ref<number>(1) // 第多少条
const orderDataList = ref<any>([]) // 订单数据
const totalCount = ref<number>(0) // 订单条数
const allData = ref<emitType>({
fromTime: timeFormatD(+new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 7)),
toTime: timeFormatD(+new Date()),
vendorID: '',
waybillVendorID: '',
isDateFinish: false,
appealTypes: '',
afsStatus: ''
}) // 已完成数据筛选
async function getOrderAllInfo() {
isLoad.value = true
let time1 = timeFormatD(+new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 7))
let time2 = timeFormatD(+new Date())
// 待接单、待检货、待配送、配送中、异常单、已完成
let data: any = {
fromDate: time1,
toDate: time2,
storeIDs: JSON.stringify([getStorage('storeID')]),
statuss: JSON.stringify(status.value),
offset: pageSize.value * (page.value - 1),
pageSize: pageSize.value,
isJxFirst: true
}
if (orderState.value == 7) {
data = {
fromDate: allData.value.fromTime ? allData.value.fromTime : time1,
toDate: allData.value.toTime ? allData.value.toTime : time2,
storeIDs: JSON.stringify([getStorage('storeID')]),
statuss: JSON.stringify(status.value),
offset: pageSize.value * (page.value - 1),
pageSize: pageSize.value,
isDateFinish: allData.value.isDateFinish,
vendorIDs: `[${allData.value.vendorID}]`,
waybillVendorIDs: `[${allData.value.waybillVendorID}]`,
isJxFirst: true,
keyword: keyword.value
}
}
let res = await order.get_orders(keyFilter(data))
if (res.code == 0) {
if (page.value <= 1) {
reset()
}
let data: Array<any> = res.data.data || []
if (data.length == 0) toast('暂无该类型订单')
orderDataList.value = orderDataList.value.concat(data)
totalCount.value = res.data.totalCount
} else {
reset()
totalCount.value = 0
}
isLoad.value = false
}
/*************************************************
* 过滤对象空字段
*/
function keyFilter(obj: AnyObject) {
for (let key in obj) {
// if ((obj[key] == "" || obj[key] == [""] || obj[key] == '[-1]')) {
if ((obj[key] == "" || obj[key] == '[-1]')) {
delete obj[key]
}
}
return obj
}
/*************************************************
* 获取异常单
*/
async function getAfsOrders() {
isLoad.value = true
// 待接单、待捡货、待配送、配送中、异常单、已完成
let data = {
storeIDs: JSON.stringify([getStorage('storeID')]),
fromTime: `${allData.value.fromTime} 00:00:00`,
toTime: `${allData.value.toTime} 23:59:59`,
pageSize: pageSize.value,
offset: pageSize.value * (page.value - 1),
vendorIDs: `[${allData.value.vendorID}]`,
appealTypes: `[${allData.value.appealTypes}]`,
statuss: `[${allData.value.afsStatus}]`,
isDetail: true,
keyword: keyword.value
}
let res = await order.get_afs_orders(keyFilter(data))
if (res.code == 0) {
if (page.value <= 1) {
reset()
}
let data: Array<any> = res.data.data || []
if (data.length == 0) toast("暂无售后订单")
orderDataList.value = orderDataList.value.concat(res.data.data)
totalCount.value = res.data.totalCount
} else {
reset()
totalCount.value = 0
}
isLoad.value = false
}
/**
* 刷新导航栏
*/
const tabInfo = ref<any>(null)
function refreshTab() {
tabInfo.value.getOrderCount()
}
/**
* 输入确认按钮
*/
const keyword = ref<string>('')
async function searchStore(keywords: string) {
keyword.value = keywords
reset()
if (orderState.value == 7) {
await getOrderAllInfo()
} else {
await getAfsOrders()
}
}
/**
* 输入框输入值变化
*/
let trembling = jx_trembling(getOrderAllInfo, 800)
let trembling2 = jx_trembling(getAfsOrders, 800)
async function changeIpt(keywords: string) {
keyword.value = keywords
reset()
if (orderState.value == 7) {
trembling()
} else {
trembling2()
}
}
/**
* 清空输入框
*/
async function clearIpt() {
keyword.value = ''
reset()
if (orderState.value == 7) {
await getOrderAllInfo()
} else {
await getAfsOrders()
}
}
/**
* 已完成和售后单筛选
*/
const active = ref<number>(0)
async function filterData(data: emitType) {
let ruler = (+new Date() - +new Date(data.fromTime)) / 1000 / 60 / 60 / 24
if (orderState.value != 7 && ruler > 59) {
return toast('售后查询不能超过60天')
}
if (orderState.value == 7 && ruler > 180) {
return toast('最大查询天数为180天')
}
allData.value = data
reset()
if (orderState.value == 7) {
await getOrderAllInfo()
} else {
await getAfsOrders()
}
}
/**
* 筛选条件实例对象
*/
const orderFilterRef = ref<any>(null)
/**
* 监听新订单刷新页面
*/
watch(() => store.state.serveInfo.updateOrder, async () => {
reset()
refreshTab()
if (orderState.value == 6) {
await getAfsOrders()
} else {
trembling()
}
})
/**
* 动态计算scorll-view 高度
*/
const scorllHeight = ref<string>('94rpx')
watch(() => orderState.value, (nweVal) => {
let arr: Array<number> = [1, 2, 4, 5]
if (arr.includes(nweVal)) {
scorllHeight.value = '100rpx'
} else {
scorllHeight.value = '190rpx'
}
})
/*************************************************
* 配送状态筛选
*/
function distributionStatu(type: number) {
active.value = type
}
/*************************************************
* 收尾工作
*/
onBeforeUnmount(() => {
clearTimeout(refresherrefreshTimer)
})
return {
jxTabClick, // 导航栏按钮
orderDataList, // 订单数据
totalCount, // 订单条数
orderState, // 订单列表判断条件
orderStateInfo, // 订单列表信息
tabInfo, // 导航栏实例
searchStore, // 输入库条件筛选
keyword, // 输入库内容
changeIpt, // 输入款输入变化
clearIpt, // 清空输入框
scorllHeight, // 动态计算scorll-view高度
active, // 待配送筛选高亮
orderFilterRef, // 筛选条件实例对象
filterData, // 筛选条件数据
refresherrefresh, // 下拉刷新
triggered, // 下拉加载
scrolltolower, // 页面触底
isLoad, // 触底加载
distributionStatu, // 配送筛选
isRest, // 是否显示休息状态
isStatu, // 门店是否在营业
}
}
export default orderInfoFn

View File

@@ -0,0 +1,439 @@
<template>
<!-- 联系运营 -->
<image
class="icon-phone"
src="https://image.jxc4.com/image/dd78fd0b128be86e681f376e794f8309.png"
mode="scaleToFill"
@tap="getPhone"
@touchmove.stop.prevent="touchM($event, 1)"
@touchstart="touchS()"
@touchend="touchE($event, 1)"
/>
<!-- 当前门店是否休息 -->
<view
v-if="!isStatu"
class="open-status-msg"
@touchmove.prevent="touchM($event, 2)"
@touchstart.prevent="touchS()"
@touchend.prevent="touchE($event, 2)"
><text>本店</text><text>已休息</text></view
>
<view
v-else-if="!isRest"
class="open-status-msg open-to-booking"
@touchmove.prevent="touchM($event, 2)"
@touchstart.prevent="touchS()"
@touchend.prevent="touchE($event, 2)"
><text>接受</text><text>预订单</text></view
>
<!-- tab -->
<view class="jx-tabs-root">
<jx-tab ref="tabInfo" @jxTab="jxTabClick"></jx-tab>
<!-- 售后单已完成 -->
<template v-if="orderState == 6 || orderState == 7">
<view class="filter-root">
<text class="btn" @tap="orderFilterRef.openRef()">更多条件</text>
<uni-search-bar
v-model="keyword"
@confirm="searchStore(keyword)"
@clear="clearIpt"
cancelButton="none"
clearButton="auto"
@input="changeIpt(keyword)"
:placeholder="
orderState == 6 ? '搜索售后单号、订单号、电话' : '搜索订单、电话等'
"
/>
</view>
</template>
<!-- 待配送筛选 -->
<template v-if="orderState == 3">
<view class="filter-order">
<view :class="{ active: active == 0 }" @tap="distributionStatu(0)"
>&emsp;</view
>
<view :class="{ active: active == 1 }" @tap="distributionStatu(1)"
>未抢单</view
>
<view :class="{ active: active == 2 }" @tap="distributionStatu(2)"
>已抢单</view
>
</view>
</template>
</view>
<!-- 列表内容 -->
<scroll-view
class="order-item-main"
scroll-y
:style="{ height: `calc(100vh - ${scorllHeight})` }"
refresher-enabled
:refresher-triggered="triggered"
@refresherrefresh="refresherrefresh"
refresher-background="#efefef"
@scrolltolower="scrolltolower"
>
<!-- 待接单 -->
<template v-if="orderState == 1 && totalCount != 0">
<pending-order
v-for="item in orderDataList"
:key="item.id"
:item="item"
/>
</template>
<!-- 待拣货 -->
<template v-if="orderState == 2 && totalCount != 0">
<pending-picking
v-for="item in orderDataList"
:key="item.id"
:item="item"
/>
</template>
<!-- 待配送 -->
<template v-if="orderState == 3 && totalCount != 0">
<view v-for="item in orderDataList" :key="item.id">
<pending-distribution
v-if="
active === 0 ||
(active === 1 && item.waybillStatus < 10) ||
(active === 2 && item.waybillStatus >= 10 && item.lockStatus !== -5)
"
:item="item"
:type="1"
/>
</view>
<jx-load-more
v-show="totalCount >= 2 && active === 0"
:isLoad="isLoad"
marginTop="25rpx"
/>
</template>
<!-- 配送中 -->
<template v-if="orderState == 4 && totalCount != 0">
<pending-distribution
v-for="item in orderDataList"
:key="item.id"
:item="item"
:type="0"
/>
</template>
<!-- 异常单 -->
<template v-if="orderState == 5 && totalCount != 0">
<view v-for="item in orderDataList" :key="item.id">
<abnormal-order
v-if="
item.lockStatus === -5 ||
((item.status === 17 || item.status === 18) &&
item.lockStatus !== -5) ||
(item.status === 22 && item.lockStatus !== -5)
"
:item="item"
/>
<jx-empty title="暂无异常订单" v-else />
</view>
</template>
<!-- 售后单 -->
<template v-if="orderState == 6 && totalCount != 0">
<after-sales-order
v-for="item in orderDataList"
:key="item.id"
:item="item"
></after-sales-order>
</template>
<!-- 已完成 -->
<template v-if="orderState == 7 && totalCount != 0">
<completed
v-for="item in orderDataList"
:key="item.id"
:item="item"
></completed>
</template>
<!-- 加载更多 -->
<jx-load-more
v-show="totalCount >= 2 && orderState != 3 && orderState != 5"
:isLoad="isLoad"
marginTop="25rpx"
/>
<!-- 空状态 -->
<jx-empty title="暂无订单信息" v-show="totalCount == 0" />
</scroll-view>
<!-- 切换筛选条件 -->
<order-filter
@filter-data="filterData"
ref="orderFilterRef"
:orderState="orderState"
:orderStateInfo="orderStateInfo"
/>
<!-- 公共组件 -->
<jx-login-empty title="马上登录,查看订单" />
<jx-loading />
</template>
<script lang="ts" setup>
import jxTab from './childPages/jx-tab/jx-tab.vue' // 待拣货
import pendingOrder from './childPages/pending-order/pending-order.vue' // 待拣货
import pendingPicking from './childPages/pending-picking/pending-picking.vue' // 待配送
import pendingDistribution from './childPages/pending-distribution/pending-distribution.vue' // 配送中
import afterSalesOrder from './childPages/after-sales-order/after-sales-order.vue' // 售后单
import completed from './childPages/completed/completed.vue'
import orderFilter from './childPages/order-filter/order-filter.vue'
import abnormalOrder from './childPages/abnormal-order/abnormal-order.vue'
import orderInfoFn from './main'
import { ref } from 'vue'
import { jx_throttles } from '@/utils/tools'
const {
jxTabClick, // 导航栏单击事件
orderDataList, // 订单数据
totalCount, // 订单条数
orderState, // 订单列表判断条件
orderStateInfo, // 订单列表信息
tabInfo, // 导航栏实例
searchStore, // 输入库条件筛选
keyword, // 输入库内容
changeIpt, // 输入款输入变化
clearIpt, // 清空输入框
scorllHeight, // 动态计算scorll-view高度
active, // 待配送筛选高亮
orderFilterRef, // 筛选条件实例对象
filterData, // 筛选条件数据
refresherrefresh, // 下拉刷新
triggered, // 刷新状态
scrolltolower, // 页面触底
isLoad, // 触底加载
distributionStatu, // 配送筛选
isRest, // 是否显示休息状态
isStatu, // 门店营业状态
} = orderInfoFn()
/**
* 联系运营页面跳转
*/
function getPhone() {
uni.navigateTo({ url: '/subPages/orderChild/getPhone/getPhone' })
}
/*************************************************
* 开始触摸运营
*/
const isAnmarion = ref<string>('0s')
let windowW = 0
let windowH = 0
function touchS() {
isAnmarion.value = '0s'
windowW = 0
windowH = 0
let sysTem = uni.getSystemInfoSync()
windowW = sysTem.windowWidth
windowH = sysTem.windowHeight
}
/*************************************************
* 移动联系运营
*/
const pageX1 = ref<string>('20rpx')
const pageY1 = ref<string>('450rpx')
const pageX2 = ref<string>('20rpx')
const pageY2 = ref<string>('300rpx')
function touchM(e: AnyObject, type: number) {
touchMThrottles(e, type)
}
const touchMThrottles = jx_throttles({
time: 5,
success(e: AnyObject, type: number) {
let sysTem = e.changedTouches[0]
if (type == 1) {
pageX1.value = sysTem.clientX - 25 + 'px'
pageY1.value = sysTem.clientY - 25 + 'px'
}
if (type == 2) {
pageX2.value = sysTem.clientX - 25 + 'px'
pageY2.value = sysTem.clientY - 25 + 'px'
}
},
})
/*************************************************
* 触摸运营结束
*/
function touchE(e: AnyObject, type: number) {
isAnmarion.value = '0.5s'
let sysTem = e.target
if (type == 1) {
if (sysTem.offsetLeft <= 0 || sysTem.offsetLeft <= windowW / 2 - 25) {
pageX1.value = '10px'
}
if (sysTem.offsetLeft >= windowW || sysTem.offsetLeft >= windowW / 2 - 25) {
pageX1.value = windowW - 60 + 'px'
}
if (sysTem.offsetTop <= 50) {
pageY1.value = '50px'
}
if (sysTem.offsetTop >= windowH - 100) {
pageY1.value = windowH - 60 + 'px'
}
}
if (type == 2) {
let pageX = parseInt(pageX2.value)
let pageY = parseInt(pageY2.value)
if (pageX <= 0 || pageX <= windowW / 2 - 25) {
pageX2.value = '10px'
}
if (pageX >= windowW || pageX >= windowW / 2 - 25) {
pageX2.value = windowW - 60 + 'px'
}
if (pageY <= 50) {
pageY2.value = '50px'
}
if (pageY >= windowH - 100) {
pageY2.value = windowH - 60 + 'px'
}
}
}
</script>
<style lang="scss" scoped>
.open-status-msg {
position: absolute;
z-index: 99999999999;
left: v-bind(pageX2);
top: v-bind(pageY2);
width: 50px;
height: 50px;
font-size: 10px;
padding: 10px;
text-align: center;
box-sizing: border-box;
border-radius: 50%;
color: #fff;
font-weight: bold;
background: rgba(116, 116, 116, 0.75);
text-shadow: 1rpx 1rpx 2rpx #eb6100;
transition: all v-bind(isAnmarion);
text {
display: inline-block;
}
}
.open-to-booking {
text-shadow: 2rpx 2rpx 2rpx $jx-warring;
}
.icon-phone {
position: absolute;
z-index: 99999999999;
left: v-bind(pageX1);
top: v-bind(pageY1);
width: 50px;
height: 50px;
transition: all v-bind(isAnmarion);
}
.filter-root {
box-sizing: border-box;
height: 90rpx;
padding: 15rpx;
background-color: #fff;
display: flex;
align-items: center;
.btn {
padding: 8rpx 0;
width: 160rpx;
text-align: center;
background-color: $jx-primary;
color: #fff;
font-size: 28rpx;
border-radius: 100rpx;
margin-right: 15rpx;
}
:deep(.uni-searchbar) {
padding: 0 !important;
width: 545rpx !important;
.uni-searchbar__box {
border-radius: 100rpx !important;
height: auto !important;
}
}
}
</style>
<style lang="scss">
page {
position: relative;
background-color: #efefef;
width: 100%;
height: 100%;
}
.self-order {
width: 100%;
text-align: center;
color: #ff0000;
font-weight: bold;
padding-bottom: 20rpx;
font-size: 28rpx;
}
.btn-picked {
background: $jx-primary;
color: white;
border-radius: 10rpx;
font-size: 36rpx;
font-weight: 400;
height: 94rpx;
line-height: 94rpx;
text-align: center;
margin: 0rpx 20rpx;
margin-top: 15rpx;
}
.jx-tabs-root {
position: sticky;
top: 0;
box-shadow: 0 5rpx 7rpx #ececec;
}
.filter-order {
height: 90rpx;
box-sizing: border-box;
padding: 15rpx;
display: flex;
justify-content: space-between;
background-color: #fff;
view {
box-sizing: border-box;
width: 230rpx;
padding: 10rpx 20rpx;
border: 1rpx solid #999999;
color: #999999;
border-radius: 10rpx;
text-align: center;
}
.active {
background-color: #82c86f;
border: 1rpx solid #82c86f;
color: #fff;
}
}
.order-item-main {
box-sizing: border-box;
}
</style>