2 Commits
test ... gyq

Author SHA1 Message Date
gyq
23e4407459 优化台桌 2025-06-03 10:09:56 +08:00
gyq
53a1442cf7 优化 2025-05-28 10:38:40 +08:00
505 changed files with 17235 additions and 75179 deletions

245
App.vue
View File

@@ -3,57 +3,61 @@
App.vue本身不是页面这里不能编写视图元素也就是没有<template>
-->
<script setup>
import { onLaunch } from '@dcloudio/uni-app';
import { getVersion } from '@/http/api/index.js';
import appConfig from '@/config/appConfig.js';
import { provide, onMounted } from 'vue';
import WebsocketUtil from '@/commons/utils/websocket.js';
import { onLaunch } from '@dcloudio/uni-app';
import { getVersion } from '@/http/api/index.js'
import appConfig from '@/config/appConfig.js';
import { provide,onMounted } from 'vue';
import WebsocketUtil from '@/commons/utils/websocket.js'
const websocketUtil = new WebsocketUtil(appConfig.wss, 5000); // 创建 WebSocket 工具类实例
provide('websocketUtil', websocketUtil); // 提供给所有子组件
onMounted(() => {
});
onLaunch(() => {
// 非开发工具移除 console.log console.info
if (uni.getSystemInfoSync().platform !== "devtools") {
console.log = () => {};
console.info = () => {};
}
let that = this
uni.hideTabBar()
// #ifdef MP-WEIXIN
const updateManager = wx.getUpdateManager() // 小程序版本更新管理器
updateManager.onCheckForUpdate(function(res) {
// 请求完新版本信息的回调
})
updateManager.onUpdateReady(function() {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate()
const websocketUtil = new WebsocketUtil(appConfig.wss, 5000); // 创建 WebSocket 工具类实例
provide('websocketUtil', websocketUtil); // 提供给所有子组件
onMounted(() => {});
onLaunch(() => {
// 非开发工具移除 console.log console.info
// if (uni.getSystemInfoSync().platform !== "devtools") {
// console.log = () => {};
// console.info = () => {};
// }
let that = this;
uni.hideTabBar();
// #ifdef MP-WEIXIN
const updateManager = wx.getUpdateManager(); // 小程序版本更新管理器
updateManager.onCheckForUpdate(function (res) {
// 请求完新版本信息的回调
});
updateManager.onUpdateReady(function () {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate();
});
updateManager.onUpdateFailed(function () {
// 新版本下载失败
});
// #endif
// console.log("getSystemSetting==",uni.getSystemInfoSync())
// console.log("getSystemSetting==",uni.getSystemSetting())
//#ifdef APP-PLUS
//获取当前系统版本信息
plus.runtime.getProperty(plus.runtime.appid, (widgetInfo) => {
//请求后台接口 解析数据 对比版本
console.log('widgetInfo==', widgetInfo);
let type = uni.getSystemInfoSync().platform == 'ios' ? 2 : 1;
getVersion({ source: 'manager_app', type: type })
.then((res) => {
console.log('selectNewApp==', res);
console.log('version===', res.url && widgetInfo.version < res.version);
})
updateManager.onUpdateFailed(function() {
// 新版本下载失败
})
// #endif
// console.log("getSystemSetting==",uni.getSystemInfoSync())
// console.log("getSystemSetting==",uni.getSystemSetting())
//#ifdef APP-PLUS
//获取当前系统版本信息
plus.runtime.getProperty(plus.runtime.appid, widgetInfo => {
//请求后台接口 解析数据 对比版本
console.log("widgetInfo==",widgetInfo)
let type = uni.getSystemInfoSync().platform == 'ios' ? 2 : 1
getVersion({source:'manager_app',type: type}).then(res => {
console.log("selectNewApp==",res)
console.log("version===",res.url && widgetInfo.version < res.version)
if (res.url && widgetInfo.version < res.version) {
console.log('version===222');
console.log("version===222")
let downloadLink = res.url;
// let downloadLink = "https://short-video.hnsiyao.cn/app/sy-duanju.apk";
console.log(downloadLink);
console.log(downloadLink)
// let androidLink = res.androidWgtUrl;
// let iosLink = res.iosWgtUrl;
let ready = false;
let ready = false;
// 校验是否强制升级
if (res.isUp == 1) {
uni.showModal({
@@ -61,84 +65,81 @@ onLaunch(() => {
title: '发现新版本',
confirmText: '立即更新',
content: res.message,
success: (res) => {
success: res => {
if (res.confirm) {
uni.showLoading('下载中...');
if (uni.getSystemInfoSync().platform == 'android') {
uni.downloadFile({
url: downloadLink,
success: (downloadResult) => {
if (downloadResult.statusCode === 200) {
plus.io.resolveLocalFileSystemURL(downloadResult.tempFilePath, (entry) => {
entry.getParent((_oldFile) => {
entry.moveTo(_oldFile, '.apk', (newFilePath) => {
console.log('newFilePath', newFilePath.fullPath);
plus.runtime.install(
newFilePath.fullPath,
{ force: false },
(d) => {
console.log('install success...');
plus.runtime.restart();
success: downloadResult => {
if (downloadResult.statusCode === 200) {
plus.io.resolveLocalFileSystemURL(downloadResult.tempFilePath, entry => {
entry.getParent(_oldFile=>{
entry.moveTo(_oldFile,'.apk',newFilePath=>{
console.log('newFilePath',newFilePath.fullPath)
plus.runtime.install(newFilePath.fullPath, { force: false },
d => {
console .log( 'install success...' );
plus.runtime .restart();
},
(e) => {
console.log(e);
console.error('install fail...');
e => {
console.log(e)
console .error( 'install fail...' );
}
);
});
});
});
})
})
})
}
}
});
}
if (uni.getSystemInfoSync().platform == 'ios') {
plus.runtime.openURL(downloadLink, function (res) {});
plus.runtime.openURL(downloadLink, function( res) {});
}
} else if (res.cancel) {
console.log('取消');
}
}
});
} else {
} else {
uni.showModal({
title: '发现新版本',
confirmText: '立即更新',
cancelText: '下次更新',
content: res.message,
success: (res) => {
content: res.message,
success: res => {
if (res.confirm) {
uni.showLoading('下载中...');
if (uni.getSystemInfoSync().platform == 'android') {
uni.downloadFile({
url: downloadLink,
success: (downloadResult) => {
success: downloadResult => {
if (downloadResult.statusCode == 200) {
plus.io.resolveLocalFileSystemURL(downloadResult.tempFilePath, (entry) => {
entry.getParent((_oldFile) => {
entry.moveTo(_oldFile, '.apk', (newFilePath) => {
console.log('newFilePath', newFilePath.fullPath);
plus.runtime.install(
newFilePath.fullPath,
{ force: false },
(d) => {
console.log('install success...');
plus.runtime.restart();
},
(e) => {
console.log(e);
console.error('install fail...');
}
);
});
});
});
plus.io.resolveLocalFileSystemURL(downloadResult.tempFilePath, entry => {
entry.getParent(_oldFile=>{
entry.moveTo(_oldFile,'.apk',newFilePath=>{
console.log('newFilePath',newFilePath.fullPath)
plus.runtime.install(newFilePath.fullPath, { force: false },
d => {
console.log('install success...');
plus.runtime.restart();
},
e => {
console.log(e)
console.error('install fail...');
}
);
})
})
})
}
}
});
}
if (uni.getSystemInfoSync().platform == 'ios') {
plus.runtime.openURL(downloadLink, function (res) {});
plus.runtime.openURL(downloadLink, function( res) {});
}
} else if (res.cancel) {
console.log('取消');
@@ -147,63 +148,23 @@ onLaunch(() => {
});
}
}
}).catch((res)=>{
console.log(res)
})
.catch((res) => {
console.log(res);
});
});
// #endif
});
// #endif
});
</script>
<style lang="scss">
@import 'uview-plus/index.scss';
/** 每个页面公共css */
@import '@/commons/style/global.scss';
@import '@/commons/style/common.scss';
/** 每个页面公共css */
@import '@/commons/style/global.scss';
@import '@/commons/style/common.scss';
/** uni 组件样式覆盖 */
@import '@/commons/style/uni-overwrite.scss';
/** uni 组件样式覆盖 */
@import '@/commons/style/uni-overwrite.scss';
// 优惠券插槽公用样式
.my-coupon-item-list {
padding-bottom: 28upx;
.my-coupon-item-item {
&:not(:first-child) {
margin-top: 28upx;
}
.my-coupon-item-row-wrap {
.row {
display: flex;
&:not(:first-child) {
margin-top: 12upx;
}
.title {
width: 180upx;
font-size: 28upx;
color: #666;
}
.info {
flex: 1;
font-size: 28upx;
color: #333;
padding-left: 28upx;
}
}
}
}
}
// u-form card专用样式
.u-form-card {
background-color: #fff;
border-radius: 20upx;
padding: 0 28upx 14upx;
margin-bottom: 28upx;
:deep(.u-form-item__body__left__content__label.data-v-b4fd400b) {
font-weight: bold;
}
}
</style>
@import "uview-plus/index.scss";
</style>

View File

@@ -308,9 +308,6 @@ text {
.border-r-12{
border-radius: 12rpx;
}
.border-r-16{
border-radius: 16rpx;
}
.border-r-18{
border-radius: 18rpx;
}
@@ -358,18 +355,6 @@ text {
left: 0;
right: 0;
}
.fixed-bottom{
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 30rpx;
padding-bottom: env(safe-area-inset-bottom);
z-index: 999;
/* #ifdef H5 */
padding-bottom: 28rpx;
/* #endif */
}
.lh30 {
line-height: 30px;
}
@@ -418,60 +403,4 @@ text {
::v-deep .u-m-t-16 .u-textarea{
border-width: 1px!important;
}
.font-700{
font-weight: 700;
}
.text-center{
text-align: center;
}
.bg-f7{
background-color: #F7F7F7;
}
.default-box-padding{
padding: 32rpx 28rpx;
}
.default-box-radius{
border-radius: 16rpx;
}
.default-box-x-padding{
padding-left: 28rpx;
padding-right: 28rpx;
}
.default-box-y-padding{
padding-top: 32rpx;
padding-bottom: 32rpx;
}
$height: 70rpx;
.number-box {
font-size: 28rpx;
padding: 0 26rpx;
border-radius: 6rpx 0 0 6rpx;
border-top: 2rpx solid #d9d9d9;
border-bottom: 2rpx solid #d9d9d9;
border-left: 2rpx solid #d9d9d9;
background: #fff;
box-sizing: border-box;
height: $height;
flex: 1;
line-height: $height;
}
.unit {
display: flex;
padding: 0 38rpx;
height: $height;
line-height: $height;
align-items: center;
border-radius: 0 6rpx 6rpx 0;
border: 2rpx solid #d9d9d9;
background: #f7f7fa;
font-size: 28rpx;
color: #999999;
}
.u-col-baseline{
align-items: baseline;
}
.text-right{
text-align: right;
}
}

View File

@@ -40,7 +40,7 @@ $v-b-color-ed: #ededed;
//common.scss 分包页面以及组件里所用的颜色
$my-main-color:#318AFE;
$my-red-color:#FE4F1E;
$my-red-color:#F02C45;
//my-components
$u-main-color: #303133;

View File

@@ -5,7 +5,7 @@
* @site https://www.jeepay.vip
* @date 2021/5/16 17:35
*/
// import { SM4 } from 'gm-crypto'
import { SM4 } from 'gm-crypto'
import appConfig from '@/config/appConfig.js'
let HEX_KEY = null

View File

@@ -1,5 +1,14 @@
export function encrypt(txt) {
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
return txt
// 密钥对生成 http://web.chacuo.net/netrsakeypair
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD\n' +
'2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ=='
// 加密
export function encrypt(txt) {
const encryptor = new JSEncrypt()
encryptor.setPublicKey(publicKey) // 设置公钥
return encryptor.encrypt(txt) // 对需要加密的数据进行加密
}

View File

@@ -19,7 +19,6 @@ const model = {
setLogin(res){
uni.setStorageSync('shopInfo',res.shopInfo)
uni.setStorageSync('shopStaff',res.shopStaff)
uni.setStorageSync('loginType',res.loginType)
if(res.loginType=='1'){
// uni.setStorageSync('merchantName',user.createBy||user.updateBy)

View File

@@ -5,191 +5,161 @@
@date 2022/11/16 11:45
-->
<template>
<!-- 宫格类型 -->
<template v-if="props.type == 'grid'">
<view class="grid-card-wrapper" :style="{ margin: calcPadding(), '--space-w': space + 'rpx' }">
<block v-for="(v, i) in navListComputed" :key="i">
<view class="card-main" :style="{ margin: calcMargin(), '--radius-size': radiusSize + 'rpx' }"
hover-class="touch-hover" hover-stay-time="150"
:class="{ 'card-big-main': navListComputed.length == 4 || navListComputed.length <= 2 }"
@tap="clickFunc(v)">
<image :src="v.icon" mode="scaleToFill" />
<view class="card-title">{{ v.title }}</view>
</view>
</block>
</view>
</template>
<!-- 宫格类型 -->
<template v-if="props.type == 'grid'">
<view class="grid-card-wrapper" :style="{ margin: calcPadding(), '--space-w': space + 'rpx' }">
<block v-for="(v, i) in navListComputed" :key="i">
<view
class="card-main"
:style="{ margin: calcMargin(), '--radius-size': radiusSize + 'rpx' }"
hover-class="touch-hover"
hover-stay-time="150"
:class="{ 'card-big-main': navListComputed.length == 4 || navListComputed.length <= 2 }"
@tap="clickFunc(v)"
>
<image :src="v.icon" mode="scaleToFill" />
<view class="card-title">{{ v.title }}</view>
</view>
</block>
</view>
</template>
<!-- 列表类型 -->
<template v-if="props.type == 'list'">
<view class="list-nav-wrapper" :style="{ '--radius-size': radiusSize + 'rpx' }">
<block v-for="(v, i) in navListComputed" :key="i">
<view class="nav-main" hover-class="touch-hover" hover-stay-time="150" @tap="clickFunc(v)">
<image :src="v.icon" mode="scaleToFill" />
<view class="nav-text">{{ v.title }}</view>
<image class="nav-right" src="/static/iconImg/icon-arrow-right.svg" mode="scaleToFill" />
</view>
</block>
</view>
</template>
<!-- 列表类型 -->
<template v-if="props.type == 'list'">
<view class="list-nav-wrapper" :style="{ '--radius-size': radiusSize + 'rpx' }">
<block v-for="(v, i) in navListComputed" :key="i">
<view class="nav-main" hover-class="touch-hover" hover-stay-time="150" @tap="clickFunc(v)">
<image :src="v.icon" mode="scaleToFill" />
<view class="nav-text">{{ v.title }}</view>
<image class="nav-right" src="/static/iconImg/icon-arrow-right.svg" mode="scaleToFill" />
</view>
</block>
</view>
</template>
</template>
<script setup>
import {
useMenusStore
} from '@/store/menus.js'
const menusStore = useMenusStore()
import {
reactive,
ref,
computed
} from "vue"
import go from "@/commons/utils/go.js"
import ent from '@/commons/utils/ent.js'
import { reactive, ref, computed } from "vue"
import go from "@/commons/utils/go.js"
import ent from '@/commons/utils/ent.js'
import {
hasPermission
} from '@/commons/utils/hasPermission.js';
// 定义组件参数
const props = defineProps({
//显示类型: 支持 grid-宫格 list-列表
type: {
type: String,
default: "list"
},
// 定义组件参数
const props = defineProps({
//显示类型: 支持 grid-宫格 list-列表
type: { type: String, default: "list" },
// 圆角矩形大小 为0 则 无圆角
radiusSize: {
type: Number,
default: 32
},
// 圆角矩形大小 为0 则 无圆角
radiusSize: { type: Number, default: 32 },
//间隙类型: 仅grid-宫格时生效, 支持 0 和 25
space: {
type: Number,
default: 25
},
//间隙类型: 仅grid-宫格时生效, 支持 0 和 25
space: { type: Number, default: 25 },
// 导航列表, 格式:{ icon, title, pageUrl, clickFunc, entId }
navList: {
type: Array,
default: () => []
},
})
// 导航列表, 格式:{ icon, title, pageUrl, clickFunc, entId }
navList: { type: Array, default: () => [] },
})
// 点击事件
async function clickFunc(nav) {
if (nav.pageUrl == "PAGES_DATA_SUMMARY") {
let res = await hasPermission('允许查看经营数据')
if (!res) return
}
// 包含回调事件
if (nav.clickFunc) {
return nav.clickFunc(nav)
}
// 包含URL
if (nav.pageUrl) {
return go.to(nav.pageUrl)
}
// 点击事件
async function clickFunc(nav) {
if(nav.pageUrl=="PAGES_SALES_SUMMARY"){
let res =await hasPermission('允许查看经营数据')
if(!res) return
}
const calcPadding = () => `${50 - props.space}rpx 35rpx 50rpx ${35 - props.space}rpx`
const calcMargin = () => `${props.space}rpx 0 0 ${props.space}rpx`
// 包含回调事件
if (nav.clickFunc) {
return nav.clickFunc(nav)
}
// 包含URL
if (nav.pageUrl) {
return go.to(nav.pageUrl)
}
}
const calcPadding = () => `${50 - props.space}rpx 35rpx 50rpx ${35 - props.space}rpx`
const calcMargin = () => `${props.space}rpx 0 0 ${props.space}rpx`
// 计算属性
let navListComputed = computed(() => {
// return props.navList.filter(r => hasEnt(r.entId))
// return props.navList.filter(r => {
// if (r.hasOwnProperty('visiable')) {
// return r.visiable
// } else {
// return true
// }
// })
return props.navList.filter(r => {
return menusStore.menuList.find(menu=>menu.pageId==r.pageUrl)
})
})
// 计算属性
let navListComputed = computed(() => {
return props.navList.filter(r => hasEnt(r.entId))
})
function hasEnt(entId) {
// 不包含: 说明无需隐藏
if (!entId) {
return true
}
return ent.has(entId)
function hasEnt(entId){
// 不包含: 说明无需隐藏
if(!entId){
return true
}
return ent.has(entId)
}
</script>
<style lang="scss" scoped>
.grid-card-wrapper {
display: flex;
flex-wrap: wrap;
.card-main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 25rpx 0 0 25rpx;
width: calc(33.3% - var(--space-w));
height: 210rpx;
border-radius: var(--radius-size);
background-color: $J-bg-ff;
.grid-card-wrapper {
display: flex;
flex-wrap: wrap;
background-color: $v-color-bgrey;
padding-top: 25rpx;
.card-main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 25rpx 0 0 25rpx;
width: calc(33.3% - var(--space-w));
height: 210rpx;
border-radius: var(--radius-size);
background-color: $J-bg-ff;
image {
width: 72rpx;
height: 72rpx;
}
.card-title {
margin-top: 22rpx;
color: $J-color-t80;
font-size: $J-f-size30;
}
}
.card-big-main {
width: calc(50% - var(--space-w));
}
}
image {
width: 72rpx;
height: 72rpx;
}
.list-nav-wrapper {
margin: 0 auto;
width: 680rpx;
border-radius: var(--radius-size);
overflow: hidden;
.card-title {
margin-top: 22rpx;
color: $J-color-t80;
font-size: $J-f-size30;
}
}
.card-big-main {
width: calc(50% - var(--space-w));
}
}
.list-nav-wrapper {
margin: 0 auto;
width: 680rpx;
border-radius: var(--radius-size);
overflow: hidden;
.nav-main {
display: flex;
align-items: center;
padding-left: 30rpx;
height: 120rpx;
background-color: $J-bg-ff;
image {
width: 40rpx;
height: 40rpx;
vertical-align: text-bottom;
margin: 0 20rpx 0 10rpx;
}
.nav-text {
flex: 1;
font-size: 32rpx;
}
.nav-right {
margin: 0;
width: 120rpx;
height: 120rpx;
}
}
.nav-setup {
margin: 30rpx auto;
width: 680rpx;
border-radius: $J-b-r32;
box-sizing: border-box;
}
}
</style>
.nav-main {
display: flex;
align-items: center;
padding-left: 30rpx;
height: 120rpx;
background-color: $J-bg-ff;
image {
width: 40rpx;
height: 40rpx;
vertical-align: text-bottom;
margin: 0 20rpx 0 10rpx;
}
.nav-text {
flex: 1;
font-size: 32rpx;
}
.nav-right {
margin: 0;
width: 120rpx;
height: 120rpx;
}
}
.nav-setup {
margin: 30rpx auto;
width: 680rpx;
border-radius: $J-b-r32;
box-sizing: border-box;
}
}
</style>

View File

@@ -1,52 +0,0 @@
#时间范围选择器
#### 参数文档
| 参数 | 说明 | 类型 | 默认值 | 其他 |
| :---- | :---- | :---- | :---- | :---- |
| show | 显示选择器 | Boolean | false | - |
| defaultDate | 默认日期 | String | - | 不传则默认今天 |
| minYear | 最小年份 | Number | 1990 | - |
| themeColor | 主题色 | String | #43b983 | - |
| startText | 开始时间文字 | String | 开始时间 | - |
| endText | 结束时间文字 | String | 结束时间 | - |
#### case
```vue
<template>
<view style="padding: 30rpx;">
<view style="margin-top: 30rpx" @click="show=true">
显示日期选择器
</view>
<view style="margin-top: 30rpx">
所选日期 {{ date.join(',') }}
</view>
<dateRangePicker
:show="show"
:minYear="2022"
@close="show=false"
@confirm="confirm"
>
</dateRangePicker>
</view>
</template>
<script>
import dateRangePicker from '@/components/date-range-picker/date-range-picker.vue'
export default {
components: {dateRangePicker},
data() {
return {
show: false,
date: []
}
},
methods: {
confirm(v) {
console.log(v);
this.date = v
}
}
}
</script>
```

View File

@@ -1,342 +0,0 @@
<template>
<view :class="{'remark':show}" :style="{'--theme-color': themeColor}" @click="close" @touchmove.stop.prevent="returnHandle">
<view class="picker-box" :class="{show: show}">
<view class="operate-box" @touchmove.stop.prevent="returnHandle" @tap.stop="returnHandle">
<view @click="touchSelect(0)" class="time-item" :style="{color:touchIndex?'#303030':themeColor}">
<view class="label">{{ startText }}</view>
<view class="date">{{ resultDate[0] }}</view>
</view>
<view></view>
<view @click="touchSelect(1)" class="time-item" :style="{color:touchIndex?themeColor:'#303030'}">
<view class="label">{{ endText }}</view>
<view class="date">{{ resultDate[1] }}</view>
</view>
</view>
<picker-view
:value="pickerValue"
@change="pickerChange"
class="picker-view"
:immediate-change="true"
indicator-class="select-line"
:indicator-style="indicatorStyle"
mask-style="background: transparent"
@tap.stop="returnHandle"
>
<picker-view-column class="column-left">
<view class="picker-item" :class="index == pickerValue[0] ? 'picker-select' : ''" v-for="(item, index) in years" :key="index">
{{ item }}
</view>
</picker-view-column>
<picker-view-column class="column-center">
<view class="picker-item" :class="index == pickerValue[1] ? 'picker-select' : ''" v-for="(item, index) in months" :key="index">
{{ item }}
</view>
</picker-view-column>
<picker-view-column class="column-right" v-if="days.length > 0">
<view class="picker-item" :class="index == pickerValue[2] ? 'picker-select' : ''" v-for="(item, index) in days" :key="index">
{{ item }}
</view>
</picker-view-column>
</picker-view>
<view class="button-group">
<view class="item cancel" @click.stop="close">取消</view>
<view class="item confirm" @click.stop="pickerConfirm">确认</view>
</view>
</view>
</view>
</template>
<script>
const date = new Date();
const years = [];
const currentYear = date.getFullYear();
const months = [];
const currentMonth = date.getMonth() + 1;
const currentDay = date.getDate();
export default {
name: 'dateRangePicker',
props: {
show: {
type: Boolean,
default: false
},
defaultDate: {
type: Array,
default: () => []
},
minYear: {
type: Number,
default: 1990,
},
themeColor: {
type: String,
default: '#43b983'
},
startText: {
type: String,
default: '开始时间'
},
endText: {
type: String,
default: '结束时间'
}
},
data() {
for (let i = this.minYear; i <= currentYear; i++) {
years.push(i);
}
for (let i = 1; i <= 12; i++) {
months.push(this.padStart(i));
}
return {
indicatorStyle: `height: ${uni.upx2px(84)}px`,
touchIndex: 0,
year: currentYear,
month: currentMonth,
day: currentDay,
years,
months,
days: [],
pickerValue: [],
resultDate: []
};
},
mounted() {
this.setDate()
},
methods: {
returnHandle() {},
setDate() {
if (this.defaultDate.length > 0) {
const date = this.defaultDate[0]
this.resultDate = this.defaultDate
this.setPicker(date)
} else {
const month = this.month.toString().padStart(2, 0)
const day = this.day.toString().padStart(2, 0)
const nowTime = `${this.year}-${month}-${day}`
this.resultDate = [nowTime, nowTime]
this.setPicker(nowTime)
}
},
setPicker(date) {
const splitVal = date.split('-')
const year = this.years.indexOf(Number(splitVal[0]))
const month = Number(splitVal[1]) - 1
const day = Number(splitVal[2]) - 1
this.pickerChange({
detail: {
value: [year, month, day]
}
})
},
touchSelect(val) {
const date = this.resultDate[val]
this.touchIndex = val
this.setPicker(date)
},
getDateTime(date) {
const year = this.years[date[0]]
const month = this.months[Number(date[1])] || this.padStart(currentMonth)
const day = this.days[Number(date[2])] || this.padStart(currentDay)
this.resultDate[this.touchIndex] = `${year}-${month}-${day}`
},
pickerChange(e) {
const currents = e.detail.value
// 月份处理,限制到当前月份
if (this.years[currents[0]] === currentYear) {
const allmonths = JSON.parse(JSON.stringify(months))
const m = allmonths.splice(0, currentMonth)
this.months = m
if(currents[1] > currentMonth - 1) {
currents[1] = currentMonth - 1
}
} else {
this.months = months
}
// 日期天数处理
let days = []
if (currents[1] + 1 === 2) {
if (
((currents[0] + this.minYear) % 4 === 0 &&
(currents[0] + this.minYear) % 100 !== 0) ||
(currents[0] + this.minYear) % 400 === 0
) {
for (let i = 1; i < 30; i++) {
days.push(this.padStart(i))
}
} else {
for (let i = 1; i < 29; i++) {
days.push(this.padStart(i))
}
}
} else if ([4, 6, 9, 11].some((item) => currents[1] + 1 === item)) {
for (let i = 1; i < 31; i++) {
days.push(this.padStart(i))
}
} else if ([1, 3, 5, 7, 8, 10, 12].some((item) => currents[1] + 1 === item)) {
for (let i = 1; i < 32; i++) {
days.push(this.padStart(i))
}
}
// 限制到当前日期
if (this.years[currents[0]] === currentYear && this.months[currents[1]]*1 === currentMonth) {
days = days.splice(0, currentDay)
if(currents[2] > currentDay - 1) {
currents[2] = currentDay - 1
}
}
this.days = days
this.pickerValue = currents
this.getDateTime(currents)
},
close() {
this.$emit('close', false)
},
pickerConfirm() {
const { resultDate } = this
let startTime = new Date(resultDate[0]).getTime()
let endTime = new Date(resultDate[1]).getTime()
let nowTime = endTime
if (startTime <= endTime && endTime <= nowTime) {
this.$emit('confirm', resultDate)
this.close()
return
}
if (startTime > endTime) {
uni.showToast({
title: '开始时间应小于结束时间',
icon: 'none',
duration: 3500
})
}
if (endTime > nowTime) {
uni.showToast({
title: '请正确选择时间范围',
icon: 'none'
})
}
},
padStart(val) {
return val.toString().padStart(2, 0)
},
}
}
</script>
<style lang="scss" scoped>
::v-deep.column-left,
::v-deep.column-center,
::v-deep.column-right {
.select-line {
background: #F9FAFC;
z-index: -1;
&::before, &::after {
border: none ;
}
}
}
::v-deep.column-left .select-line {
border-radius: 42rpx 0 0 42rpx;
}
::v-deep.column-right .select-line {
border-radius: 0 42rpx 42rpx 0;
}
.remark {
position: fixed;
z-index: 998;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
}
.picker-box {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
transition: all 0.3s ease;
transform: translateY(100%);
padding: 0 30rpx;
box-sizing: border-box;
background-color: #FFFFFF;
z-index: 998;
border-radius: 24rpx 24rpx 0 0;
overflow: hidden;
padding-bottom: calc(40rpx + constant(safe-area-inset-bottom)/2) !important;
padding-bottom: calc(40rpx + env(safe-area-inset-bottom)/2) !important;
&.show {
transform: translateY(0);
}
.operate-box {
display: flex;
align-items: center;
justify-content: space-around;
padding: 34rpx 30rpx 20rpx;
background-color: #FFFFFF;
text-align: center;
border-bottom: 2rpx solid #f6f6f6;
.label {
font-size: 26rpx;
}
.date {
font-size: 32rpx;
}
}
.picker-view {
width: 100%;
height: 420rpx;
background-color: #FFFFFF;
.picker-item {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
transition: all 0.2s ease;
height: 84rpx;
line-height: 84rpx;
font-size: 32rpx;
color: rgba(94, 104, 128, 0.6);
&.picker-select {
color: var(--theme-color);
font-size: 38rpx;
transition: all 0.2s ease;
}
}
}
.button-group {
display: flex;
justify-content: space-around;
margin-top: 30rpx;
.item {
width: 280rpx;
height: 84rpx;
text-align: center;
line-height: 84rpx;
border-radius: 42rpx;
&.cancel {
background: #f8f8f8;
color: #333;
}
&.confirm {
background: var(--theme-color);
color: #fff;
}
}
}
}
</style>

View File

@@ -1,223 +1,201 @@
<template>
<my-model ref="model" :title="title" iconColor="#000" @close="resetForm">
<template #desc>
<view class="u-text-left u-p-30 color-666">
<view class="u-m-t-32 u-flex">
<view>应付金额</view>
<view class="u-m-l-32">
{{ form.price }}
</view>
</view>
<view class="u-m-t-40 u-flex">
<view>实收金额</view>
<view class="u-m-l-32 border u-p-l-10 u-p-r-10 u-flex-1">
<uni-easyinput
type="number"
@input="currentPriceInput"
@change="currentPriceChange"
paddingNone
:inputBorder="false"
v-model="form.currentPrice"
placeholder="输入实际金额"
></uni-easyinput>
</view>
</view>
<view class="u-m-t-54 u-flex">
<view>优惠折扣</view>
<view class="u-m-l-32 u-flex-1 u-flex border u-p-l-10 u-p-r-10">
<view class="u-flex-1">
<uni-easyinput
type="number"
@input="discountInput"
@change="discountChange"
paddingNone
:inputBorder="false"
v-model="form.discount"
placeholder="输入折扣"
></uni-easyinput>
</view>
<view class="u-font-32 color-333">%</view>
</view>
</view>
</view>
</template>
<template #btn>
<view class="u-p-30">
<view class="u-m-t-10">
<my-button @tap="confirm" shape="circle" fontWeight="700"
>修改</my-button
>
<view class="">
<my-button @tap="close" type="cancel" bgColor="#fff"
>取消</my-button
>
</view>
</view>
</view>
</template>
</my-model>
<my-model ref="model" :title="title" iconColor="#000" @close="resetForm">
<template #desc>
<view class="u-text-left u-p-30 color-666">
<view class="u-m-t-32 u-flex ">
<view>应付金额</view>
<view class="u-m-l-32">
{{form.price}}
</view>
</view>
<view class="u-m-t-40 u-flex ">
<view>实收金额</view>
<view class="u-m-l-32 border u-p-l-10 u-p-r-10 u-flex-1">
<uni-easyinput type="number" @input="currentPriceInput" @change="currentPriceChange" paddingNone :inputBorder="false"
v-model="form.currentPrice"
placeholder="输入实际金额"></uni-easyinput>
</view>
</view>
<view class="u-m-t-54 u-flex ">
<view>优惠折扣</view>
<view class="u-m-l-32 u-flex-1 u-flex border u-p-l-10 u-p-r-10">
<view class="u-flex-1">
<uni-easyinput type="number" @input="discountInput" @change="discountChange" paddingNone :inputBorder="false"
v-model="form.discount"
placeholder="输入折扣"></uni-easyinput>
</view>
<view class="u-font-32 color-333">%</view>
</view>
</view>
</view>
</template>
<template #btn>
<view class="u-p-30">
<view class="u-m-t-10">
<my-button @tap="confirm" shape="circle" fontWeight="700" >修改</my-button>
<view class="">
<my-button @tap="close" type="cancel" bgColor="#fff" >取消</my-button>
</view>
</view>
</view>
</template>
</my-model>
</template>
<script setup>
import { reactive, nextTick, ref, watch } from "vue";
import myModel from "@/components/my-components/my-model.vue";
import myButton from "@/components/my-components/my-button.vue";
import infoBox from "@/commons/utils/infoBox.js";
const props = defineProps({
title: {
type: String,
default: "",
},
discount: {
type: [Number, String],
default: 100,
},
price: {
type: [Number, String],
default: 0,
},
});
function currentPriceInput(newval) {
form.discount = ((newval * 100) / form.price).toFixed(2);
}
function discountInput(newval) {
const currentPrice = uni.$utils.isMoney((form.price * newval) / 100) * 1;
form.currentPrice = currentPrice.toFixed(2);
}
function currentPriceChange(newval) {
if (newval < 0) {
form.currentPrice = "0.00";
form.discount = 100;
return infoBox.showToast("实收金额不能小于0");
}
console.log(props.price);
console.log(newval);
if (newval > props.price) {
const currentPrice = uni.$utils.isMoney(props.price * 1);
form.currentPrice = currentPrice.toFixed(2);
form.discount = 0;
return infoBox.showToast("实收金额不能大于应付金额");
}
}
function discountChange(newval) {
if (newval < 0) {
form.currentPrice = props.price;
form.discount = 0;
return infoBox.showToast("优惠折扣不能小于0");
}
if (newval > 100) {
form.discount = 100;
form.currentPrice = 0;
return infoBox.showToast("优惠折扣不能大于100");
}
}
import { reactive, nextTick, ref,watch } from 'vue';
import myModel from '@/components/my-components/my-model.vue'
import myButton from '@/components/my-components/my-button.vue'
import infoBox from '@/commons/utils/infoBox.js'
const props = defineProps({
title: {
type: String,
default: ''
},
discount:{
type: [Number,String],
default:100
},
price: {
type: [Number,String],
default: 0
},
})
function currentPriceInput(newval){
form.discount = (newval*100/form.price).toFixed(2)
}
function discountInput(newval){
form.currentPrice= uni.$utils.isMoney(form.price*newval/100).toFixed(2)
}
function currentPriceChange(newval){
if(newval<0){
form.currentPrice = '0.00'
form.discount=100
return infoBox.showToast('实收金额不能小于0')
}
console.log(props.price)
console.log(newval)
if(newval > props.price){
form.currentPrice = (uni.$utils.isMoney(props.price)*1).toFixed(2)
form.discount=0
return infoBox.showToast('实收金额不能大于应付金额')
}
}
function discountChange(newval){
if(newval<0){
form.currentPrice=props.price
form.discount=0
return infoBox.showToast('优惠折扣不能小于0')
}
if(newval>100){
form.discount=100
form.currentPrice=0
return infoBox.showToast('优惠折扣不能大于100')
}
}
const $form = {
price: props.price,
currentPrice: props.price,
discount: 100
}
const form = reactive({
...$form
})
watch(()=>props.price,(newval)=>{
form.price = (newval*1).toFixed(2)
form.currentPrice=newval
})
function resetForm() {
Object.assign(form, {
...$form
})
}
const $form = {
price: props.price,
currentPrice: props.price,
discount: 100,
};
const form = reactive({
...$form,
});
watch(
() => props.price,
(newval) => {
form.price = (newval * 1).toFixed(2);
form.currentPrice = newval;
}
);
function resetForm() {
Object.assign(form, {
...$form,
});
}
const model = ref(null)
const model = ref(null);
function open() {
model.value.open()
form.price= (props.price*1).toFixed(2)
form.discount=props.discount
form.currentPrice=(props.discount*props.price/100).toFixed(2)
console.log(form)
}
function open() {
model.value.open();
form.price = (props.price * 1).toFixed(2);
form.discount = props.discount;
form.currentPrice = ((props.discount * props.price) / 100).toFixed(2);
console.log(form);
}
function close() {
model.value.close()
}
const emits = defineEmits(['confirm'])
function close() {
model.value.close();
}
const emits = defineEmits(["confirm"]);
function confirm() {
emits("confirm", {
...form,
currentPrice: Number(form.currentPrice).toFixed(2),
});
close();
}
defineExpose({
open,
close,
});
function confirm() {
emits('confirm',{...form,currentPrice:Number(form.currentPrice).toFixed(2)})
close()
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.border {
border-radius: 8rpx;
overflow: hidden;
border-color: #999;
}
.lh34 {
line-height: 34rpx;
}
.border{
border-radius: 8rpx;
overflow: hidden;
border-color: #999;
}
.lh34 {
line-height: 34rpx;
}
.tag {
background-color: #fff;
border: 1px solid #e5e5e5;
line-height: inherit;
font-size: 24rpx;
color: #666666;
padding: 6rpx 20rpx;
border-radius: 8rpx;
.tag {
background-color: #fff;
border: 1px solid #E5E5E5;
line-height: inherit;
font-size: 24rpx;
color: #666666;
padding: 6rpx 20rpx;
border-radius: 8rpx;
&.active {
border-color: #e6f0ff;
color: $my-main-color;
}
}
&.active {
border-color: #E6F0FF;
color: $my-main-color;
}
}
.hover-class {
background-color: #e5e5e5;
}
.hover-class {
background-color: #E5E5E5;
}
.discount {
.u-absolute {
top: 0;
bottom: 0;
right: 0;
}
}
.discount {
.u-absolute {
top: 0;
bottom: 0;
right: 0;
}
}
.bg1 {
background: #f7f7fa;
}
.bg1 {
background: #F7F7FA;
}
.tab {
padding: 0 80rpx;
}
.tab {
padding: 0 80rpx;
}
.border {
border: 1px solid #e5e5e5;
border-radius: 4rpx;
}
.border {
border: 1px solid #E5E5E5;
border-radius: 4rpx;
}
.input-box {
padding: 22rpx 32rpx;
font-size: 28rpx;
color: #666;
}
.input-box {
padding: 22rpx 32rpx;
font-size: 28rpx;
color: #666;
}
.placeholder-class {
font-size: 28rpx;
}
</style>
.placeholder-class {
font-size: 28rpx;
}
</style>

View File

@@ -1,68 +0,0 @@
<template>
<view v-if="isShow">
<view class="zhanwei" :class="[direction == 'column' ? 'zhanwei1' : '']"></view>
<view
class="fixed-bottom u-flex gap-20"
:class="[direction == 'column' ? 'u-flex-column' : '']"
>
<view class="u-flex-1">
<my-button type="primary" @click="save" shape="circle">
保存
</my-button>
</view>
<view class="u-flex-1">
<my-button bgColor="#fff" type="default" @click="cancel" shape="circle">
取消
</my-button>
</view>
</view>
</view>
</template>
<script setup>
import { computed } from "vue";
const emit = defineEmits(["save", "cancel"]);
import { isMainShop } from "@/store/account.js";
const props = defineProps({
isOpenPermission: {
type: Boolean,
default: false,
},
//方向 row横向布局 column 纵向布局
direction: {
type: String,
default: "row",
},
});
const isShow = computed(() => {
if (props.isOpenPermission) {
return isMainShop();
}
return true;
});
function save() {
emit("save");
}
function cancel() {
emit("cancel");
}
</script>
<style lang="scss">
.zhanwei {
height: 180rpx;
}
.zhanwei1{
height: 240rpx;
}
.fixed-bottom {
&.u-flex-column{
align-items: stretch;
}
}
</style>

View File

@@ -1,142 +0,0 @@
<!-- 优惠券item -->
<template>
<view class="item">
<view class="header">
<text class="title" v-if="item.couponType == 5">消费赠券</text>
<text v-else>{{ item.title }}</text>
<text class="id">ID:{{ item.id }}</text>
</view>
<view class="content">
<slot></slot>
</view>
<view class="total-info">
<view class="item">
<text class="info">
<template v-if="item.giveNum == -10086">无限</template>
<template v-else>{{ item.giveNum }}</template>
</text>
<text class="title">总发放</text>
</view>
<view class="item" v-if="item.couponType == 5">
<text class="info">{{ item.giftNum }}</text>
<view class="title">
<text class="t">已赠送</text>
<text class="l" @click="toDetail(item)">详情</text>
</view>
</view>
<template v-else>
<view class="item">
<text class="info">{{ item.giftNum }}</text>
<text class="title">已领取</text>
</view>
<view class="item">
<text class="info">{{ item.useNum }}</text>
<text class="title">已使用</text>
</view>
</template>
<view class="item">
<text class="info">
<template v-if="item.giveNum == -10086">无限</template>
<template v-else>{{ item.leftNum }}</template>
</text>
<text class="title">剩余</text>
</view>
</view>
<view class="footer-wrap">
<view class="btn">
<u-button shape="circle" @click="emits('delete', item)">删除</u-button>
</view>
<view class="btn">
<u-button shape="circle" type="primary" @click="emits('editor', item)">编辑</u-button>
</view>
</view>
</view>
</template>
<script setup>
import go from '@/commons/utils/go.js';
const props = defineProps({
item: {
type: Object,
default: {}
}
});
const emits = defineEmits(['delete', 'editor']);
// 去领取详情
function toDetail(item) {
go.to('PAGES_COUPON_GET_DETAIL', { couponId: item.couponGiftList[0].couponId });
}
</script>
<style scoped lang="scss">
.item {
background-color: #fff;
border-radius: 20upx;
padding: 28upx;
.header {
display: flex;
justify-content: space-between;
.title {
font-size: 32upx;
color: #333;
}
.id {
font-size: 24upx;
color: #999;
background-color: #f8f8f8;
display: flex;
align-items: center;
justify-content: center;
padding: 0 8upx;
border-radius: 4upx;
}
}
.content {
margin-top: 28upx;
padding: 20upx;
background-color: #f8f8f8;
border-radius: 12upx;
}
.total-info {
display: flex;
.item {
flex: 1;
display: flex;
gap: 12upx;
align-items: center;
justify-content: center;
flex-direction: column;
.info {
font-weight: bold;
font-size: 28upx;
color: #333;
}
.title {
font-size: 24upx;
color: #999;
display: flex;
gap: 28upx;
.t {
font-size: 24upx;
color: #999;
}
.l {
font-size: 24upx;
color: #318afe;
}
}
}
}
.footer-wrap {
display: flex;
justify-content: flex-end;
gap: 28upx;
.btn {
width: 200upx;
}
}
}
</style>

View File

@@ -1,597 +0,0 @@
<template>
<view class="mask" v-if="show" @tap="close">
<view class="box" @tap.stop="nullFunction">
<view
class="u-flex u-relative u-row-center u-p-30 top"
v-if="props.isArea"
>
<view class="font-bold u-font-32">{{ props.title }}</view>
<view class="close" @tap="close">
<uni-icons type="closeempty" size="24"></uni-icons>
</view>
</view>
<view
class="u-p-30 u-flex u-flex-wrap gap-20 fastTime"
v-if="props.isArea"
>
<view
class="item"
v-for="(item, index) in fastTime"
:key="index"
@tap="changeTime(item)"
>
{{ item.label }}
</view>
</view>
<picker-view
:immediate-change="true"
@pickend="pickend"
:value="value"
@change="bindChange"
class="picker-view"
>
<template v-if="props.mode === 'all' || props.mode === 'date'">
<picker-view-column>
<view class="item" v-for="(item, index) in years" :key="index"
>{{ item }}</view
>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in months" :key="index"
>{{ item }}</view
>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in days" :key="index"
>{{ item }}</view
>
</picker-view-column>
</template>
<template v-if="props.mode === 'all' || props.mode === 'time'">
<picker-view-column>
<view class="item" v-for="(item, index) in hours" :key="index"
>{{ item }}</view
>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in minutes" :key="index"
>{{ item }}</view
>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in seconds" :key="index"
>{{ item }}</view
>
</picker-view-column>
</template>
</picker-view>
<template v-if="props.isArea">
<view class="u-text-center color-999"></view>
<picker-view
:immediate-change="true"
:value="value1"
@pickend="pickend1"
@change="bindChange1"
class="picker-view"
>
<template v-if="props.mode === 'all' || props.mode === 'date'">
<picker-view-column>
<view class="item" v-for="(item, index) in years" :key="index"
>{{ item }}</view
>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in months" :key="index"
>{{ item }}</view
>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in days1" :key="index"
>{{ item }}</view
>
</picker-view-column>
</template>
<template v-if="props.mode === 'all' || props.mode === 'time'">
<picker-view-column>
<view class="item" v-for="(item, index) in hours" :key="index"
>{{ item }}</view
>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in minutes" :key="index"
>{{ item }}</view
>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in seconds" :key="index"
>{{ item }}</view
>
</picker-view-column>
</template>
</picker-view>
</template>
<!-- 占位 -->
<view style="height: 80px"></view>
<template v-if="props.isArea">
<view class="fixed_b">
<my-button shape="circle" @tap="confirm">确定</my-button>
</view>
</template>
<template v-else>
<view class="fixed_b u-flex u-row-center">
<view class="u-m-r-16">
<my-button type="cancel" @tap="close" width="240">
<view class="color-999">取消</view>
</my-button>
</view>
<view class="u-m-l-16">
<button @tap="confirm">确定</button>
</view>
</view>
</template>
</view>
</view>
</template>
<script setup>
import { reactive, nextTick, ref } from "vue";
import dayjs from "dayjs";
const $time = {
// 获取今天的开始和结束时间
getTodayTimestamps() {
const start = dayjs().startOf("day").format("YYYY-MM-DD HH:mm:ss");
const end = dayjs().endOf("day").format("YYYY-MM-DD HH:mm:ss");
return { start, end, label: "今日" };
},
// 获取昨天的开始和结束时间
getYesterdayTimestamps() {
const start = dayjs()
.subtract(1, "day")
.startOf("day")
.format("YYYY-MM-DD HH:mm:ss");
const end = dayjs()
.subtract(1, "day")
.endOf("day")
.format("YYYY-MM-DD HH:mm:ss");
return { start, end, label: "昨日" };
},
// 获取本周的开始和结束时间
getThisWeekTimestamps() {
const start = dayjs().startOf("week").format("YYYY-MM-DD HH:mm:ss");
const end = dayjs().endOf("week").format("YYYY-MM-DD HH:mm:ss");
return { start, end, label: "本周" };
},
// 获取本月的开始和结束时间
getThisMonthTimestamps() {
const start = dayjs().startOf("month").format("YYYY-MM-DD HH:mm:ss");
const end = dayjs().endOf("month").format("YYYY-MM-DD HH:mm:ss");
return { start, end, label: "本月" };
},
// 获取上个月的开始和结束时间
getThisLastMonthTimestamps() {
const start = dayjs()
.subtract(1, "month")
.startOf("month")
.format("YYYY-MM-DD HH:mm:ss");
const end = dayjs()
.subtract(1, "month")
.endOf("month")
.format("YYYY-MM-DD HH:mm:ss");
return { start, end, label: "上月" };
},
};
const props = defineProps({
selTime: {
type: [String, Number],
},
defaultIndex: {
type: Array,
default: () => {
return [];
},
},
defaultTime: {
type: Array,
default: () => {
return [];
},
},
title: {
type: String,
default: "筛选日期时间",
},
isArea: {
//是否选中范围时间
type: Boolean,
default: true,
},
mode: {
//all date time
type: String,
default: "time",
},
yearsLen: {
type: Number,
default: 30,
},
});
const $nowDate = new Date();
const nowDate = {
year: $nowDate.getFullYear(),
month: $nowDate.getMonth() + 1,
day: $nowDate.getDate(),
hours: $nowDate.getHours(),
minutes: $nowDate.getMinutes(),
seconds: $nowDate.getSeconds(),
};
const yearsLen = props.yearsLen;
function returnYears() {
if (props.isArea) {
return new Array(yearsLen)
.fill(1)
.map((v, index) => {
return nowDate.year - index;
})
.reverse();
} else {
return new Array(yearsLen).fill(1).map((v, index) => {
return nowDate.year - Math.floor(yearsLen / 2) + index;
});
}
}
const years = returnYears();
const months = new Array(12).fill(1).map((v, index) => {
return index + 1;
});
const days = ref(
new Array(getMonthArea($nowDate, "end").getDate()).fill(1).map((v, index) => {
return index + 1;
})
);
const days1 = ref(
new Array(getMonthArea($nowDate, "end").getDate()).fill(1).map((v, index) => {
return index + 1;
})
);
const hours = new Array(24).fill(1).map((v, index) => {
return index;
});
const minutes = new Array(60).fill(1).map((v, index) => {
return index;
});
const seconds = new Array(60).fill(1).map((v, index) => {
return index;
});
// const fastTime = reactive([{
// title: '今日',
// key: 'now'
// },
// {
// title: '昨日',
// key: 'prve'
// },
// {
// title: '本月',
// key: 'nowMonth'
// },
// {
// title: '上月',
// key: 'prveMonth'
// }
// ])
const today = $time.getTodayTimestamps();
const yesterday = $time.getYesterdayTimestamps();
const thisMonth = $time.getThisMonthTimestamps();
const thisLastMonth = $time.getThisLastMonthTimestamps();
const fastTime = reactive([today, yesterday, thisMonth, thisLastMonth]);
function setPrveDay() {}
function setNowMoneth() {}
function setprveMoneth() {}
function setDay(start, end) {
value.value = [start.year, start.month, start.day, 0, 0, 0];
value1.value = [end.year, end.month, end.day, 23, 59, 59];
}
function changeTime(e) {
console.log(e);
const start = e.start;
const end = e.end;
emits("confirm", {
text: `${start}——${end}`,
start,
end,
});
close();
}
let value = ref([]);
let value1 = ref([]);
initValue();
function returnFindIndex(arr) {
const yearIndex = years.findIndex((v) => v == arr[0]);
const monthIndex = arr[1];
const dayIndex = arr[2] - 1;
const hIndex = arr[3];
const mIndex = arr[4];
const sIndex = arr[5];
return [yearIndex, monthIndex, dayIndex, hIndex, mIndex, sIndex];
}
function initValue() {
if (props.defaultTime.length && !props.isArea) {
value.value = returnFindIndex(props.defaultTime);
} else {
const yearIndex = years.findIndex((v) => v == nowDate.year);
value.value = [yearIndex, nowDate.month - 1, nowDate.day - 1, 0, 0, 0];
value1.value = [yearIndex, nowDate.month - 1, nowDate.day - 1, 23, 59, 59];
}
}
let show = ref(false);
const emits = defineEmits("close", "open", "confirm");
function toggle() {
show.value = !show.value;
if (show.value) {
open();
} else {
close();
}
}
function close() {
show.value = false;
// emits('close', false)
}
function open() {
if (typeof props.selTime === "number") {
const d = new Date(props.selTime);
value.value[0] = years.findIndex((v) => v === d.getFullYear());
value.value[1] = months.findIndex((v) => v === d.getMonth()) + 1;
value.value[2] = days.value.findIndex((v) => v === d.getDate());
}
show.value = true;
// emits('open', true)
}
function returnDateString(arr, isObj) {
const year = years[arr[0]];
const month = arr[1] + 1;
const day = arr[2] + 1;
const hour = ("0" + (arr[3] || 0)).slice(-2);
const min = ("0" + (arr[4] || 0)).slice(-2);
const sen = ("0" + (arr[5] || 0)).slice(-2);
if (isObj) {
return new Date(year, month, day, hour, min, sen);
}
return `${year}-${month}-${day} ${hour}:${min}:${sen}`;
}
function confirm(e) {
const start = returnDateString(value.value);
const end = returnDateString(value1.value);
if (!props.isArea) {
emits("confirm", start);
} else {
emits("confirm", {
text: `${start}——${end}`,
start,
end,
});
}
close();
}
function returnMonthStart(arr) {
return new Date(years[arr[0]], months[arr[1]] - 1, 1).getDate();
}
function returnMonthEnd(arr) {
return new Date(years[arr[0]], months[arr[1]], 0).getDate();
}
//防抖
function debounce(fn, wait) {
let timeout = null;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait);
if (callNow) fn.apply(context, args);
};
}
/**
* @param {Object} isDays1 //是否是结束时间选择
* @param {Object} arr
*/
function changeDays(isDays1, arr) {
const end = returnMonthEnd(arr);
if (end) {
if (isDays1) {
days1.value = new Array(end).fill(1).map((v, index) => {
return index + 1;
});
} else {
days.value = new Array(end).fill(1).map((v, index) => {
return index + 1;
});
}
}
}
function bindChange(e) {
const startTotal = returnDateString(e.detail.value, true).getTime();
const endTotal = returnDateString(value1.value, true).getTime();
value.value = e.detail.value;
setTimeout(() => {
if (props.isArea) {
value.value = startTotal > endTotal ? value1.value : e.detail.value;
}
debounce(changeDays(false, value.value), 100);
}, 10);
// nextTick(() => {
// if (props.isArea) {
// value.value = startTotal > endTotal ? value1.value : e.detail.value
// }
// console.log(value.value);
// debounce(changeDays(false, value.value), 100)
// })
}
function bindChange1(e) {
const startTotal = returnDateString(value.value, true).getTime();
const endTotal = returnDateString(e.detail.value, true).getTime();
value1.value = e.detail.value;
nextTick(() => {
if (props.isArea) {
value1.value = endTotal < startTotal ? value.value : e.detail.value;
}
debounce(changeDays(true, value1.value), 100);
});
}
function getDayDate(date = new Date(), type) {
const now = date;
if (type === "start") {
const startOfDay = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate()
);
return startOfDay;
}
if (type === "end") {
const endOfDay = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(),
23,
59,
59,
999
);
return endOfDay;
}
}
function getMonthArea(date = new Date(), type) {
let now = date;
let currentMonthStart = new Date(now.getFullYear(), now.getMonth(), 1);
let currentMonthEnd = new Date(
now.getFullYear(),
now.getMonth() + 1,
0,
23,
59,
59,
999
);
if (type === "start") {
return currentMonthStart;
}
if (type === "end") {
return currentMonthEnd;
}
return {
start: currentMonthStart,
end: currentMonthEnd,
};
}
function nullFunction() {}
function pickend(e) {
console.log(e);
}
function pickend1(e) {
console.log(e);
}
defineExpose({
close,
open,
confirm,
toggle,
});
</script>
<style lang="scss" scoped>
.fastTime {
.item {
background-color: rgb(247, 247, 247);
padding: 6rpx 40rpx;
border-radius: 6rpx;
font-size: 32rpx;
}
}
.top {
border-bottom: 1px solid #eee;
}
.close {
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 30rpx;
}
.mask {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
z-index: 9999;
background-color: rgba(51, 51, 51, 0.5);
.item {
line-height: 34px;
text-align: center;
}
.box {
position: absolute;
background-color: #fff;
bottom: 0;
left: 0;
right: 0;
border-radius: 16rpx 16rpx 0 0;
}
}
.fixed_b {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 30rpx;
z-index: 100;
background-color: #fff;
}
.picker-view {
width: 750rpx;
height: 300rpx;
}
</style>

View File

@@ -495,7 +495,7 @@
right: 0;
bottom: 0;
top: 0;
z-index: 9999;
z-index: 999;
background-color: rgba(51, 51, 51, .5);
.item {

View File

@@ -1,57 +0,0 @@
<template>
<view>
<up-checkbox-group
v-model="useType"
placement="row"
shape="square"
size="28rpx"
>
<up-checkbox
v-for="item in dinetyps"
:key="item.value"
:name="item.value"
:label="item.label"
:customStyle="radioCustomStyle"
></up-checkbox>
</up-checkbox-group>
</view>
</template>
<script setup>
import { ref } from "vue";
// 可使用类型dine堂食/pickup自取/deliv配送/express快递
const dinetyps = [
{
value: "dine-in",
label: "堂食",
},
{
value: "take-out",
label: "自取",
},
{
value: "post",
label: "配送",
},
{
value: "take-away",
label: "快递",
},
];
const props = defineProps({
radioCustomStyle: {
type: Object,
default: () => {
return { marginRight: "15px" };
},
},
});
const useType = defineModel({
default: () => [],
type: Array,
});
</script>

View File

@@ -1,96 +0,0 @@
<template>
<view class="fixed-wrap" :style="{ '--num': `${numValue}px` }">
<view class="fixed-btn" :class="[type]" id="targetRef">
<div class="btn">
<u-button type="primary" :color="confirmColor" :shape="shape" size="large" @click="emits('confirm')">{{ confirmText }}</u-button>
</div>
<div class="btn" v-if="showCancel">
<u-button :shape="shape" size="large" @click="emits('cancel')">取消</u-button>
</div>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, nextTick, getCurrentInstance, computed } from 'vue';
// import { isMainShop } from '@/store/account.js';
const props = defineProps({
type: {
type: String,
default: 'vertical' // horizontal横向 vertical竖向
},
isOpenPermission: {
type: Boolean,
default: true
},
confirmText: {
type: String,
default: '保存'
},
confirmColor: {
type: String,
default: '#3C9CFF'
},
showCancel: {
type: Boolean,
default: false
},
shape: {
type: String,
default: 'circle' // squre circle
}
});
// const isShow = computed(() => {
// if (props.isOpenPermission) {
// return isMainShop();
// }
// return true;
// });
const numValue = computed(() => {
let num = 0;
if (props.type == 'vertical' && props.showCancel) {
num = 63;
return num;
}
if (props.type == 'horizontal') {
num = 0;
return num;
}
return num;
});
const emits = defineEmits(['confirm', 'cancel']);
</script>
<style scoped lang="scss">
.fixed-wrap {
--height: calc(83px + var(--num) + env(safe-area-inset-bottom) / 2);
width: 100%;
height: var(--height);
.fixed-btn {
width: 100%;
height: var(--height);
position: fixed;
bottom: 0;
left: 0;
z-index: 999;
padding: 10px 14px calc(20px + env(safe-area-inset-bottom) / 2) 10px;
background-color: #fff;
&.horizontal {
display: flex;
gap: 28upx;
.btn {
flex: 1;
}
}
.btn {
&:first-child {
margin-bottom: 14px;
}
}
}
}
</style>

View File

@@ -1,52 +0,0 @@
<template>
<view class="conetnt">
<div class="row">
<u-radio-group v-model="getType">
<u-radio :label="item.label" :name="item.value" v-for="item in emunList.getType" :key="item.value" :customStyle="style"></u-radio>
</u-radio-group>
</div>
<template v-if="getType == 'yes'">
<view class="row mt">
<text class="title">用户领取方式</text>
</view>
<div class="row">
<u-checkbox-group v-model="getMode">
<u-checkbox :label="item.label" :name="item.value" v-for="item in emunList.getMode" :key="item.value" :customStyle="style"></u-checkbox>
</u-checkbox-group>
</div>
</template>
</view>
</template>
<script setup>
import { emunList } from '@/utils/couponUtils.js';
const style = {
marginRight: '20px'
};
const getType = defineModel('getType', {
type: String,
default: 'no'
});
const getMode = defineModel('getMode', {
type: Array,
default: ['eat']
});
</script>
<style scoped lang="scss">
.conetnt {
.row {
&.mt {
margin-top: 28upx;
padding-bottom: 10upx;
}
.title {
color: #303133;
font-size: 30upx;
}
}
}
</style>

View File

@@ -1,105 +0,0 @@
<template>
<view class="item-doc" :style="{ height: headHeight + 'px' }">
<view class="item" :style="{ height: headHeight + 'px' }">
<view class="left">
<image v-if="options.icon.indexOf('http') != -1" :src="options.icon" mode="aspectFit" class="icon"></image>
<image v-else :src="`/static/applocation/${options.icon}.png`" mode="aspectFit" class="icon"></image>
<view class="info">
<view class="title">
<text class="t">{{ options.name }}</text>
</view>
<view class="intro">
<text class="t">{{ options.intro }}</text>
</view>
</view>
</view>
<view class="right" v-if="showSwitch">
<!-- 关键修复删掉错误的 @change 绑定defineModel 已自动处理双向绑定 -->
<u-switch :active-value="1" :inactive-value="0" v-model="isOpen"></u-switch>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue';
const props = defineProps({
options: {
type: Object,
default: () => ({
// 注意:对象/数组默认值推荐用函数,避免引用共享
name: '标题',
intro: '说明',
icon: 'xszk'
})
},
showSwitch: {
type: Boolean,
default: false
}
});
const headHeight = ref(70);
// defineModel 是 Vue3.4+ 语法糖,自动实现 v-model:isOpen 的双向绑定
const isOpen = defineModel('isOpen', {
type: [Boolean, String, Number],
default: 0
});
// 声明自定义 emit 事件(仅在 script setup 内使用)
const emits = defineEmits(['load']);
onMounted(() => {
nextTick(() => {
emits('load', { height: headHeight.value });
});
});
</script>
<style scoped lang="scss">
.item-doc {
width: 100%;
}
.item {
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 99;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
padding: 20upx 28upx;
.left {
display: flex;
.icon {
$size: 80upx;
width: $size;
height: $size;
flex-shrink: 0;
}
.info {
display: flex;
padding-left: 20upx;
flex-direction: column;
.title {
margin-top: -4upx;
.t {
font-size: 28upx;
font-weight: bold;
}
}
.intro {
.t {
font-size: 28upx;
color: #999;
}
}
}
}
}
</style>

View File

@@ -1,110 +0,0 @@
<template>
<view>
<up-radio-group v-model="useTimeType" placement="row" v-if="showType">
<up-radio v-for="item in useTimeTypeList" :key="item.value" :value="item.value" :name="item.value" :label="item.label" :customStyle="customStyle"></up-radio>
</up-radio-group>
<view class="container" v-if="useTimeType == 'custom' || !showType">
<view class="u-flex u-m-t-30 box">
<view class="u-flex u-flex-1">
<view class="item" @click="pirckerShow(startValue, 'startValue')">
<text class="u-m-r-12" v-if="!startValue">开始时间</text>
<text class="u-m-r-12" v-else>{{ startValue }}</text>
</view>
<view class="u-m-l-8 u-m-r-8" style="padding: 0 30rpx"></view>
<view class="item" @click="pirckerShow(endValue, 'endValue')">
<text class="u-m-r-12" v-if="!endValue">结束时间</text>
<text class="u-m-r-12" v-else>{{ endValue }}</text>
</view>
</view>
<up-icon name="clock"></up-icon>
</view>
</view>
<up-datetime-picker :show="show" v-model="value1" closeOnClickOverlay @close="close" @cancel="close" @confirm="confirm" mode="time"></up-datetime-picker>
</view>
</template>
<script setup>
import { computed, ref } from 'vue';
const props = defineProps({
showType: {
type: Boolean,
default: true
}
});
function cancel() {
union.navigateBack();
}
const customStyle = ref({
marginRight: '15px'
});
const useTimeType = defineModel('useTimeType', {
type: String,
default: 'all'
});
const useTimeTypeList = [
{
value: 'all',
label: '全时段可用'
},
{
value: 'custom',
label: '指定时间段可用'
}
];
import dayjs from 'dayjs';
const startValue = defineModel('startValue', {
type: String,
default: ''
});
const endValue = defineModel('endValue', {
type: String,
default: ''
});
function close() {
show.value = false;
}
const value1 = ref('');
const show = ref(false);
const nowKey = ref('');
function pirckerShow(date, key) {
nowKey.value = key;
show.value = true;
value1.value = date || '';
}
function confirm(e) {
console.log(e);
if (nowKey.value == 'startValue') {
startValue.value = e.value;
} else if (nowKey.value == 'endValue') {
endValue.value = e.value;
}
value1.value = e.value;
show.value = false;
}
</script>
<style lang="scss" scoped>
.item {
font-size: 28rpx;
color: #666;
line-height: 48rpx;
padding: 0 12rpx;
display: flex;
}
.box {
border: 2rpx solid #dddfe6;
padding: 16rpx 30rpx;
box-sizing: border-box;
width: 564rpx;
border-radius: 4rpx;
overflow: hidden;
}
</style>

View File

@@ -1,41 +0,0 @@
<template>
<view class="mask-conetnt">
<text class="t">门店未参与{{ name }}活动或主店未开启活动如需开启参与请联系主店</text>
</view>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
name: {
type: String,
default: '活动'
}
});
</script>
<style scoped lang="scss">
.mask-conetnt {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 999;
background-color: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
padding-bottom: 5vh;
.t {
width: 80vw;
font-size: 28upx;
color: #333;
display: flex;
justify-content: center;
text-align: center;
}
}
</style>

View File

@@ -1,190 +0,0 @@
<template>
<view class="">
<view @click="show = true">
<slot v-if="$slots.default"></slot>
<view v-else class="choose-goods u-flex u-row-between">
<text class="color-999" v-if="!modelValue">请选择优惠券</text>
<text class="color-333 u-m-r-32 u-line-1" v-else>{{ goodsName }}</text>
<up-icon size="14" name="arrow-down"></up-icon>
</view>
</view>
<up-popup :show="show" :round="20" mode="bottom">
<view class="">
<view class="top u-flex u-row-between">
<text class="font-bold u-font-32 color-333">{{ title }}</text>
<up-icon size="18" name="close" @click="show = false"></up-icon>
</view>
<scroll-view :scroll-y="true" style="max-height: 50vh" @scroll="scroll" :scroll-top="scrollTop">
<view v-for="(item, index) in list" :key="index" class="item" @click="itemClick(item)" :class="[selGoods && selGoods.id == item.id ? 'selected' : '']">
<view class="u-flex u-row-between">
<view class="u-flex gap-20">
<!-- <view class="u-flex" @click.stop="preview(item)">
<up-image :src="item.coverImg" width="80rpx" height="80rpx"></up-image>
</view> -->
<text class="u-font-32 color-333">{{ item.title }}</text>
</view>
<!-- <text class="u-font-32 color-red u-p-l-30">¥{{ item.lowPrice }}</text> -->
</view>
</view>
</scroll-view>
<view class="bottom">
<view class="btn cancel" @click="close">{{ cancelText }}</view>
<view class="btn success" @click="confirm">{{ confirmText }}</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue';
import { couponPage } from '@/http/api/market/index.js';
const show = ref(false);
const modelValue = defineModel({
type: String,
default: ''
});
const goodsName = defineModel('goodsName', {
type: String,
default: ''
});
const props = defineProps({
title: {
type: String,
default: '选择优惠券'
},
confirmText: {
type: String,
default: '确认'
},
cancelText: {
type: String,
default: '取消'
}
});
const selGoods = ref('');
function itemClick(item) {
if (selGoods.value && selGoods.value.id == item.id) {
selGoods.value = '';
return;
}
selGoods.value = item;
}
const list = ref([]);
const scrollTop = ref(0);
function scroll(e) {
scrollTop.value = e.detail.scrollTop;
}
function preview(item) {
uni.previewImage({
urls: item.images || [item.coverImg]
});
}
watch(
() => modelValue.value,
(newVal, oldVal) => {
console.log(newVal, oldVal);
selGoods.value = list.value.find((item) => item.id == newVal);
console.log(selGoods.value);
if (selGoods.value) {
goodsName.value = selGoods.value.title;
}
}
);
watch(
() => list.value.length,
(newVal, oldVal) => {
selGoods.value = list.value.find((item) => item.id == modelValue.value);
console.log(selGoods.value);
if (selGoods.value) {
modelValue.value = selGoods.value.id;
goodsName.value = selGoods.value.title;
}
}
);
function close() {
show.value = false;
}
function confirm() {
if (!selGoods.value) {
uni.showToast({
title: '请选择优惠券',
icon: 'none'
});
return;
}
modelValue.value = selGoods.value.id;
show.value = false;
}
const emits = defineEmits(['load']);
onMounted(() => {
couponPage({ page: 1, size: 500 }).then((res) => {
list.value = res.records;
emits('load', list.value);
});
});
</script>
<style lang="scss">
.popup-content {
background: #fff;
width: 640rpx;
border-radius: 18rpx;
}
.top {
padding: 40rpx 48rpx;
border-bottom: 1px solid #d9d9d9;
}
.bottom {
padding: 48rpx 52rpx;
display: flex;
justify-content: space-between;
border-top: 1px solid #d9d9d9;
gap: 50rpx;
.btn {
flex: 1;
text-align: center;
padding: 18rpx 60rpx;
border-radius: 100rpx;
font-size: 32rpx;
border: 2rpx solid transparent;
&.success {
background-color: $my-main-color;
color: #fff;
}
&.cancel {
border-color: #d9d9d9;
box-shadow: 0 4rpx 0 0 #00000005;
}
}
}
.item {
padding: 10rpx 30rpx;
border: 1px solid #d9d9d9;
margin: 10rpx;
border-radius: 8rpx;
transition: all 0.3s ease-in-out;
box-shadow: 0 0 10px transparent;
&.selected {
border-color: $my-main-color;
box-shadow: 0 0 10px $my-main-color;
}
}
.choose-goods {
display: flex;
padding: 24rpx;
align-items: center;
border-radius: 8rpx;
border: 2rpx solid #d9d9d9;
background: #fff;
font-size: 28rpx;
font-weight: 400;
}
</style>

View File

@@ -1,325 +0,0 @@
<template>
<view class="my-select-goods">
<view class="radio-wrap">
<u-radio-group v-model="foodType" @change="foodTypeChange">
<u-radio v-for="item in radioList" :key="item.value" :label="item.label" :name="item.value" :customStyle="customStyle"></u-radio>
</u-radio-group>
</view>
<view class="selec-goods-card" @click="popupShow = true" v-if="foodType == 2">
<view class="title">
<text class="t">选择商品</text>
</view>
<view class="placeholder">
<view class="left">
<text class="placeholder-t" v-if="selectGoodsCount.length <= 0">请选择商品</text>
<text class="t" v-else>{{ selectGoodsCount.map((item) => item.name).join('、') }}</text>
</view>
<u-icon name="arrow-right" size="14px" color="#999"></u-icon>
</view>
</view>
<u-popup :show="popupShow" :round="20" closeable @close="popupClosed">
<view class="popup-container">
<view class="title">
<text class="t">请选择</text>
</view>
<view class="goods-scroll-wrap">
<view class="left">
<scroll-view scroll-y class="scroll-view">
<view
class="category-item"
v-for="(item, index) in categorys"
:key="item.id"
:class="{ active: categorysIndex == index }"
@click="changeCategorys(item, index)"
>
<text class="t">{{ item.name }}</text>
<text class="t" v-if="item.selectedNum > 0">({{ item.selectedNum }})</text>
</view>
</scroll-view>
</view>
<view class="right">
<scroll-view scroll-y class="scroll-view">
<view class="goods-item" v-for="(item, index) in categorys[categorysIndex].goods" :key="item.id" @click="selectGoods(item, index)">
<view class="name">
<text class="t">{{ item.name }}</text>
</view>
<view class="selec-btn">
<u-icon name="checkmark-circle-fill" color="#318afe" size="18" v-if="item.selected"></u-icon>
<view class="circle" v-else></view>
</view>
</view>
</scroll-view>
</view>
</view>
<view class="footer">
<view class="btn">
<u-button type="primary" size="large" @click="confirmHandle">
确定
<template v-if="countNum > 0">({{ countNum }})</template>
</u-button>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { getCategoryList, getProductList } from '@/http/api/product.js';
const popupShow = ref(false);
const modelValue = defineModel({
type: [String, Array],
default: []
});
const customStyle = ref({
marginRight: '20px'
});
const radioList = ref([
{
value: 1,
label: '全部商品参与'
},
{
value: 2,
label: '部分商品参与'
}
]);
const emits = defineEmits('foodTypeChange');
function foodTypeChange(e) {
console.log('foodTypeChange', e);
emits('foodTypeChange', e);
}
const foodType = defineModel('foodType', {
type: [Number, String],
default: 1
});
const categorys = ref([]);
const categorysIndex = ref(0);
// 切换分类
function changeCategorys(item, index) {
categorysIndex.value = index;
}
// 获取商品分类
async function getCategoryListAjax() {
try {
categorys.value = await getCategoryList();
categorys.value.forEach((item) => {
item.goods = [];
item.selectedNum = 0;
});
} catch (error) {
console.log(error);
}
}
const goods = ref([]);
function selectGoods(item, index) {
item.selected = !item.selected;
updateSelectGoods();
}
// 在这个方法里更新已选择的数量和商品
const countNum = ref(0);
function updateSelectGoods() {
countNum.value = 0;
categorys.value.forEach((item) => {
let num = 0;
item.goods.forEach((val) => {
if (val.selected) {
num++;
countNum.value++;
}
});
item.selectedNum = num;
});
confirmSelectGoods;
}
// 确定
function confirmHandle() {
confirmSelectGoods();
popupShow.value = false;
}
// 点击确定更新已选择的商品
const selectGoodsCount = ref([]);
function confirmSelectGoods() {
selectGoodsCount.value = [];
categorys.value.forEach((item) => {
item.goods.forEach((val) => {
if (val.selected) {
selectGoodsCount.value.push(val);
}
});
});
modelValue.value = selectGoodsCount.value.map((item) => item.id);
}
// 获取商品列表
async function getProductListAjax() {
try {
const res = await getProductList();
res.forEach((item, index) => {
// console.log('modelValue.value===', modelValue.value);
// console.log('index===', item.id.includes(modelValue.value));
if (modelValue.value.includes(item.id)) {
item.selected = true;
} else {
item.selected = false;
}
categorys.value.forEach((val, i) => {
if (val.id == item.categoryId) {
val.goods.push(item);
}
});
});
updateSelectGoods();
confirmSelectGoods();
} catch (error) {
console.log(error);
}
}
// popup关闭
function popupClosed() {
popupShow.value = false;
// countNum.value = 0;
}
onMounted(async () => {
await getCategoryListAjax();
await getProductListAjax();
});
</script>
<style scoped lang="scss">
.my-select-goods {
.selec-goods-card {
margin-top: 20upx;
background-color: #f8f8f8;
border-radius: 10upx;
padding: 20upx;
.ttile {
.t {
font-size: 28upx;
font-weight: bold;
color: #333;
}
}
.placeholder {
display: flex;
padding-top: 12upx;
.left {
flex: 1;
.placeholder-t {
font-size: 28upx;
color: #999;
}
.t {
display: block;
max-width: 400upx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
.popup-container {
$color: #318afe;
.title {
padding: 28upx;
display: flex;
justify-content: center;
.t {
font-size: 32upx;
font-weight: bold;
}
}
.goods-scroll-wrap {
width: 100%;
height: 50vh;
display: flex;
.left {
width: 240upx;
height: 100%;
background-color: #f9f9f9;
}
.right {
flex: 1;
height: 100%;
}
.scroll-view {
width: 100%;
height: 100%;
.category-item {
width: 100%;
height: 84upx;
display: flex;
align-items: center;
padding-left: 38upx;
&.active {
background-color: #fff;
position: relative;
&::after {
content: '';
width: 8upx;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 10;
background-color: $color;
}
}
.t {
font-size: 32upx;
}
}
.goods-item {
height: 84upx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 28upx;
.name {
.t {
font-size: 32upx;
}
}
.selec-btn {
$size: 32upx;
width: $size;
height: $size;
display: flex;
align-items: center;
justify-content: center;
.circle {
width: $size;
height: $size;
border-radius: 50%;
border: 1px solid #999;
}
}
}
}
}
.footer {
padding: 28upx;
}
}
</style>

View File

@@ -1,134 +0,0 @@
<template>
<view>
<up-radio-group v-model="useType" placement="row">
<up-radio v-for="item in useTypeList" :key="item.value" :name="item.value" :label="item.label" :customStyle="customStyle"></up-radio>
</up-radio-group>
<view class="box" v-if="useType == 'custom'" @click.stop="openPopup">
<text class="u-font-28 color-999 u-p-r-16" v-if="selShops.length == 0">请选择</text>
<view class="u-font-24 shop-item u-flex u-p-r-20 u-col-center u-p-r-16" v-for="(item, index) in selShops" :key="item.shopId">
<text>{{ returnShopName(item) }}</text>
<view @click.stop="delShop(index)">
<up-icon name="close" size="14" @click="delShop(index)" color="#999"></up-icon>
</view>
</view>
<view class="icon">
<up-icon name="arrow-down" size="14" color="#999"></up-icon>
</view>
</view>
<up-popup :show="show" placement="bottom" round="18rpx" closeOnClickOverlay @close="close">
<view class="u-p-30">
<view class="font-bold color-333 u-font-32">选择门店</view>
<scroll-view class="scroll-view u-m-t-30" scroll-y="true" max-height="40vh">
<view class="u-m-b-10" v-for="item in list" :key="item.shopId">
<up-checkbox usedAlone :name="item.shopId" :label="item.shopName" v-model:checked="item.checked"></up-checkbox>
</view>
</scroll-view>
<view class="u-flex gap-20 u-m-t-30">
<view class="u-flex-1">
<my-button type="default" @click="close">取消</my-button>
</view>
<view class="u-flex-1">
<my-button type="primary" @click="submit">确定</my-button>
</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import { onMounted, reactive, ref, watch } from 'vue';
import { getShopList } from '@/http/api/shop.js';
const customStyle = ref({
marginRight: '15px'
});
const show = ref(false);
const selShops = defineModel('selShops', {
default: () => [],
type: [Array, String]
});
const useType = defineModel('useType', {
default: () => 'only',
type: String
});
const useTypeList = [
{
value: 'only',
label: '仅本店'
},
{
value: 'all',
label: '全部门店'
},
{
value: 'custom',
label: '指定门店可用'
}
];
function returnShopName(shopId) {
const item = list.value.find((v) => v.shopId == shopId);
return item?.shopName || '';
}
function close() {
show.value = false;
}
function submit() {
selShops.value = list.value.filter((item) => item.checked).map((v) => v.shopId);
show.value = false;
}
function delShop(index) {
selShops.value.splice(index, 1);
}
const list = ref([]);
function openPopup() {
show.value = true;
list.value = list.value.map((item) => ({
shopId: item.shopId,
shopName: item.shopName,
checked: selShops.value.includes(item.shopId)
}));
}
async function init() {
const res = await getShopList();
console.log('selShops.value', selShops.value);
if (res) {
list.value = res.map((item) => ({
shopId: item.shopId,
shopName: item.shopName,
checked: selShops.value.includes(item.shopId)
}));
}
}
onMounted(init);
</script>
<style lang="scss">
.box {
border-radius: 8upx;
margin-top: 16rpx;
display: flex;
flex-direction: row;
align-items: top;
flex-wrap: wrap;
padding: 10rpx 24rpx;
border: 2rpx solid #e5e5e5;
position: relative;
.icon {
position: absolute;
top: 50%;
right: 24rpx;
transform: translateY(-50%);
}
}
.shop-item {
padding: 4rpx 8rpx 4rpx 16rpx;
border-radius: 4rpx;
border: 2rpx solid #f0f0f0;
background-color: #f5f5f5;
margin-bottom: 16rpx;
margin-left: 16rpx;
}
</style>

View File

@@ -1,135 +0,0 @@
<template>
<view>
<up-radio-group v-model="useType" placement="row" @change="useTypeChange">
<up-radio v-for="item in useTypeList" :key="item.value" :name="item.value" :label="item.label" :customStyle="customStyle"></up-radio>
</up-radio-group>
<view class="box" v-if="useType == 'part'" @click.stop="openPopup">
<text class="u-font-28 color-999 u-p-r-16" v-if="selShops.length == 0">请选择</text>
<view class="u-font-24 shop-item u-flex u-p-r-20 u-col-center u-p-r-16" v-for="(item, index) in selShops" :key="item.shopId">
<text>{{ returnShopName(item) }}</text>
<view @click.stop="delShop(index)">
<up-icon name="close" size="14" @click="delShop(index)" color="#999"></up-icon>
</view>
</view>
<view class="icon">
<up-icon name="arrow-down" size="14" color="#999"></up-icon>
</view>
</view>
<up-popup :show="show" placement="bottom" round="18rpx" closeOnClickOverlay @close="close">
<view class="u-p-30">
<view class="font-bold color-333 u-font-32">选择门店</view>
<scroll-view class="scroll-view u-m-t-30" scroll-y="true" max-height="40vh">
<view class="u-m-b-10" v-for="item in list" :key="item.shopId">
<up-checkbox usedAlone :name="item.shopId" :label="item.shopName" v-model:checked="item.checked"></up-checkbox>
</view>
</scroll-view>
<view class="u-flex gap-20 u-m-t-30">
<view class="u-flex-1">
<my-button type="default" @click="close">取消</my-button>
</view>
<view class="u-flex-1">
<my-button type="primary" @click="submit">确定</my-button>
</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import { onMounted, reactive, ref, watch } from 'vue';
import { getShopList } from '@/http/api/shop.js';
const customStyle = ref({
marginRight: '20px'
});
const show = ref(false);
let selShops = defineModel('selShops', {
default: () => [],
type: Array
});
const useType = defineModel('useType', {
default: () => 'all',
type: String
});
const useTypeList = [
{
value: 'all',
label: '全部门店'
},
{
value: 'part',
label: '指定门店可用'
}
];
const emits = defineEmits(['useTypeChange']);
function useTypeChange(e) {
emits('useTypeChange', e);
}
function returnShopName(shopId) {
const item = list.value.find((v) => v.shopId == shopId);
return item?.shopName || '';
}
function close() {
show.value = false;
}
function submit() {
selShops.value = list.value.filter((item) => item.checked).map((v) => v.shopId);
show.value = false;
}
function delShop(index) {
selShops.value.splice(index, 1);
}
const list = ref([]);
function openPopup() {
show.value = true;
list.value = list.value.map((item) => ({
shopId: item.shopId,
shopName: item.shopName,
checked: selShops.value.includes(item.shopId)
}));
}
async function init() {
const res = await getShopList();
console.log('selShops.value', selShops.value);
if (res) {
list.value = res.map((item) => ({
shopId: item.shopId,
shopName: item.shopName,
checked: selShops.value.includes(item.shopId)
}));
}
}
onMounted(init);
</script>
<style lang="scss">
.box {
border-radius: 8upx;
margin-top: 16rpx;
display: flex;
flex-direction: row;
align-items: top;
flex-wrap: wrap;
padding: 10rpx 24rpx;
border: 2rpx solid #e5e5e5;
position: relative;
.icon {
position: absolute;
top: 50%;
right: 24rpx;
transform: translateY(-50%);
}
}
.shop-item {
padding: 4rpx 8rpx 4rpx 16rpx;
border-radius: 4rpx;
border: 2rpx solid #f0f0f0;
background-color: #f5f5f5;
margin-bottom: 16rpx;
margin-left: 16rpx;
}
</style>

View File

@@ -1,22 +0,0 @@
<template>
<u-radio-group v-model="modelValue" @change="defineEmits(['update:modelValue'])">
<u-radio label="仅本店" name="only" :customStyle="customStyle"></u-radio>
<u-radio label="全部" name="all" :customStyle="customStyle"></u-radio>
<u-radio label="指定" name="custom" :customStyle="customStyle"></u-radio>
</u-radio-group>
</template>
<script setup>
import { ref } from 'vue';
const customStyle = ref({
marginRight: '20px'
});
const modelValue = defineModel('modelValue', {
type: String,
default: 'only'
});
</script>
<style></style>

View File

@@ -1,89 +0,0 @@
<template>
<view>
<view class="u-flex">
<view class="item" @click="pirckerShow(startDate, 'startDate')">
<text class="u-m-r-12" v-if="!startDate">{{ startText }}</text>
<text class="u-m-r-12" v-else>{{ startDate }}</text>
<up-icon name="arrow-down" size="14"></up-icon>
</view>
<view class="u-m-l-8 u-m-r-8"></view>
<view class="item" @click="pirckerShow(endDate, 'endDate')">
<text class="u-m-r-12" v-if="!endDate">{{ endText }}</text>
<text class="u-m-r-12" v-else>{{ endDate }}</text>
<up-icon name="arrow-down" size="14"></up-icon>
</view>
</view>
<up-datetime-picker
:show="show"
v-model="value1"
closeOnClickOverlay
@close="close"
@cancel="close"
@confirm="confirm"
mode="date"
></up-datetime-picker>
</view>
</template>
<script setup>
import { computed, ref } from "vue";
import dayjs from "dayjs";
const props=defineProps({
startText: {
type: String,
default: "请选择开始时间",
},
endText: {
type: String,
default: "请选择结束时间",
},
});
const startDate = defineModel("startDate", {
type: String,
default: "",
});
const endDate = defineModel("endDate", {
type: String,
default: "",
});
const minDate = ref(0);
const maxDate = ref(dayjs().add(80, "year").valueOf());
function close() {
show.value = false;
}
const value1 = ref(Date.now());
const show = ref(false);
const nowKey = ref("");
function pirckerShow(date, key) {
nowKey.value = key;
console.log(date);
show.value = true;
}
function confirm(e) {
console.log(e);
if (nowKey.value == "startDate") {
startDate.value = dayjs(e.value).format("YYYY-MM-DD");
} else if (nowKey.value == "endDate") {
endDate.value = dayjs(e.value).format("YYYY-MM-DD");
}
value1.value = e.value
show.value = false;
}
</script>
<style lang="scss" scoped>
.item {
font-size: 28rpx;
color: #666;
line-height: 64rpx;
border: 2rpx solid #dddfe6;
padding: 0 12rpx;
display: flex;
}
</style>

View File

@@ -1,125 +1,113 @@
<template>
<up-upload
:fileList="images"
@afterRead="afterRead"
@delete="deletePic"
:multiple="multiple"
:width="width"
:height="height"
:maxCount="maxCount"
>
<template #default v-if="$slots.default">
<slot></slot>
</template>
</up-upload>
<up-upload :fileList="images" @afterRead="afterRead" @delete="deletePic" :multiple="multiple" :width="width"
:height="height" :maxCount="maxCount"></up-upload>
</template>
<script setup>
import { ref, watch } from "vue";
import { uploadFile } from "@/http/api/index.js";
import { ref, watch } from 'vue';
import { uploadFile } from '@/http/api/index.js'
const props = defineProps({
modelValue: {
type: Array,
default: () => [],
},
width: {
type: [String, Number],
default: 60,
},
height: {
type: [String, Number],
default: 60,
},
maxCount: {
type: [Number],
default: 10,
},
multiple: {
type: Boolean,
default: true,
},
});
const props = defineProps({
modelValue: {
type: Array,
default: () => []
},
width: {
type: [String, Number],
default: 60
},
height: {
type: [String, Number],
default: 60
},
maxCount: {
type: [Number],
default: 10
},
multiple: {
type: Boolean,
default: true
}
})
const emits = defineEmits(["update:modelValue"]);
const images = ref(props.modelValue);
function uploadfile(par) {
let file = null;
// #ifdef H5
file = par.file;
// #endif
// #ifndef H5
file = par;
// #endif
return uploadFile(file);
}
const emits = defineEmits(['update:modelValue'])
const images = ref(props.modelValue)
function uploadfile(par){
let file=null;
// #ifdef H5
file= par.file
// #endif
// #ifndef H5
file= par
// #endif
return uploadFile(file)
}
function afterRead(e) {
console.log(e);
if (Array.isArray(e.file)) {
for (let i in e.file) {
const file = e.file[i]
console.log(file);
uploadfile(file).then(res => {
console.log(res);
images.value.push({
url: e.file[i].url,
serveUrl: res
})
}).catch(res => {
console.log(res);
if (res.errMsg) {
images.value.splice(i, 1)
uni.showToast({
title: '图片大小超出限制',
icon: 'error'
})
}
function afterRead(e) {
console.log(e);
if (Array.isArray(e.file)) {
for (let i in e.file) {
const file = e.file[i];
console.log(file);
uploadfile(file)
.then((res) => {
console.log(res);
images.value.push({
url: e.file[i].url,
serveUrl: res,
});
})
.catch((res) => {
console.log(res);
if (res.errMsg) {
images.value.splice(i, 1);
uni.showToast({
title: "图片大小超出限制",
icon: "error",
});
}
});
}
} else {
const i = 0;
uploadfile(e.file)
.then((res) => {
console.log(res);
images.value.push({
url: e.file.url,
serveUrl: res,
});
})
.catch((res) => {
console.log(res);
if (res.errMsg) {
images.value.splice(i, 1);
uni.showToast({
title: "图片大小超出限制",
icon: "error",
});
}
});
}
}
})
}
}else{
const i=0
uploadfile(e.file).then(res => {
console.log(res);
images.value.push({
url: e.file.url,
serveUrl: res
})
}).catch(res => {
console.log(res);
if (res.errMsg) {
images.value.splice(i, 1)
uni.showToast({
title: '图片大小超出限制',
icon: 'error'
})
}
})
}
function deletePic(e) {
const { index } = e;
images.value.splice(index, 1);
}
}
watch(
() => images.value,
(newval) => {
emits("update:modelValue", newval);
}
);
watch(
() => props.modelValue,
(newval) => {
images.value = newval;
}
);
function deletePic(e) {
const {
index
} = e
images.value.splice(index, 1)
}
watch(() => images.value, (newval) => {
emits('update:modelValue', newval)
})
watch(() => props.modelValue, (newval) => {
images.value = newval
})
</script>
<style></style>
<style>
</style>

View File

@@ -1,89 +0,0 @@
<template>
<view class="upload-file" @click="chooseImage">
<slot v-if="$slots.default"></slot>
<view class="icon" v-if="!modelValue">+</view>
<image class="img" v-else :src="modelValue"></image>
<view class="close" @click.stop="() => {}" v-if="modelValue">
<up-icon name="close-circle" color="#333" size="14" @click="clearImg"></up-icon>
</view>
</view>
</template>
<script setup>
import { uploadFile } from '@/http/api/index.js';
import { reactive, ref, watch } from 'vue';
const modelValue = defineModel({
type: String,
default: ''
});
function chooseImage() {
uni.chooseImage({
count: 1, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'],
success: async function (res) {
uni.showLoading({
title: '上传中'
});
console.log(res);
const fileRes = await uploadFile(res.tempFiles[0]);
uni.hideLoading();
if (fileRes) {
modelValue.value = fileRes;
} else {
uni.showToast({
title: '上传失败',
icon: 'none'
});
}
}
});
}
function clearImg() {
modelValue.value = '';
}
</script>
<style lang="scss">
.upload-file {
$size: 128rpx;
width: $size;
height: $size;
display: flex;
justify-content: center;
align-items: center;
border: 1px dashed #d9d9d9;
border-radius: 8rpx;
position: relative;
.close {
position: absolute;
right: -10rpx;
top: -10rpx;
}
.img {
width: $size;
height: $size;
}
.icon {
width: 36rpx;
height: 36rpx;
border: 4rpx solid #999;
border-radius: 8rpx;
font-weight: 700;
color: #999;
font-size: 32rpx;
display: flex;
justify-content: center;
align-items: center;
line-height: 1;
padding: 0;
margin: 0;
text-align: center;
}
}
</style>

View File

@@ -1,181 +0,0 @@
<template>
<view class="upload-file-wrap">
<!-- 多图展示区域 -->
<view class="upload-file-item" v-for="(img, index) in modelValue" :key="index">
<image class="img" :src="img" mode="aspectFill" @click="previewImage(index)"></image>
<view class="close" @click.stop="removeImg(index)">
<up-icon name="close-circle" color="#333" size="14"></up-icon>
</view>
</view>
<!-- 上传按钮未达上限时显示 -->
<view class="upload-file-btn" @click="chooseImage" v-if="modelValue.length < props.maxCount">
<slot v-if="$slots.default"></slot>
<view class="icon" v-else>+</view>
</view>
</view>
</template>
<script setup>
import { uploadFile } from '@/http/api/index.js';
import { ref, watch, defineProps, defineEmits } from 'vue';
// 1. 定义Props扩展配置项
const props = defineProps({
// 最大上传数量默认9可外部传参
maxCount: {
type: Number,
default: 9
},
// 是否压缩(默认开启)
isCompressed: {
type: Boolean,
default: true
},
// 图片来源(相册/相机,默认都支持)
sourceType: {
type: Array,
default: () => ['album', 'camera']
}
});
// 2. 双向绑定多图列表Array类型
const modelValue = defineModel({
type: Array,
default: () => []
});
// 3. 选择图片(支持多图)
async function chooseImage() {
// 计算剩余可上传数量
const remainCount = props.maxCount - modelValue.value.length;
if (remainCount <= 0) {
uni.showToast({ title: `最多只能上传${props.maxCount}张图片`, icon: 'none' });
return;
}
uni.chooseImage({
count: remainCount, // 仅能选择剩余数量的图片
sizeType: props.isCompressed ? ['compressed'] : ['original', 'compressed'],
sourceType: props.sourceType,
success: async function (res) {
uni.showLoading({ title: '上传中' });
try {
// 批量上传选中的图片
const uploadPromises = res.tempFiles.map((file) => uploadFile(file));
const fileResList = await Promise.all(uploadPromises);
// 过滤上传失败的图片仅保留成功的URL
const successUrls = fileResList.filter((url) => !!url);
if (successUrls.length > 0) {
modelValue.value = [...modelValue.value, ...successUrls]; // 追加到列表
uni.showToast({ title: `成功上传${successUrls.length}张图片`, icon: 'success' });
}
} catch (error) {
uni.showToast({ title: '部分图片上传失败', icon: 'none' });
console.error('图片上传失败:', error);
} finally {
uni.hideLoading();
}
},
fail: () => {
uni.showToast({ title: '取消选择图片', icon: 'none' });
}
});
}
// 4. 删除单张图片
function removeImg(index) {
uni.showModal({
title: '提示',
content: '确定要删除这张图片吗?',
success: (res) => {
if (res.confirm) {
modelValue.value.splice(index, 1); // 删除对应索引的图片
}
}
});
}
// 5. 预览图片(支持左右滑动)
function previewImage(currentIndex) {
uni.previewImage({
urls: modelValue.value, // 所有已上传的图片列表
current: modelValue.value[currentIndex], // 当前预览的图片
loop: true // 支持循环预览
});
}
// 6. 扩展:批量清空图片(可暴露给父组件调用)
const clearAllImg = () => {
uni.showModal({
title: '提示',
content: '确定要清空所有图片吗?',
success: (res) => {
if (res.confirm) {
modelValue.value = [];
}
}
});
};
// 暴露方法给父组件
defineExpose({ clearAllImg });
</script>
<style lang="scss" scoped>
.upload-file-wrap {
display: flex;
flex-wrap: wrap;
gap: 20rpx; // 图片之间的间距
}
.upload-file-item {
$size: 128rpx;
width: $size;
height: $size;
border: 1px dashed #d9d9d9;
border-radius: 8rpx;
position: relative;
overflow: hidden;
.img {
width: 100%;
height: 100%;
}
.close {
position: absolute;
right: -10rpx;
top: -10rpx;
background: #fff;
border-radius: 50%;
padding: 2rpx;
z-index: 10;
}
}
.upload-file-btn {
$size: 128rpx;
width: $size;
height: $size;
display: flex;
justify-content: center;
align-items: center;
border: 1px dashed #d9d9d9;
border-radius: 8rpx;
.icon {
width: 36rpx;
height: 36rpx;
border: 4rpx solid #999;
border-radius: 8rpx;
font-weight: 700;
color: #999;
font-size: 32rpx;
display: flex;
justify-content: center;
align-items: center;
line-height: 1;
}
}
</style>

View File

@@ -1,57 +0,0 @@
<template>
<view>
<up-checkbox-group v-model="selectedWeek" :options="week">
<up-checkbox :customStyle="customStyle" :shape="shape" v-for="item in week" :key="item.value" :value="item.value" :name="item.value" :label="item.value">
{{ item.name }}
</up-checkbox>
</up-checkbox-group>
</view>
</template>
<script setup>
import { ref } from 'vue';
const customStyle = {
marginRight: '40rpx',
marginBottom: '16rpx'
};
const props = defineProps({
shape: {
type: String,
default: 'square' // circle
}
});
const selectedWeek = defineModel({
type: Array,
default: () => []
});
const week = ref([
{
name: '周一',
value: '周一'
},
{
name: '周二',
value: '周二'
},
{
name: '周三',
value: '周三'
},
{
name: '周四',
value: '周四'
},
{
name: '周五',
value: '周五'
},
{
name: '周六',
value: '周六'
},
{
name: '周日',
value: '周日'
}
]);
</script>

View File

@@ -1,33 +0,0 @@
import dayjs from "dayjs";
export const timeList = [
{
label: "今天",
value: "today",
beginDate: dayjs().format("YYYY-MM-DD"),
endDate: dayjs().format("YYYY-MM-DD"),
},
{
label: "昨天",
value: "yesterday",
beginDate: dayjs().subtract(1, "day").format("YYYY-MM-DD"),
endDate: dayjs().subtract(1, "day").format("YYYY-MM-DD"),
},
{
label: "本周",
value: "this_week",
beginDate: dayjs().startOf("week").format("YYYY-MM-DD"),
endDate: dayjs().endOf("week").format("YYYY-MM-DD"),
},
{
label: "本月",
value: "this_month",
beginDate: dayjs().startOf("month").format("YYYY-MM-DD"),
endDate: dayjs().endOf("month").format("YYYY-MM-DD"),
},
{
label: "自定义",
value: "custom",
beginDate: "",
endDate: "",
},
];

View File

@@ -1,7 +1,7 @@
export default {
'JEEPAY_BASE_URL': 'http://192.168.1.42/', // 请求URL生产环境
'JEEPAY_BASE_URL_H5': 'http://192.168.1.42/',
'JEEPAY_BASE_URL_WSS': 'ws://192.168.1.42:2348' ,// sockets
'JEEPAY_BASE_URL': 'http://192.168.1.31/', // 请求URL生产环境
'JEEPAY_BASE_URL_H5': 'http://192.168.1.31/',
'JEEPAY_BASE_URL_WSS': 'ws://192.168.1.31:2348' ,// sockets
// 'JEEPAY_BASE_URL': 'https://cashier.sxczgkj.com/', // 请求URL生产环境
// 'JEEPAY_BASE_URL_H5': 'https://cashier.sxczgkj.com/',
// 'JEEPAY_BASE_URL_WSS': 'wss://czgeatws.sxczgkj.com/wss' // sockets

View File

@@ -1,13 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "account";
export function tableOrderStatistic(data) {
return request({
url: `${urlType}/admin/tableOrderStatistic`,
method: "GET",
data: {
...data,
},
});
}

View File

@@ -1,55 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
export function chatCouponCreate(data) {
return request({
url: urlType + `/admin/chat/coupon/create`,
method: "post",
data: {
...data,
},
});
}
export function chatCouponPage(params) {
return request({
url: urlType + `/admin/chat/coupon/page`,
method: "get",
params: {
...params,
},
});
}
export function chatCouponExpired(id) {
return request({
url: urlType + `/admin/chat/coupon/expired/${id}`,
method: "delete",
});
}
export function chatCouponGrant(data) {
return request({
url: urlType + `/admin/chat/coupon/grant`,
method: "post",
data,
});
}
export function chatCouponRecord(params) {
return request({
url: urlType + `/admin/chat/coupon/record`,
method: "get",
params,
});
}
export function deleteRecord(params) {
return request({
url: urlType + `/admin/coupon/deleteRecord?id=`+params.id,
method: "delete",
params,
});
}

View File

@@ -1,14 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
export function chatCoupon(params) {
return request({
url: urlType + `/admin/coupon/chatCoupon`,
method: "get",
params: {
...params,
},
});
}

View File

@@ -1,32 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
export function getList(data) {
return request({
url: `${urlType}/admin/consumeCashback/record`,
method: "GET",
data: {
...data,
},
});
}
export function getConfig(data) {
return request({
url: `${urlType}/admin/consumeCashback`,
method: "GET",
data: {
...data,
},
});
}
export function update(data) {
return request({
url: `${urlType}/admin/consumeCashback`,
method: "POST",
data: {
...data,
},
});
}

View File

@@ -1,38 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const marketUrl = "market";
/**
* 新客立减
* @returns
*/
export function getDiscountByUserId(params) {
return request({
url: marketUrl + `/admin/consumeDiscount/getDiscountByUserId`,
method: "get",
params: {
...params,
},
});
}
export function getConfig(params) {
return request({
url: marketUrl + `/admin/consumeDiscount`,
method: "get",
params: {
...params,
},
});
}
export function editConfig(params) {
return request({
url: marketUrl + `/admin/consumeDiscount`,
method: "POST",
params: {
...params,
},
});
}

View File

@@ -1,77 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
export function enable(params) {
return request({
url: urlType + `/admin/couponRedemption/enable`,
method: "PUT",
params: {
...params,
},
});
}
export function status(params) {
return request({
url: urlType + `/admin/couponRedemption/enable/status`,
method: "get",
params: {
...params,
},
});
}
export function couponRedemption(params) {
return request({
url: urlType + `/admin/couponRedemption`,
method: "get",
params: {
...params,
},
});
}
export function couponRedemptionList(params) {
return request({
url: urlType + `/admin/couponRedemption/list`,
method: "get",
params: {
...params,
},
});
}
export function add(params) {
return request({
url: urlType + `/admin/couponRedemption`,
method: "POST",
params: {
...params,
},
});
}
export function edit(params) {
return request({
url: urlType + `/admin/couponRedemption`,
method: "PUT",
params: {
...params,
},
});
}
export function codeList(params) {
return request({
url: urlType + `/admin/couponRedemption/code/list`,
method: "GET",
params: {
...params,
},
});
}
export function codeExport(params) {
return http.download({
url: urlType + `/admin/couponRedemption/code/export`,
method: "GET",
params: {
...params,
},
});
}

View File

@@ -1,55 +0,0 @@
import http from '@/http/http.js'
const request = http.request
const urlType='market'
export function getList(data) {
return request({
url: `${urlType}/admin/discountActivity/page`,
method: "GET",
data: {
...data
}
})
}
export function add(data) {
return request({
url: `${urlType}/admin/discountActivity`,
method: "POST",
data: {
...data
}
})
}
export function update(data) {
return request({
url: `${urlType}/admin/discountActivity`,
method: "PUT",
data: {
...data
}
})
}
export function del(id) {
return request({
url: `${urlType}/admin/discountActivity?id=`+id,
method: "DELETE",
})
}
/**
* 满减活动
* @returns
*/
export function discountActivity(params) {
return request({
url: urlType+`/admin/discountActivity`,
method: 'get',
params: {
...params
}
})
}

View File

@@ -1,139 +0,0 @@
import http from '@/http/http.js'
const request = http.request
const urlType='market'
export function getConfig(data) {
return request({
url: `${urlType}/admin/distribution`,
method: "GET",
data: {
...data
}
})
}
export function editConfig(data) {
return request({
url: `${urlType}/admin/distribution`,
method: "PUT",
data: {
...data
}
})
}
export function moneyRecoders(data) {
return request({
url: `${urlType}/admin/distribution/flow`,
method: "GET",
data: {
...data
}
})
}
export function cashPay(data) {
return request({
url: `${urlType}/admin/distribution/cashPay`,
method: "POST",
data: {
...data
}
})
}
export function openFlow(data) {
return request({
url: `${urlType}/admin/distribution/openFlow`,
method: "GET",
data: {
...data
}
})
}
export function distributionFlow(data) {
return request({
url: `${urlType}/admin/distribution/distributionFlow`,
method: "GET",
data: {
...data
}
})
}
export function rechargeQrCode(data) {
return request({
url: `${urlType}/admin/distribution/rechargeQrCode`,
method: "GET",
data: {
...data
}
})
}
export function withdrawFlow(data) {
return request({
url: `${urlType}/admin/distribution/withdrawFlow`,
method: "GET",
data: {
...data
}
})
}
export function distributionUser(data) {
return request({
url: `${urlType}/admin/distribution/user`,
method: "GET",
data: {
...data
}
})
}
export function addDistributionUser(data) {
return request({
url: `${urlType}/admin/distribution/user`,
method: "POST",
data: {
...data
}
})
}
export function editDistributionUser(data) {
return request({
url: `${urlType}/admin/distribution/user`,
method: "PUT",
data: {
...data
}
})
}
export function deleteDistributionUser(data) {
return request({
url: `${urlType}/admin/distribution/user`,
method: "DELETE",
data: {
...data
}
})
}
export function inviteUser(data) {
return request({
url: `${urlType}/admin/distribution/user/inviteUser`,
method: "GET",
data: {
...data
}
})
}
export function resetLevel(data) {
return request({
url: `${urlType}/admin/distribution/user/resetLevel`,
method: "POST",
data: {
...data
}
})
}

View File

@@ -1,35 +0,0 @@
import http from '@/http/http.js'
const request = http.request
const urlType='market'
export function distributionFlow(data) {
return request({
url: `${urlType}/admin/distribution/distributionFlow`,
method: "GET",
data: {
...data
}
})
}
export function openFlow(data) {
return request({
url: `${urlType}/admin/distribution/openFlow`,
method: "GET",
data: {
...data
}
})
}
export function flow(data) {
return request({
url: `${urlType}/admin/distribution/flow`,
method: "GET",
data: {
...data
}
})
}

View File

@@ -1,24 +0,0 @@
import http from '@/http/http.js'
const request = http.request
const urlType='market'
export function getConfig(data) {
return request({
url: `${urlType}/admin/drainageConfig`,
method: "GET",
data: {
...data
}
})
}
export function update(data) {
return request({
url: `${urlType}/admin/drainageConfig`,
method: "POST",
data: {
...data
}
})
}

View File

@@ -1,191 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
/**
* 限时折扣-分页
* @param {Object} data
*/
export function limitTimeDiscountPage(data) {
return request({
url: `${urlType}/admin/limitTimeDiscount/page`,
method: "GET",
data
});
}
/**
* 删除限时折扣
* @param {Object} data
*/
export function limitTimeDiscountDel(id) {
return request({
url: `${urlType}/admin/limitTimeDiscount?id=${id}`,
method: "DELETE"
});
}
/**
* 限时折扣-新增/编辑
* @param {Object} data
*/
export function limitTimeDiscount(data) {
return request({
url: `${urlType}/admin/limitTimeDiscount`,
method: data.id ? 'put' : 'post',
data
});
}
/**
* 优惠券-分页
* @param {Object} params
*/
export function couponPage(params) {
return request({
url: `${urlType}/admin/coupon/page`,
method: "GET",
params
});
}
/**
* 优惠券-添加/编辑
* @param {Object} params
*/
export function couponAdd(data) {
return request({
url: `${urlType}/admin/coupon`,
method: data.id ? 'put' : 'post',
data
});
}
/**
* 优惠券-删除
* @param id
*/
export function couponDel(id) {
return request({
url: `${urlType}/admin/coupon?id=${id}&type=0`,
method: 'delete'
});
}
/**
* 优惠券-详情
* @param id
*/
export function couponDetail(id) {
return request({
url: `${urlType}/admin/coupon/${id}`,
method: "GET"
});
}
/**
* 消费赠券-添加
* @param {Object} data
*/
export function couponPost(data) {
return request({
url: `${urlType}/admin/consumerCoupon/addConsumerCoupon`,
method: 'post',
data
});
}
/**
* 消费赠券-更新
* @param {Object} data
*/
export function updateConsumerCouponById(data) {
return request({
url: `${urlType}/admin/consumerCoupon/updateConsumerCouponById`,
method: 'put',
data
});
}
/**
* 消费赠券-分页
* @param {Object} data
*/
export function getConsumerCouponPage(params) {
return request({
url: `${urlType}/admin/consumerCoupon/getConsumerCouponPage`,
method: 'get',
params
});
}
/**
* 消费赠券-删除
* @param {Object} data
*/
export function deleteConsumerCoupon(id) {
return request({
url: `${urlType}/admin/consumerCoupon/deleteConsumerCoupon?id=${id}`,
method: 'DELETE',
});
}
/**
* 消费赠券-详情
* @param {Object} data
*/
export function getConsumerCouponById(params) {
return request({
url: `${urlType}/admin/consumerCoupon/getConsumerCouponById`,
method: 'get',
params
});
}
/**
* 优惠券列表/已领取详情
* @param {Object} data
*/
export function couponGetRecord(params) {
return request({
url: `${urlType}/admin/coupon/record`,
method: 'get',
params
});
}
/**
* 智慧充值规/配置信息修改
* @param {Object} data
*/
export function shopRechargePost(data) {
return request({
url: `${urlType}/admin/shopRecharge`,
method: 'post',
data
});
}
/**
* 智慧充值规/配置信息获取
* @param {Object} data
*/
export function shopRechargeGet(data) {
return request({
url: `${urlType}/admin/shopRecharge`,
method: 'get',
data
});
}
/**
* 获取店铺用户充值记录
* @param {Object} data
*/
export function shopUserFlow(data) {
return request({
url: `/account/admin/shopUser/flow`,
method: 'get',
data
});
}

View File

@@ -1,17 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
/**
* 限时折扣
* @returns
*/
export function limitTimeDiscount(params) {
return request({
url: urlType + `/admin/limitTimeDiscount`,
method: "get",
params: {
...params,
},
});
}

View File

@@ -1,67 +0,0 @@
import http from '@/http/http.js'
const request = http.request
const urlType='market'
export function getConfig(data) {
return request({
url: `${urlType}/admin/member`,
method: "GET",
data: {
...data
}
})
}
export function editConfig(data) {
return request({
url: `${urlType}/admin/member`,
method: "POST",
data: {
...data
}
})
}
export function levelList(data) {
return request({
url: `${urlType}/admin/member/level/list`,
method: "GET",
data: {
...data
}
})
}
export function orderList(data) {
return request({
url: `${urlType}/admin/member/order`,
method: "GET",
data: {
...data
}
})
}
export function levelAdd(data) {
return request({
url: `${urlType}/admin/member/level`,
method: "POST",
data: {
...data
}
})
}
export function levelEdit(data) {
return request({
url: `${urlType}/admin/member/level`,
method: "PUT",
data: {
...data
}
})
}
export function levelDel(id) {
return request({
url: `${urlType}/admin/member/level/${id}`,
method: "DELETE",
})
}

View File

@@ -1,145 +0,0 @@
import http from '@/http/http.js'
const request = http.request
const MARKET_URL = 'market'
const ORDER_URL = 'order'
/**
* 积分:配置:新增/更新
* @param {Object} data
*/
export function pointsConfigPost(data) {
return request({
url: `${MARKET_URL}/admin/points/config`,
method: "POST",
data
})
}
/**
* 积分:配置:详情
* @param {Object} data
*/
export function pointsConfigGet() {
return request({
url: `${MARKET_URL}/admin/points/config`,
method: "GET"
})
}
/**
* 积分:商品:列表
* @param {Object} data
*/
export function pointsGoodsPage(data) {
return request({
url: `${MARKET_URL}/admin/pointsGoods/page`,
method: "GET",
data
})
}
/**
* 积分:商品:新增/修改
* @param {Object} data
*/
export function pointsGoodsPost(data) {
return request({
url: `${MARKET_URL}/admin/pointsGoods`,
method: "POST",
data
})
}
/**
* 积分-商品-删除
* @param {Object} data
*/
export function pointsGoodsDel(id) {
return request({
url: `${MARKET_URL}/admin/pointsGoods/${id}`,
method: "DELETE"
})
}
/**
* 积分:商品:详情
* @param {Object} data
*/
export function pointsGoodsDetail(id) {
return request({
url: `${MARKET_URL}/admin/pointsGoods/${id}`,
method: "GET"
})
}
/**
* 积分:积分商品:兑换记录
* @param {Object} data
*/
export function goodsRecordPage(data) {
return request({
url: `${ORDER_URL}/admin/points/goodsRecord/page`,
method: "GET",
data
})
}
/**
* 积分:积分商品:商家核销
* @param {Object} data
*/
export function goodsRecordCkecout(data) {
return request({
url: `${ORDER_URL}/admin/points/goodsRecord/checkout`,
method: "post",
data
})
}
/**
* 积分:积分商品:同意退单
* @param {Object} data
*/
export function goodsRecordAgreeRefund(data) {
return request({
url: `${ORDER_URL}/admin/points/goodsRecord/agreeRefund`,
method: "post",
data
})
}
/**
* 积分:积分商品:驳回退单
* @param {Object} data
*/
export function goodsRecordRejectRefund(data) {
return request({
url: `${ORDER_URL}/admin/points/goodsRecord/rejectRefund`,
method: "post",
data
})
}
/**
* 积分:获取用户所有门店下积分列表
* @param {Object} data
*/
export function pointUserPage(data) {
return request({
url: `${MARKET_URL}/admin/points/userPage`,
method: "GET",
data
})
}
/**
* 积分:积分详情
* @param {Object} data
*/
export function pointUserRecord(data) {
return request({
url: `${MARKET_URL}/admin/points/userRecord`,
method: "GET",
data
})
}

View File

@@ -1,77 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
export function enable(params) {
return request({
url: urlType + `/admin/rechargeRedemption/enable`,
method: "PUT",
params: {
...params,
},
});
}
export function status(params) {
return request({
url: urlType + `/admin/rechargeRedemption/enable/status`,
method: "get",
params: {
...params,
},
});
}
export function couponRedemption(params) {
return request({
url: urlType + `/admin/rechargeRedemption`,
method: "get",
params: {
...params,
},
});
}
export function couponRedemptionList(params) {
return request({
url: urlType + `/admin/rechargeRedemption/list`,
method: "get",
params: {
...params,
},
});
}
export function add(params) {
return request({
url: urlType + `/admin/rechargeRedemption`,
method: "POST",
params: {
...params,
},
});
}
export function edit(params) {
return request({
url: urlType + `/admin/rechargeRedemption`,
method: "PUT",
params: {
...params,
},
});
}
export function codeList(params) {
return request({
url: urlType + `/admin/rechargeRedemption/code/list`,
method: "GET",
params: {
...params,
},
});
}
export function codeExport(params) {
return http.download({
url: urlType + `/admin/rechargeRedemption/code/export`,
method: "GET",
params: {
...params,
},
});
}

View File

@@ -1,41 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "market";
export function suggestPage(params) {
return request({
url: urlType + `/admin/suggest/page`,
method: "get",
params: {
...params,
},
});
}
export function addSuggest(params) {
return request({
url: urlType + `/admin/suggest`,
method: "POST",
params: {
...params,
},
});
}
export function editSuggest(params) {
return request({
url: urlType + `/admin/suggest`,
method: "PUT",
params: {
...params,
},
});
}
export function deleteSuggest(params) {
return request({
url: urlType + `/admin/suggest?id=${params.id}`,
method: "DELETE",
params: {
...params,
},
});
}

View File

@@ -1,9 +0,0 @@
import http from '@/http/http.js'
const request = http.request
const prveUrl='account'
export function getMenus() {
return request({
url: `${prveUrl}/admin/menus`,
method: "GET",
})
}

View File

@@ -1,33 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "order";
export function tableSummaryList(data) {
return request({
url: `${urlType}/admin/table/summary/list`,
method: "GET",
data: {
...data,
},
});
}
export function saleSummaryPage(data) {
return request({
url: `${urlType}/admin/sale/summary/page`,
method: "GET",
data: {
...data,
},
});
}
export function saleSummaryCount(data) {
return request({
url: `${urlType}/admin/sale/summary/count`,
method: "GET",
data: {
...data,
},
});
}

View File

@@ -8,7 +8,7 @@ const request = http.request
export function getOrderPayUrl(data, urlType = 'order') {
return request({
url: `${urlType}/pay/shopPayApi/orderPayUrl`,
method: "POST",
method: "GET",
data: {
...data
}
@@ -98,13 +98,3 @@ export function queryOrderStatus(data, urlType = 'order') {
}
})
}
// 分销员开通支付
export function mchRecharge(data, urlType = 'order') {
return request({
url: `${urlType}/pay/distribution/mchRecharge`,
method: "POST",
data: {
...data
}
})
}

View File

@@ -5,7 +5,7 @@ const request = http.request
* 获取商品分页
* @returns
*/
export function getProductPage(data, urlType = 'product', showLoading) {
export function getProductPage(data, urlType = 'product' ,showLoading) {
return request({
url: `${urlType}/admin/product/page`,
method: "GET",
@@ -20,7 +20,7 @@ export function getProductPage(data, urlType = 'product', showLoading) {
* 获取商品列表
* @returns
*/
export function getProductList(data, urlType = 'product', showLoading) {
export function getProductList(data, urlType = 'product' ,showLoading) {
return request({
url: `${urlType}/admin/product/list`,
method: "GET",
@@ -36,11 +36,11 @@ export function getProductList(data, urlType = 'product', showLoading) {
* 获取商品详情
* @returns
*/
export function getProductDetail(id, urlType = 'product') {
export function getProductDetail (id, urlType = 'product') {
return request({
url: `${urlType}/admin/product/${id}`,
method: "GET",
})
}
@@ -73,7 +73,7 @@ export function delProduct(id, urlType = 'product') {
* 商品上下架
* @returns
*/
export function productOnOff(data, urlType = 'product') {
export function productOnOff (data, urlType = 'product') {
return request({
url: `${urlType}/admin/product/onOff`,
method: "POST",
@@ -87,7 +87,7 @@ export function productOnOff(data, urlType = 'product') {
* 商品售罄
* @returns
*/
export function productMarkIsSoldOut(data, urlType = 'product') {
export function productMarkIsSoldOut (data, urlType = 'product') {
return request({
url: `${urlType}/admin/product/markIsSoldOut`,
method: "POST",
@@ -262,7 +262,7 @@ export function delSpec(id, urlType = 'product') {
return request({
url: `${urlType}/admin/prod/spec/${id}`,
method: "DELETE",
})
}
@@ -343,28 +343,4 @@ export function delProdGroup(id, urlType = 'product') {
url: `${urlType}/admin/prod/group/${id}`,
method: "DELETE",
})
}
/**
* 入库单识别
* @returns
*/
export function stockOcr(data, urlType = 'product') {
return request({
url: `${urlType}/admin/product/stock/ocr`,
method: "post",
data
})
}
/**
* ocr识别结果
* @returns
*/
export function ocrResult(data, urlType = 'product') {
return request({
url: `${urlType}/admin/product/stock/ocrResult`,
method: "get",
data
})
}

View File

@@ -1,7 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "product";
export function stickCount(file, data) {
return http.upload(`${urlType}/admin/stick/count`,data,file)
}

View File

@@ -56,17 +56,3 @@ export function editShopExtend(data, urlType = 'account') {
}
})
}
/**
* 获取门店列表
* @returns
*/
export function getShopList(data, urlType = 'account') {
return request({
url: `${urlType}/admin/shopInfo/branchList`,
method: "GET",
data: {
...data
}
})
}

View File

@@ -21,7 +21,7 @@ export function getShopTable(data, urlType = 'account') {
*/
export function getShopTableDetail(data, urlType = 'account') {
return request({
url: `/account/admin/shopTable/detail`,
url: `${urlType}/admin/shopTable/detail`,
method: "GET",
data: {
...data

View File

@@ -15,18 +15,6 @@ export function getVendorPage(data, urlType = 'product') {
})
}
/**
* 获取供应商列表/无分页
* @returns
*/
export function getVendorList(urlType = 'product') {
return request({
url: `${urlType}/admin/product/vendor/list`,
method: "GET"
})
}
/**
* 添加供应商
* @returns
@@ -64,4 +52,5 @@ export function delVendor(id, urlType = 'product') {
url: `${urlType}/admin/product/vendor/${id}`,
method: "DELETE",
})
}
}

View File

@@ -1,303 +0,0 @@
import http from '@/http/http.js'
const request = http.request
const ORDER_URL = 'order'
const Market_BaseUrl = 'market'
/**
* 拼团商品-列表
* @param {Object} data
*/
export function getGbWarePage(data) {
return request({
url: `${ORDER_URL}/admin/ware/getGbWarePage`,
method: "GET",
data
})
}
/**
* 拼团商品-新增
* @param {Object} data
*/
export function addGbWare(data) {
return request({
url: `${ORDER_URL}/admin/ware/addGbWare`,
method: "post",
data
})
}
/**
* 拼团商品-列表
* @param {Object} data
*/
export function updateGbWareById(data) {
return request({
url: `${ORDER_URL}/admin/ware/updateGbWareById`,
method: "post",
data
})
}
/**
* 拼团商品-修改状态
* @param {Object} data
*/
export function editOnlineStatus(data) {
return request({
url: `${ORDER_URL}/admin/ware/editOnlineStatus`,
method: "post",
data
})
}
/**
* 拼团商品-删除
* @param {Object} data
*/
export function deleteGbWare(id) {
return request({
url: `${ORDER_URL}/admin/ware/deleteGbWare/${id}`,
method: "DELETE"
})
}
/**
* 拼团商品:订单列表
* @param {Object} data
*/
export function gbOrderPage(data) {
return request({
url: `${ORDER_URL}/admin/gbOrder/page`,
method: "GET",
data
})
}
/**
* 拼团商品-活动开关
* @param {Object} data
*/
export function upShopConfig(data) {
return request({
url: `${ORDER_URL}/admin/ware/upShopConfig`,
method: "POST",
data
})
}
/**
* 拼团订单-退单/同意退单
* @param {Object} data
*/
export function agreeRefund(data) {
return request({
url: `${ORDER_URL}/admin/gbOrder/agreeRefund`,
method: "POST",
data
})
}
/**
* 拼团订单-驳回退单
* @param {Object} data
*/
export function rejectRefund(data) {
return request({
url: `${ORDER_URL}/admin/gbOrder/rejectRefund`,
method: "POST",
data
})
}
/**
* 拼团商品:核销
* @param {Object} data
*/
export function checkout(data) {
return request({
url: `${ORDER_URL}/admin/gbOrder/checkout`,
method: "POST",
data
})
}
/**
* 拼团商品:拼团商品详情
* @param {Object} data
*/
export function wareDetail(data) {
return request({
url: `${ORDER_URL}/admin/ware/ware/detail`,
method: "get",
data
})
}
/**
* 套餐推广:添加套餐
* @param {*} data
* @returns
*/
export function packageAddEdit(data) {
return request({
url: `${Market_BaseUrl}/admin/package`,
method: data.id ? 'put' : 'post',
data,
});
}
/**
* 套餐推广:获取套餐列表
* @param {*} data
* @returns
*/
export function packageGet(params) {
return request({
url: `${Market_BaseUrl}/admin/package`,
method: 'get',
params,
});
}
/**
* 套餐推广:获取套餐推广开关
* @param {*} data
* @returns
*/
export function packageSwitchGet() {
return request({
url: `${Market_BaseUrl}/admin/package/switch`,
method: 'get'
});
}
/**
* 套餐推广:修改套餐推广开关
* @param {*} data
* @returns
*/
export function packageSwitchPut(data) {
return request({
url: `${Market_BaseUrl}/admin/package/switch`,
method: 'put',
data
});
}
/**
* 套餐推广:删除套餐
* @param {*} data
* @returns
*/
export function packageDel(id) {
return request({
url: `${Market_BaseUrl}/admin/package/${id}`,
method: 'DELETE'
});
}
/**
* 套餐推广:确认删除套餐
* @param {*} data
* @returns
*/
export function packageSureDel(id) {
return request({
url: `${Market_BaseUrl}/admin/package/sure/${id}`,
method: 'DELETE'
});
}
/**
* 套餐推广:修改套餐推广开关
* @param {*} data
* @returns
*/
export function packageOnline(data) {
return request({
url: `${Market_BaseUrl}/admin/package/online`,
method: 'put',
data
});
}
/**
* 套餐推广:获取套餐推广订单列表
* @param {*} data
* @returns
*/
export function packageOrder(params) {
return request({
url: `${Market_BaseUrl}/admin/package/order`,
method: 'GET',
params
});
}
/**
* 套餐推广:订单统计
* @param {*} data
* @returns
*/
export function packageOrderStat(params) {
return request({
url: `${Market_BaseUrl}/admin/package/order/stat`,
method: 'GET',
params
});
}
/**
* 套餐推广:确认退单
* @param {*} data
* @returns
*/
export function packageConfirmRefund(data) {
return request({
url: `${ORDER_URL}/admin/ppOrder/confirmRefund`,
method: 'post',
data
});
}
/**
* 套餐推广:驳回退单
* @param {*} data
* @returns
*/
export function packageRejectRefund(data) {
return request({
url: `${ORDER_URL}/admin/ppOrder/rejectRefund`,
method: 'post',
data
});
}
/**
* 套餐推广:核销
* @param {*} data
* @returns
*/
export function packageCheckout(data) {
return request({
url: `${ORDER_URL}/admin/ppOrder/checkout`,
method: 'post',
data
});
}
/**
* 套餐推广:获取套餐详情
* @param {*} data
* @returns
*/
export function packageDetail(data) {
return request({
url: `${Market_BaseUrl}/admin/package/detail/${data.id}`,
method: 'GET',
data: {
shopId: data.shopId
}
});
}

View File

@@ -1,26 +1,26 @@
/**
* HTTP的封装 基于uni.request
* 包括: 通用响应结果的处理 和 业务的增删改查函数
*
*
* @author terrfly
* @site https://www.jeequan.com
* @date 2021/12/16 18:35
*/
// 设置env配置文件
import envConfig from "@/env/config.js";
import envConfig from '@/env/config.js'
// 导入全局属性
import appConfig from "@/config/appConfig.js";
import storageManage from "@/commons/utils/storageManage.js";
import infoBox from "@/commons/utils/infoBox.js";
import go from "@/commons/utils/go.js";
import { reject } from "lodash";
import appConfig from '@/config/appConfig.js'
import storageManage from '@/commons/utils/storageManage.js'
import { sm4DecryptByResData } from '@/commons/utils/encryptUtil.js'
import infoBox from "@/commons/utils/infoBox.js"
import go from '@/commons/utils/go.js';
import { reject } from 'lodash';
// 设置node环境
envConfig.changeEnv(storageManage.env('production')) //正式
// envConfig.changeEnv(storageManage.env("development")); //测试
envConfig.changeEnv(storageManage.env())
// 测试服
// #ifdef H5
let baseUrl = "/javaapi/";
let baseUrl = '/api/'
// #endif
// #ifndef H5
// let baseUrl = 'https://tapi.cashier.sxczgkj.cn/'
@@ -29,247 +29,215 @@ let baseUrl = "/javaapi/";
//正式
// let baseUrl = 'https://cashier.sxczgkj.com/'
let baseUrl = appConfig.env.JEEPAY_BASE_URL;
let baseUrl = appConfig.env.JEEPAY_BASE_URL
// #endif
const loadingShowTime = 200;
const loadingShowTime = 200
function getHeader() {
const headerObject = {};
headerObject["token"] = storageManage.token();
headerObject["shopId"] = uni.getStorageSync("shopInfo").id;
headerObject["platformType"] = "APP";
return headerObject;
function getHeader(){
const headerObject={}
headerObject["token"] = storageManage.token()
headerObject["shopId"] = uni.getStorageSync("shopInfo").id
headerObject["platformType"] = 'APP'
return headerObject
}
// 通用处理逻辑
// 通用处理逻辑
function commonsProcess(showLoading, httpReqCallback) {
// 判断是否请求完成(用作 是否loading
// 包括: 'ing', 'ingLoading', 'finish'
let reqState = "ing";
// 是否已经提示的错误信息
let isShowErrorToast = false;
// 判断是否请求完成(用作 是否loading
// 包括: 'ing', 'ingLoading', 'finish'
let reqState = 'ing'
// 请求完成, 需要处理的动作
let reqFinishFunc = () => {
if (reqState == "ingLoading") {
// 关闭loading弹层
infoBox.hideLoading();
}
reqState = "finish"; // 请求完毕
};
// 是否已经提示的错误信息
let isShowErrorToast = false
// 明确显示loading
if (showLoading) {
// xx ms内响应完成不提示loading
setTimeout(() => {
if (reqState == "ing") {
reqState = "ingLoading";
infoBox.showLoading();
}
}, loadingShowTime);
}
return httpReqCallback()
.then((httpData) => {
reqFinishFunc(); // 请求完毕的动作
// 从http响应数据中解构响应数据 [ 响应码、 bodyData ]
let { statusCode, data } = httpData;
// 避免混淆重新命名
let bodyData = data;
if (statusCode == 500) {
isShowErrorToast = true;
return Promise.reject(bodyData); // 跳转到catch函数
}
if (statusCode == 501) {
// storageManage.token(null, true)
// 提示信息
isShowErrorToast = true;
// infoBox.showErrorToast('请登录').then(() => {
// go.to("PAGES_LOGIN", {}, go.GO_TYPE_RELAUNCH)
// })
return Promise.reject(bodyData); // 跳转到catch函数
}
// http响应码不正确
if (statusCode != 200 && statusCode != 204 && statusCode != 201) {
isShowErrorToast = true;
bodyData.msg =
bodyData.msg == "Bad credentials" ? "用户名或密码错误" : bodyData.msg;
infoBox.showToast(bodyData.msg || "服务器异常");
return Promise.reject(bodyData); // 跳转到catch函数
}
// 请求完成, 需要处理的动作
let reqFinishFunc = () => {
// // 业务响应异常
if (bodyData.hasOwnProperty("code") && bodyData.code != 200) {
isShowErrorToast = true;
infoBox.showToast(bodyData.msg);
// if (bodyData.code == 5005) { // 密码已过期, 直接跳转到更改密码页面
// uni.reLaunch({
// url: '/pageUser/setting/updatePwd'
// })
// }
// if(bodyData.code == 500){ // 密码已过期, 直接跳转到更改密码页面
// uni.redirectTo({url: '/pages/login/index'})
// }
return Promise.reject(bodyData); // 跳转到catch函数
}
if (reqState == 'ingLoading') { // 关闭loading弹层
infoBox.hideLoading()
}
reqState = 'finish' // 请求完毕
}
// 构造请求成功的响应数据
return Promise.resolve(bodyData.data);
})
.catch((res) => {
console.log(res);
if (res.code == 501) {
storageManage.token(null, true);
infoBox.showToast("登录过期,请重新登录").then(() => {
uni.redirectTo({ url: "/pages/login/index" });
reject();
});
}
// if(res.status==400){
// storageManage.token(null, true)
// infoBox.showErrorToast('').then(() => {
// go.to("PAGES_LOGIN", {}, go.GO_TYPE_RELAUNCH)
// })
// }
if (res.code == 500) {
infoBox.showToast(res.msg || "服务器异常").then(() => {});
}
// if(res&&res.msg){
// infoBox.showErrorToast(res.msg)
// }
reqFinishFunc(); // 请求完毕的动作
// 明确显示loading
if (showLoading) {
// xx ms内响应完成不提示loading
setTimeout(() => {
if (reqState == 'ing') {
reqState = 'ingLoading'
infoBox.showLoading()
}
}, loadingShowTime)
}
// 如果没有提示错误, 那么此处提示 异常。
if (!isShowErrorToast) {
infoBox.showToast(`请求网络异常`);
}
return httpReqCallback().then((httpData) => {
reqFinishFunc(); // 请求完毕的动作
// 从http响应数据中解构响应数据 [ 响应码、 bodyData ]
let {
statusCode,
data
} = httpData
// 避免混淆重新命名
let bodyData = data
if (statusCode == 500) {
isShowErrorToast = true
return Promise.reject(bodyData) // 跳转到catch函数
}
if (statusCode == 501) {
// storageManage.token(null, true)
// 提示信息
isShowErrorToast = true
// infoBox.showErrorToast('请登录').then(() => {
// go.to("PAGES_LOGIN", {}, go.GO_TYPE_RELAUNCH)
// })
return Promise.reject(bodyData) // 跳转到catch函数
}
// http响应码不正确
if (statusCode != 200 && statusCode != 204 && statusCode != 201) {
isShowErrorToast = true
bodyData.msg=bodyData.msg=='Bad credentials'?'用户名或密码错误':bodyData.msg
infoBox.showToast(bodyData.msg || '服务器异常')
return Promise.reject(bodyData) // 跳转到catch函数
}
// // 业务响应异常
if (bodyData.hasOwnProperty('code') && bodyData.code != 200) {
isShowErrorToast = true
infoBox.showToast(bodyData.msg)
// if (bodyData.code == 5005) { // 密码已过期, 直接跳转到更改密码页面
// uni.reLaunch({
// url: '/pageUser/setting/updatePwd'
// })
// }
// if(bodyData.code == 500){ // 密码已过期, 直接跳转到更改密码页面
// uni.redirectTo({url: '/pages/login/index'})
// }
return Promise.reject(bodyData) // 跳转到catch函数
}
// 加密数据
if (!bodyData.data && bodyData.encryptData) {
return Promise.resolve({
bizData: sm4DecryptByResData(bodyData.encryptData),
code: bodyData.code
})
}
// 构造请求成功的响应数据
return Promise.resolve(bodyData.data)
}).catch(res => {
console.log(res)
if(res.code==501){
storageManage.token(null, true)
infoBox.showToast('登录过期,请重新登录').then(() => {
uni.redirectTo({url: '/pages/login/index'})
reject()
})
}
// if(res.status==400){
// storageManage.token(null, true)
// infoBox.showErrorToast('').then(() => {
// go.to("PAGES_LOGIN", {}, go.GO_TYPE_RELAUNCH)
// })
// }
if(res.code==500){
infoBox.showToast(res.msg||'服务器异常').then(() => {})
}
// if(res&&res.msg){
// infoBox.showErrorToast(res.msg)
// }
reqFinishFunc(); // 请求完毕的动作
// 如果没有提示错误, 那么此处提示 异常。
if (!isShowErrorToast) {
infoBox.showToast(`请求网络异常`)
}
return Promise.reject(res)
}).finally(() => { // finally 是 then结束后再执行, 此处不适用。 需要在请求完成后立马调用: reqFinishFunc()
});
return Promise.reject(res);
})
.finally(() => {
// finally 是 then结束后再执行, 此处不适用。 需要在请求完成后立马调用: reqFinishFunc()
});
}
// 默认 显示loading(控制 xxs 内 不提示loading )
function req(uri, data, method = "GET", showLoading = true, extParams = {}) {
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.request(
Object.assign(
{
url: baseUrl + uri,
data: data,
method: method,
header: getHeader(),
},
extParams
)
);
});
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.request(
Object.assign({
url: baseUrl + uri,
data: data,
method: method,
header: getHeader()
}, extParams)
)
})
}
// 默认 显示loading(控制 xxs 内 不提示loading )
function request(args) {
const {
url,
data,
params,
method = "GET",
showLoading = true,
extParams = {},
} = args;
let headerObject = {};
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.request(
Object.assign(
{
url: baseUrl + url,
data: params || data,
method: method,
header: getHeader(),
},
extParams
)
);
});
const {
url,
data,
params,
method = "GET",
showLoading = true,
extParams = {}
} = args
let headerObject = {}
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.request(
Object.assign({
url: baseUrl + url,
data: params||data,
method: method,
header: getHeader()
}, extParams)
)
})
}
// 上传
function upload(uri, data, file, showLoading = true, extParams = {}) {
// 放置token
let headerObject = {};
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni
.uploadFile(
Object.assign(
{
url: baseUrl + uri,
formData: data,
name: "file",
filePath: file.path || file.url ||file,
header: getHeader(),
},
extParams
)
)
.then((httpData) => {
// uni.upload 返回bodyData 的是 string类型。 需要解析。
httpData.data = JSON.parse(httpData.data);
return Promise.resolve(httpData);
})
.catch((err) => {
uni.hideLoading();
infoBox.showErrorToast(`上传失败`);
});
});
}
// 放置token
let headerObject = {}
// headerObject[appConfig.tokenKey] = storageManage.token()
function returnParams(params) {
let returnStr = "";
if (params) {
returnStr = "?";
for (let key in params) {
returnStr += `${key}=${params[key]}&`;
}
returnStr = returnStr.substring(0, returnStr.length - 1);
}
return returnStr;
}
// 下载文件
function download(args) {
const {
url,
data,
params,
method = "GET",
showLoading = true,
extParams = {},
} = args;
let headerObject = {};
return uni
.downloadFile({
url: baseUrl + url + returnParams(params),
header: getHeader(),
})
.then((httpData) => {
return Promise.resolve(httpData);
})
.catch((err) => {
uni.hideLoading();
infoBox.showErrorToast(`下载失败`);
});
return commonsProcess(showLoading, () => {
return uni.uploadFile(
Object.assign({
url: baseUrl + uri,
formData: data,
name: "file",
filePath: file.path||file.url,
header: getHeader()
}, extParams)
).then((httpData) => {
// uni.upload 返回bodyData 的是 string类型。 需要解析。
httpData.data = JSON.parse(httpData.data)
return Promise.resolve(httpData)
}).catch(err=>{
uni.hideLoading()
infoBox.showErrorToast(`上传失败`)
})
})
}
export default {
req: req,
request,
upload: upload,
download,
};
req: req,
request,
upload: upload
}

View File

@@ -1,92 +0,0 @@
import { request } from "./request";
const prveUrl = "chat/";
/**
* 群消息
* @param {Object} data
* @returns
*/
export const groupInfo = (data) => {
return request(prveUrl + "group/info", "POST", data, true);
};
export const commonPhrase = (data) => {
return request(prveUrl + "common-phrase/index", "POST", data, true);
};
export const commonPhraseAdd = (data) => {
return request(prveUrl + "common-phrase/add", "POST", data, true);
};
export const commonPhraseDel = (data) => {
return request(prveUrl + "common-phrase/del", "POST", data, true);
};
export const groupCreate = (data) => {
return request(prveUrl + "group/create", "POST", data, true);
};
export const groupJoin = (data) => {
return request(prveUrl + "group/join", "POST", data, true);
};
export const groupGetGroupUrl = (data) => {
return request(prveUrl + "group/getgrepurl", "POST", data, true);
};
export const groupQuit = (data) => {
return request(prveUrl + "group/quit", "POST", data, true);
};
export const groupTarsGroup = (data) => {
return request(prveUrl + "group/tarsgroup", "POST", data, true);
};
export const groupAnnouncement = (data) => {
return request(prveUrl + "group/announcement", "POST", data, true);
};
export const groupMute = (data) => {
return request(prveUrl + "group/mute", "POST", data, true);
};
export const groupMunute = (data) => {
return request(prveUrl + "group/unmute", "POST", data, true);
};
export const groupKick = (data) => {
return request(prveUrl + "group/kick", "POST", data, true);
};
export const groupMembers = (data) => {
return request(prveUrl + "group/members", "POST", data, true);
};
export const messageHistory = (data) => {
return request(prveUrl + "message/history", "POST", data, true);
};
export const messageMarkRead = (data) => {
return request(prveUrl + "message/mark-read", "POST", data, true);
};
export const messageMarkReadAll = (data) => {
return request(prveUrl + "message/mark-read-all", "POST", data, true);
};
export const messageUnreadCount = (data) => {
return request(prveUrl + "message/unread-count", "POST", data, true);
};
export const messageSessionList = (data) => {
return request(prveUrl + "message/sessionlist", "POST", data, true);
};
export const groupDoNotDisturb = (data) => {
return request(prveUrl + "group/do-not-disturb", "POST", data, true);
};
export const groupEditTitle = (data) => {
return request(prveUrl + "group/editTitle", "POST", data, true);
};

View File

@@ -1,100 +1,80 @@
//服务器接口地址
// const baseURL : string = 'https://newblockwlx.sxczgkj.cn/index.php/api/'
let baseURL: string = "http://192.168.1.42:8787/api/";
// #ifdef H5
baseURL = "/phpapi/api/";
// #endif
import go from "@/commons/utils/go.js";
const baseURL : string = 'https://newblockwlx.sxczgkj.cn/index.php/api/'
// 封装公共请求方法
function request(
url: string,
method: "GET" | "POST" | undefined,
data: object | any,
toast: boolean
) {
let networkType = "";
uni.getNetworkType({
success: (res) => {
networkType = res.networkType;
},
});
if (networkType == "none") {
uni.showToast({
title: "网络异常,请检查网络",
icon: "none",
});
return false;
}
if (toast) {
uni.showLoading({
title: "加载中",
mask: true,
});
}
return new Promise(async (resolve, reject) => {
let header: any;
header = {
"content-type": "application/json",
clinttype: uni.getStorageSync("clint_type"),
bausertoken: uni.getStorageSync("phpuserinfo").token || "",
token: uni.getStorageSync("iToken").tokenValue || "",
};
uni.request({
url: baseURL + url,
method: method,
data: data,
header: header,
success(res: any) {
if (res.data.code != 1) {
if (res.data.code === 3000) {
uni.hideLoading();
go.to("PAGES_LOGIN", {}, "redirect");
reject();
return;
}
//是否提示错误
if (toast) {
uni.showToast({
title: res.data.msg || res.data.message,
icon: "none",
});
setTimeout(() => {
uni.hideLoading();
}, 1000);
}
if (res.data.code == 401) {
uni.showToast({
title: res.message || res.msg,
icon: "none",
success: () => {
// uni.removeStorageSync('logintoken');
// uni.removeStorageSync('token');
uni.reLaunch({
url: "/pages/index/index",
});
},
});
}
uni.hideLoading();
reject(res.message | res.msg);
} else {
uni.hideLoading();
console.log(res);
resolve(res.data.data);
}
},
fail(err) {
uni.hideLoading();
//请求失败
uni.showToast({
title: "无法连接到服务器",
icon: "none",
});
reject(err);
},
});
});
function request(url : string, method : "GET" | "POST" | undefined, data : object | any, toast : boolean) {
let networkType = ''
uni.getNetworkType({
success: (res) => {
networkType = res.networkType
}
});
if (networkType == 'none') {
uni.showToast({
title: '网络异常,请检查网络',
icon: 'none'
})
return false;
}
if (toast) {
uni.showLoading({
title: '加载中',
mask: true
})
}
return new Promise(async (resolve, reject) => {
let header : any
header = {
'content-type': 'application/json',
'clinttype':uni.getStorageSync('clint_type'),
'bausertoken': uni.getStorageSync('phpuserinfo').token
};
uni.request({
url: baseURL + url,
method: method,
data: data,
header: header,
success(res : any) {
if (res.data.code != 1) {
//是否提示错误
if (toast) {
uni.showToast({
title: res.data.msg || res.data.message,
icon: 'none'
})
setTimeout(() => {
uni.hideLoading()
}, 1000)
}
if (res.data.code == 401) {
uni.showToast({
title: res.message || res.msg,
icon: "none",
success: () => {
// uni.removeStorageSync('logintoken');
// uni.removeStorageSync('token');
uni.reLaunch({
url: '/pages/index/index'
})
}
})
}
uni.hideLoading()
reject(res.message | res.msg);
} else {
uni.hideLoading()
resolve(res.data.data);
}
},
fail(err) {
uni.hideLoading()
//请求失败
uni.showToast({
title: '无法连接到服务器',
icon: 'none'
})
reject(err)
}
})
})
}
export { request, baseURL };
export { request, baseURL }

View File

@@ -1,415 +0,0 @@
// 代课下单
import http from '@/http/yskApi/http.js'
import { accountUrl, marketUrl } from './prveUrl.js'
const request = http.request
//就餐形式,默认堂食后付费
const useType = 'dine-in-after'
function getUseType() {
const type = uni.getStorageSync("useType")
return type ? type : useType
}
// import {
// webscoketUtill
// } from '../websock.js'
// let wxObj = null
// wx初始化购物车
// export function getWXCart(params) {
// let wxUrl = 'ws://192.168.1.31:2348/?' + objectToString(params)
// wxObj = new webscoketUtill(wxUrl,3000,9,(e)=>{
// console.log('收到消息');
// console.log(e);
// })
// return uni.getStorageSync('wxList')
// }
// 新增\删除\修改到购物车
// export function addWXCart(params) {
// wxObj.ws.send(params)
// }
function objectToString(obj) {
let result = '';
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result += `${key}=${obj[key]}&`;
}
}
// 去掉最后一个多余的 &
return result.slice(0, -1);
}
/**
* 获取当前台桌订单信息
* @returns
*/
export function getCart(params) {
return request({
url: `/api/place/cart`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...params
}
});
}
/**
* 已上架商品列表
* @returns
*/
export function getGoodsLists(params, showLoading = true) {
return request({
url: `/product/admin/product/list`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...params
},
showLoading
});
}
/**
* 点单
* @returns
*/
export function addCart(data) {
return request({
url: `/api/place/addCart`,
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 清空购物车/支付订单
* @returns
*/
export function $clearCart(data) {
return request({
url: `/api/place/clearCart`,
method: "delete",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 删除购物车某个商品
* @returns
*/
export function $removeCart(data) {
return request({
url: `/api/place/removeCart`,
method: "delete",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 更新规格
* @returns
*/
export function $updateCart(data) {
return request({
url: `/api/place/updateCart`,
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
/**
* 批量打包
* @returns
*/
export function $allPack(data) {
return request({
url: `/api/place/pack`,
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
/**
* 获取取餐号
* @returns
*/
export function $getMasterId(data) {
return request({
url: `/api/place/masterId`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 支付方式获取
* @returns
*/
export function $getPayType(data) {
return request({
url: `/account/admin/payType`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
/**
* 创建订单
* @returns
*/
export function $createOrder(data) {
return request({
url: `/api/place/order`,
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 挂起订单
* @returns
*/
export function $cacheOrder(data) {
return request({
url: `/api/place/pending`,
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
/**
* 获取已挂起订单
* @returns
*/
export function $getCacheOrder(data) {
return request({
url: `/api/place/pending/cart`,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
// 会员点单/取消会员点单
export function $setUser(data) {
return request({
url: `/api/place/updateVip`,
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 删除订单
export function $delOrder(data) {
return request({
url: `/api/place/order`,
method: "delete",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 支付订单
export function $payOrder(data) {
return request({
url: '/api/place/pay',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//退单
export function $returnCart(data) {
return request({
url: '/api/place/returnCart',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 选择台桌
export function $choseTable(data) {
return request({
url: '/api/place/choseTable',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 用餐人数
export function $choseCount(data) {
return request({
url: '/api/place/choseCount',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
useType: getUseType(),
...data
}
});
}
// 批量生成台桌
export function $fastCreateTable(data) {
return request({
url: '/api/tbShopTable/generate',
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//打印当前台桌订单
export function $printOrder(data) {
return request({
url: '/api/place/printOrder',
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//打印当前台桌菜品
export function $printDishes(data) {
return request({
url: '/api/place/printDishes',
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 就餐模式切换
export function $changeUseType(data) {
return request({
url: '/api/place/choseModel',
method: "put",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 退款
export function $returnOrder(data) {
return request({
url: '/api/place/returnOrder',
method: "post",
data: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//获取用户可用优惠券
export function $findCoupon(data) {
return request({
url: marketUrl+'/admin/coupon/findCoupon',
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//会员积分列表
export function $returnMemberPointsList(data) {
return request({
url: '/api/points/member-points/page',
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 会员积分账户信息
export function $returnMemberPoints(memberId) {
return request({
url: '/api/points/member-points/' + memberId,
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
//002-获取订单可用积分及抵扣金额(支付页面使用)
export function $calcUsablePoints(data) {
return request({
url: '/api/points/member-points/calc-usable-points',
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}
// 003-根据积分计算可抵扣金额
export function $calcDeDuctionPoints(data) {
return request({
url: '/api/points/member-points/calc-deduction-amount',
method: "get",
params: {
shopId: uni.getStorageSync("shopId"),
...data
}
});
}

View File

@@ -1,234 +0,0 @@
/**
* HTTP的封装 基于uni.request
* 包括: 通用响应结果的处理 和 业务的增删改查函数
*
* @author terrfly
* @site https://www.jeequan.com
* @date 2021/12/16 18:35
*/
// 设置env配置文件
import envConfig from '@/env/config.js'
// 导入全局属性
import appConfig from '@/config/appConfig.js'
import storageManage from '@/commons/utils/storageManage.js'
import infoBox from "@/commons/utils/infoBox.js"
import go from '@/commons/utils/go.js';
import { reject } from 'lodash';
// 设置node环境
// envConfig.changeEnv(storageManage.env('production'))
// 测试服
// #ifdef H5
let baseUrl = '/api/'
// #endif
// #ifndef H5
// let baseUrl = 'https://tapi.cashier.sxczgkj.cn/'
//预发布
// let baseUrl = 'https://pre-cashieradmin.sxczgkj.cn'
//正式
// let baseUrl = 'https://cashier.sxczgkj.com/'
let baseUrl = appConfig.env.JEEPAY_BASE_URL
// #endif
const loadingShowTime = 200
function getHeader(){
const headerObject={}
headerObject["token"] = storageManage.token()
headerObject["shopId"] = uni.getStorageSync("shopInfo").id
headerObject["platformType"] = 'APP'
return headerObject
}
// 通用处理逻辑
function commonsProcess(showLoading, httpReqCallback) {
// 判断是否请求完成(用作 是否loading
// 包括: 'ing', 'ingLoading', 'finish'
let reqState = 'ing'
// 是否已经提示的错误信息
let isShowErrorToast = false
// 请求完成, 需要处理的动作
let reqFinishFunc = () => {
if (reqState == 'ingLoading') { // 关闭loading弹层
infoBox.hideLoading()
}
reqState = 'finish' // 请求完毕
}
// 明确显示loading
if (showLoading) {
// xx ms内响应完成不提示loading
setTimeout(() => {
if (reqState == 'ing') {
reqState = 'ingLoading'
infoBox.showLoading()
}
}, loadingShowTime)
}
return httpReqCallback().then((httpData) => {
reqFinishFunc(); // 请求完毕的动作
// 从http响应数据中解构响应数据 [ 响应码、 bodyData ]
let {
statusCode,
data
} = httpData
// 避免混淆重新命名
let bodyData = data
if (statusCode == 500) {
isShowErrorToast = true
return Promise.reject(bodyData) // 跳转到catch函数
}
if (statusCode == 501) {
// storageManage.token(null, true)
// 提示信息
isShowErrorToast = true
// infoBox.showErrorToast('请登录').then(() => {
// go.to("PAGES_LOGIN", {}, go.GO_TYPE_RELAUNCH)
// })
return Promise.reject(bodyData) // 跳转到catch函数
}
// http响应码不正确
if (statusCode != 200 && statusCode != 204 && statusCode != 201) {
isShowErrorToast = true
bodyData.msg=bodyData.msg=='Bad credentials'?'用户名或密码错误':bodyData.msg
infoBox.showToast(bodyData.msg || '服务器异常')
return Promise.reject(bodyData) // 跳转到catch函数
}
// // 业务响应异常
if (bodyData.hasOwnProperty('code') && bodyData.code != 200) {
isShowErrorToast = true
infoBox.showToast(bodyData.msg)
// if (bodyData.code == 5005) { // 密码已过期, 直接跳转到更改密码页面
// uni.reLaunch({
// url: '/pageUser/setting/updatePwd'
// })
// }
// if(bodyData.code == 500){ // 密码已过期, 直接跳转到更改密码页面
// uni.redirectTo({url: '/pages/login/index'})
// }
return Promise.reject(bodyData) // 跳转到catch函数
}
// 构造请求成功的响应数据
return Promise.resolve(bodyData.data)
}).catch(res => {
console.log(res)
if(res.code==501){
storageManage.token(null, true)
infoBox.showToast('登录过期,请重新登录').then(() => {
uni.redirectTo({url: '/pages/login/index'})
reject()
})
}
// if(res.status==400){
// storageManage.token(null, true)
// infoBox.showErrorToast('').then(() => {
// go.to("PAGES_LOGIN", {}, go.GO_TYPE_RELAUNCH)
// })
// }
if(res.code==500){
infoBox.showToast(res.msg||'服务器异常').then(() => {})
}
// if(res&&res.msg){
// infoBox.showErrorToast(res.msg)
// }
reqFinishFunc(); // 请求完毕的动作
// 如果没有提示错误, 那么此处提示 异常。
if (!isShowErrorToast) {
infoBox.showToast(`请求网络异常`)
}
return Promise.reject(res)
}).finally(() => { // finally 是 then结束后再执行, 此处不适用。 需要在请求完成后立马调用: reqFinishFunc()
});
}
// 默认 显示loading(控制 xxs 内 不提示loading )
function req(uri, data, method = "GET", showLoading = true, extParams = {}) {
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.request(
Object.assign({
url: baseUrl + uri,
data: data,
method: method,
header: getHeader()
}, extParams)
)
})
}
// 默认 显示loading(控制 xxs 内 不提示loading )
function request(args) {
const {
url,
data,
params,
method = "GET",
showLoading = true,
extParams = {}
} = args
let headerObject = {}
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.request(
Object.assign({
url: baseUrl + url,
data: params||data,
method: method,
header: getHeader()
}, extParams)
)
})
}
// 上传
function upload(uri, data, file, showLoading = true, extParams = {}) {
// 放置token
let headerObject = {}
// headerObject[appConfig.tokenKey] = storageManage.token()
return commonsProcess(showLoading, () => {
return uni.uploadFile(
Object.assign({
url: baseUrl + uri,
formData: data,
name: "file",
filePath: file.path||file.url,
header: getHeader()
}, extParams)
).then((httpData) => {
// uni.upload 返回bodyData 的是 string类型。 需要解析。
httpData.data = JSON.parse(httpData.data)
return Promise.resolve(httpData)
}).catch(err=>{
uni.hideLoading()
infoBox.showErrorToast(`上传失败`)
})
})
}
export default {
req: req,
request,
upload: upload
}

View File

@@ -1,19 +0,0 @@
import http from '@/http/yskApi/http.js'
const request = http.request
import {marketUrl} from './prveUrl.js'
/**
* 限时折扣
* @returns
*/
export function limitTimeDiscount(params) {
return request({
url: marketUrl+`/admin/limitTimeDiscount`,
method: 'get',
params: {
shopId: uni.getStorageSync('shopInfo').id,
...params
}
})
}

View File

@@ -1,20 +0,0 @@
import http from '@/http/yskApi/http.js'
const request = http.request
import {marketUrl} from '../prveUrl.js'
/**
* 新客立减
* @returns
*/
export function getDiscountByUserId(params) {
return request({
url: marketUrl+`/admin/consumeDiscount/getDiscountByUserId`,
method: 'get',
params: {
shopId: uni.getStorageSync('shopInfo').id,
...params
}
})
}

View File

@@ -1,21 +0,0 @@
import http from '@/http/yskApi/http.js'
const request = http.request
import {marketUrl} from '../prveUrl.js'
/**
* 满减活动
* @returns
*/
export function discountActivity(params) {
console.log(uni.getStorageSync('shopInfo').id)
return request({
url: marketUrl+`/admin/discountActivity`,
method: 'get',
params: {
shopId: uni.getStorageSync('shopInfo').id,
...params
}
})
}

View File

@@ -1,118 +0,0 @@
import http from '@/http/yskApi/http.js'
const request = http.request
import {orderUrl} from './prveUrl.js'
/**
* 查询订单
* @param {*} data
* @returns
*/
export function tbOrderInfoData(data) {
return request({
url: "/order/admin/order",
method: "get",
data: {
shopId: uni.getStorageSync('shopId'),
...data
}
});
}
/**
* 导出数据
* @param {*} data
* @returns
*/
export function tbOrderInfoDownload(data) {
return request({
url: "/api/tbOrderInfo/download",
method: "post",
data: {
shopId: uni.getStorageSync('shopId'),
...data
},
responseType: "blob"
});
}
export function createOrder(data, urlType = 'order') {
return request({
url: `/${urlType}/admin/order/createOrder`,
method: "POST",
data: {
...data
}
})
}
/**
* 通过Id查询订单
* @param {*} id
* @returns
*/
export function tbOrderInfoDetail(id) {
return request({
url: orderUrl+ `/admin/order/historyOrder?orderId=${id}`,
method: "get"
});
}
/**
* 通过Id查询订单
* @param {*} createdAt
* @returns
*/
export function payCount(createdAt) {
console.log(createdAt);
return request({
url: `/api/tbOrderInfo/payCount`,
method: "post",
data: {
shopId: uni.getStorageSync('shopId'),
createdAt: createdAt
}
});
}
/**
* 订单列表
* @param {*} createdAt
* @returns
*/
export function tbGroupOrderInfo(params) {
return request({
url: `/api/tbGroupOrderInfo`,
method: "post",
data: {
shopId: uni.getStorageSync('shopId'),
...params
}
});
}
/**
* 退单
* @param {*} data
* @returns
*/
export function returnGpOrder(data) {
return request({
url: `/api/tbGroupOrderInfo/returnGpOrder`,
method: "post",
data
});
}
/**
* 店铺订单支付获取链接
*/
export function $getOrderPayUrl(data) {
return request({
url: `/api/shopPayApi/getOrderPayUrl`,
method: "get",
data: {
shopId: uni.getStorageSync('shopId'),
...data
}
});
}

View File

@@ -1,3 +0,0 @@
export const marketUrl = '/market'
export const accountUrl = '/account'
export const orderUrl = '/order'

View File

@@ -1,59 +0,0 @@
// 用户管理
import http from './http.js'
import {accountUrl} from './prveUrl.js'
const request=http.request
// 获取店铺会员二维码
export function getwxacode(data) {
return request({
url: `/shop/storage/getwxacode`,
method: "post",
data
});
}
/**
* 商家用户列表
* @returns
*/
export function queryAllShopUser(params) {
return request({
url: accountUrl+`/admin/shopUser`,
method: "get",
params: {
shopId: uni.getStorageSync('shopId'),
...params
}
});
}
/**
* 查询商家用户概述信息
* @returns
*/
export function queryAllShopInfo(params) {
return request({
url: `/api/tbShopUser/summary`,
method: "get",
params: {
shopId: uni.getStorageSync('shopId'),
isVip:1,
...params
}
});
}
/**
* 获取店铺用户详情
* @returns
*/
export function shopUserDetail(params) {
return request({
url: accountUrl+`/admin/shopUser/detail`,
method: "get",
params: {
shopId: uni.getStorageSync('shopId'),
...params
}
});
}

View File

@@ -1,22 +0,0 @@
{
"compilerOptions": {
"target": "ES6",
"module": "ESNext",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"] // 适配 uni-app 的 @ 路径别名(关键,避免导入路径报错)
},
"jsx": "preserve",
"allowJs": true
},
"include": [
"src/**/*",
"pages.json",
"uni.pages.json" // 包含 uni-app 配置文件,让 Vetur 识别页面结构
],
"exclude": [
"node_modules",
"dist",
"unpackage" // 排除编译产物和依赖目录
]
}

View File

View File

@@ -1,852 +0,0 @@
import { BigNumber } from "bignumber.js";
import _ from "lodash";
import {
ShopInfo,
couponCalcParams,
BaseCartItem,
TimeLimitDiscountConfig,
CanDikouGoodsArrArgs,
Coupon,
ShopUserInfo,
GoodsType,
BackendCoupon,
ExchangeCalculationResult,
PointDeductionRule,
OrderCostSummary,
} from "./types";
import { getCompatibleFieldValue } from "./utils";
/**
* 返回商品单价
* @param goods 商品
* @param user 用户信息
* @param {Object} shopInfo
*/
export function returnGoodsPrice(
goods: BaseCartItem,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined
) {
if (!goods) {
return 0;
}
//是否可以使用会员价
const canUseVipPrice =
user &&
user.isVip &&
user.isMemberPrice &&
goods.memberPrice * 1 > 0 &&
shopInfo &&
shopInfo.isMemberPrice;
// 商家改价
if (goods.discount_sale_amount && goods.discount_sale_amount * 1 > 0) {
return goods.salePrice;
}
// 限时折扣
if (limitTimeDiscount && limitTimeDiscount.id) {
//优先使用
// 兼容 isTimeDiscount/is_time_discount这里顺便处理该字段的命名兼容
const isTimeDiscount = getCompatibleFieldValue(
goods,
"isTimeDiscount",
"is_time_discount"
);
if (isTimeDiscount) {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
const canUseFoods = limitTimeDiscount.foods.split(",");
const canUseLimit =
limitTimeDiscount.foodType == 1 ||
canUseFoods.includes(`${goods.productId}`);
if (canUseLimit && limitTimeDiscount.discountPriority == "limit-time") {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
if (canUseLimit && limitTimeDiscount.discountPriority == "vip-price") {
if (canUseVipPrice) {
return goods.memberPrice;
} else {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
}
}
if (canUseVipPrice) {
return goods.memberPrice;
}
return goods.salePrice;
}
/**
* 返回商品分组
* @param arr 商品列表
*/
export function returnGoodsGroupMap(arr: BaseCartItem[]) {
let map: { [key: string]: BaseCartItem[] } = {};
arr.forEach((v) => {
const key = v.productId + "_" + v.skuId;
if (!map[key]) {
map[key] = [];
}
map[key].push(v);
});
return map;
}
interface CouponTypes {
1: "满减券";
2: "商品券";
3: "折扣券";
4: "第二件半价券";
5: "消费送券";
6: "买一送一券";
7: "固定价格券";
8: "免配送费券";
}
/**
* 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
* @param coupon
*/
export function returnCoupType(coupon: Coupon) {
const couponTypes: CouponTypes = {
1: "满减券",
2: "商品券",
3: "折扣券",
4: "第二件半价券",
5: "消费送券",
6: "买一送一券",
7: "固定价格券",
8: "免配送费券",
};
return couponTypes[coupon.type as keyof CouponTypes] || "未知类型";
}
/**
* 返回商品券抵扣后的商品列表
* @param canDikouGoodsArr 可抵扣商品列表
* @param selCoupon 已选择的优惠券列表
* @param user 用户信息
*/
export function returnCanDikouGoodsArr(args: CanDikouGoodsArrArgs) {
const { canDikouGoodsArr, selCoupon, user, shopInfo, limitTimeDiscount } =
args;
const types = [2, 4, 6];
// 收集已抵扣商品并关联对应的优惠券类型
const goodsCouponGoods = selCoupon
.filter((v) => types.includes(v.type))
.reduce((prev: BaseCartItem[], cur) => {
// 给每个抵扣商品添加所属优惠券类型
if (cur && cur.discount) {
const goodsWithType = cur.discount.hasDiscountGoodsArr.map((goods) => ({
...goods,
couponType: cur.type, // 记录该商品是被哪种类型的优惠券抵扣的
}));
prev.push(...goodsWithType);
}
return prev;
}, []);
const arr = _.cloneDeep(canDikouGoodsArr)
.map((v) => {
const findCart = goodsCouponGoods.find((carts) => carts.id == v.id);
if (findCart) {
// 根据优惠券类型判断扣减数量
if ([4, 6].includes(findCart.couponType ?? 0)) {
// 类型4第二件半价或6买一送一数量减2
if (v.num) {
v.num -= 2;
}
} else {
// 其他类型如类型2商品券按原逻辑扣减对应数量
if (v.num) {
v.num -= findCart.num ?? 0;
}
}
}
return v;
})
.filter((v) => {
const canUseNum = (v.num ?? 0) - (v.returnNum || 0);
// 兼容 is_temporary/isTemporary 和 is_gift/isGift
const isTemporary = getCompatibleFieldValue(
v,
"isTemporary",
"is_temporary"
);
const isGift = getCompatibleFieldValue(v, "isGift", "is_gift");
if (canUseNum <= 0 || isTemporary || isGift) {
return false;
}
return true;
}); // 过滤掉数量<=0的商品,赠菜,临时菜
return arr;
}
/**
* 返回商品是否享用了会员价/会员折扣
* @param {*} goods
*/
function returnGoodsIsUseVipPrice(
shopInfo: ShopInfo,
user: ShopUserInfo,
goods: BaseCartItem
) {
// 兼容 isTimeDiscount/is_time_discount
const isTimeDiscount = getCompatibleFieldValue(
goods,
"isTimeDiscount",
"is_time_discount"
);
if (isTimeDiscount) {
return false;
}
if (shopInfo.isMemberPrice != 1 || user.isVip != 1) {
return false;
}
if (shopInfo.isMemberPrice == 1 && user.isVip == 1) {
if (goods.memberPrice <= 0) {
return false;
} else {
return true;
}
}
return false;
}
/**
* 返回可以计算抵扣金额的商品列表
*/
function returnCanCalcGoodsList(
canCalcGoodsArr: BaseCartItem[],
coupon: Coupon,
shopInfo: ShopInfo,
user: ShopUserInfo
) {
return canCalcGoodsArr.filter((goods) => {
// 兼容 isTimeDiscount/is_time_discount
const isTimeDiscount = getCompatibleFieldValue(
goods,
"isTimeDiscount",
"is_time_discount"
);
if (!coupon.discountShare && isTimeDiscount) {
return false;
}
if (
!coupon.vipPriceShare &&
returnGoodsIsUseVipPrice(shopInfo, user, goods)
) {
return false;
}
return true;
});
}
/**
* 判断优惠券是否可使用,并返回不可用原因
*
* @param {Object} args - 函数参数集合
* @param {Array} args.canDikouGoodsArr - 可参与抵扣的商品列表
* @param {Object} args.coupon - 优惠券信息对象
* @param {boolean} args.coupon.use - 优惠券是否启用
* @param {Array} args.coupon.useFoods - 优惠券适用的商品ID列表
* @param {number} args.coupon.fullAmount - 优惠券使用门槛金额
* @param {number} args.coupon.type - 优惠券类型
* @param {number} args.goodsOrderPrice - 订单中所有商品的总金额
* @param {Object} args.user - 用户信息对象
* @param {Object} args.selCoupon - 已经选择的优惠券信息对象
* @param {Object} args.shopInfo
* @param {boolean} args.limitTimeDiscount - 限时折扣
* @returns {Object} - { canUse: boolean, reason: string } 可用状态及不可用原因
*/
export function returnCouponCanUse(args: couponCalcParams) {
let {
canDikouGoodsArr,
coupon,
goodsOrderPrice,
user,
selCoupon,
shopInfo,
isMemberPrice,
limitTimeDiscount,
} = args;
// 优惠券未启用
if (!coupon.use) {
return {
canUse: false,
reason: coupon.noUseRestrictions || "不在可用时间段内",
};
}
if (
limitTimeDiscount &&
limitTimeDiscount.id &&
limitTimeDiscount.foodType == 1 &&
!coupon.discountShare
) {
return {
canUse: false,
reason: coupon.noUseRestrictions || "不可与限时折扣同享",
};
}
// 计算门槛金额
let fullAmount = goodsOrderPrice;
canDikouGoodsArr = returnCanDikouGoodsArr(args);
//优惠券指定门槛商品列表
let canCalcGoodsArr = [...canDikouGoodsArr];
//部分商品参与门槛计算
if (coupon.thresholdFoods.length) {
canCalcGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.thresholdFoods.find((food) => food.id == v.productId);
});
}
canCalcGoodsArr = returnCanCalcGoodsList(
canCalcGoodsArr,
coupon,
shopInfo,
user
);
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return (
pre +
returnGoodsPrice(cur, user, shopInfo, limitTimeDiscount) * (cur.num || 0)
);
}, 0);
// 是否全部商品可用
const isDikouAll = coupon.useFoods.length === 0;
// 订单可用商品列表
let canUseGoodsArr: BaseCartItem[] = [];
if (!isDikouAll) {
canUseGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.useFoods.find((food) => food.id == v.productId);
});
}
// if (user.isVip && !coupon.vipPriceShare) {
// return {
// canUse: false,
// reason: "非会员可用",
// };
// }
if (selCoupon.length > 0 && !selCoupon[0].otherCouponShare) {
return {
canUse: false,
reason: "当前选中的券不可与其他券同享",
};
}
if (selCoupon.length > 0 && !coupon.otherCouponShare) {
return {
canUse: false,
reason: "当前选中的券不可与其他券同享",
};
}
// 满减券和折扣券计算门槛金额是否满足
if ([1, 3].includes(coupon.type)) {
if (canCalcGoodsArr.length <= 0) {
return {
canUse: false,
reason: "没有可参与计算门槛的商品",
};
}
// 不满足门槛金额
if (fullAmount < (coupon.fullAmount || 0)) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
}
// 商品兑换券,第二件半价和买一送一判断是否有可用商品
if ([2, 4, 5].includes(coupon.type)) {
// 没有符合条件的商品
if (isDikouAll && canDikouGoodsArr.length === 0) {
return {
canUse: false,
reason: "没有符合条件的商品",
};
}
if (!isDikouAll && canUseGoodsArr.length === 0) {
return {
canUse: false,
reason: "没有符合条件的商品",
};
}
if (coupon.type == 2) {
if (canCalcGoodsArr.length <= 0) {
return {
canUse: false,
reason: "没有符合计算门槛条件的商品",
};
}
if (fullAmount < (coupon.fullAmount || 0)) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
}
}
//商品兑换券是否达到门槛金额
if (coupon.type == 2 && goodsOrderPrice < (coupon.fullAmount || 0)) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
// 买一送一券特殊验证
if (coupon.type === 6) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => (v.num || 0) >= 2);
} else if (canUseGoodsArr.length > 0) {
canUse = canUseGoodsArr.some((v) => (v.num || 0) >= 2);
}
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
}
// 第二件半价券特殊验证
if (coupon.type === 4) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => (v.num || 0) >= 2);
} else if (canUseGoodsArr.length > 0) {
canUse = canUseGoodsArr.some((v) => (v.num || 0) >= 2);
}
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
}
// 所有条件都满足
return {
canUse: true,
reason: "",
};
}
/**
* 计算抵扣商品金额
* @param discountGoodsArr 可抵扣商品列表
* @param discountNum 抵扣数量
* @param user 用户信息
* @param {Object} shopInfo 店铺信息
*/
export function calcDiscountGoodsArrPrice(
discountGoodsArr: BaseCartItem[],
discountNum: number,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
let hasCountNum = 0;
let discountPrice = 0;
let hasDiscountGoodsArr:BaseCartItem[] = [];
for (let i = 0; i < discountGoodsArr.length; i++) {
if (hasCountNum >= discountNum) {
break;
}
const goods = discountGoodsArr[i];
const shengyuNum = discountNum - hasCountNum;
const num = Math.min(goods.num || 0, shengyuNum);
const realPrice = returnGoodsPrice(
goods,
user,
shopInfo,
limitTimeDiscount
);
discountPrice += realPrice * num;
hasCountNum += num;
if(goods){
hasDiscountGoodsArr.push({
...goods,
num,
});
}
}
return {
discountPrice,
hasDiscountGoodsArr,
};
}
/**
* 计算优惠券抵扣金额
* @param arr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCouponDiscount(
arr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
goodsOrderPrice: number,
selCoupon: Coupon[],
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
arr = returnCanDikouGoods(arr, user, shopInfo, limitTimeDiscount);
const canDikouGoodsArr = returnCanDikouGoodsArr({
canDikouGoodsArr: arr,
selCoupon,
user,
shopInfo,
limitTimeDiscount,
});
if (coupon.type == 2) {
return returnCouponProductDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
}
if (coupon.type == 6) {
const result = returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
return result;
}
if (coupon.type == 4) {
return returnSecoendDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
}
if (coupon.type == 3) {
return returnCouponZhekouDiscount(
canDikouGoodsArr,
coupon,
user,
goodsOrderPrice,
selCoupon,
limitTimeDiscount
);
}
}
/**
* 折扣券抵扣金额
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
* @param limitTimeDiscount 限时折扣
*/
export function returnCouponZhekouDiscount(
canDikouGoodsArr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
goodsOrderPrice: number,
selCoupon: Coupon[],
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
let { discountRate, maxDiscountAmount } = coupon;
maxDiscountAmount = maxDiscountAmount || 0;
// 计算商品优惠券折扣总和使用BigNumber避免精度问题
const goodsCouponDiscount = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
return new BigNumber(prve).plus(
new BigNumber(cur?.discount?.discountPrice || 0)
);
}, new BigNumber(0));
// 将商品订单价格转换为BigNumber并减去优惠券折扣
const adjustedGoodsOrderPrice = new BigNumber(goodsOrderPrice).minus(
goodsCouponDiscount
);
// 计算优惠比例:(100 - 折扣率) / 100
const discountAmountRatio = new BigNumber(100)
.minus(discountRate || 0)
.dividedBy(100);
// 计算折扣金额:调整后的商品订单金额 × 优惠比例
let discountPrice = adjustedGoodsOrderPrice
.times(discountAmountRatio)
.decimalPlaces(2, BigNumber.ROUND_FLOOR)
.toNumber();
// 应用最大折扣金额限制
if (maxDiscountAmount !== 0) {
discountPrice =
discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice;
}
return {
discountPrice, // 折扣抵扣金额(即优惠的金额)
hasDiscountGoodsArr: [],
};
}
/**
* 商品券抵扣金额
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
*/
export function returnCouponProductDiscount(
canDikouGoodsArr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
let { useFoods, discountNum, useRule } = coupon;
discountNum = discountNum || 0;
//抵扣商品数组
let discountGoodsArr:BaseCartItem[] = [];
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoodsArr = canDikouGoodsArr.slice(discountNum * -1).reverse();
} else {
discountGoodsArr = canDikouGoodsArr.slice(0, discountNum);
}
} else {
//抵扣选中商品
const discountSelGoodsArr = canDikouGoodsArr.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoodsArr = discountSelGoodsArr.slice(discountNum * -1).reverse();
} else {
discountGoodsArr = discountSelGoodsArr.slice(0, discountNum);
}
}
const result = calcDiscountGoodsArrPrice(
discountGoodsArr,
discountNum,
user,
shopInfo,
limitTimeDiscount
);
return result;
}
/**
* 返回买一送一券抵扣详情
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
*/
function returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods:BaseCartItem | undefined = undefined;
//符合买一送一条件的商品(数量>=2 + 非临时/非赠品)
const canUseGoods = canDikouGoodsArr.filter((v) => {
const isTemporary = getCompatibleFieldValue(
v,
"isTemporary",
"is_temporary"
);
const isGift = getCompatibleFieldValue(v, "isGift", "is_gift");
return (v.num || 0) >= 2 && !isTemporary && !isGift;
});
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoods = canUseGoods[canUseGoods.length - 1];
} else {
discountGoods = canUseGoods[0];
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1[0];
}
}
let discountPrice = 0;
let hasDiscountGoodsArr: BaseCartItem[] = [];
if (discountGoods) {
discountPrice = returnGoodsPrice(
discountGoods,
user,
shopInfo,
limitTimeDiscount
);
hasDiscountGoodsArr = [discountGoods];
}
return {
discountPrice: discountPrice <= 0 ? 0 : discountPrice,
hasDiscountGoodsArr,
};
}
/**
* 返回第二件半价券抵扣详情
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
*/
function returnSecoendDiscount(
canDikouGoodsArr: BaseCartItem[],
coupon: Coupon,
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods:BaseCartItem | undefined = undefined;
//符合条件的商品(数量>=2 + 非临时/非赠品)
const canUseGoods = canDikouGoodsArr.filter((v) => {
const isTemporary = getCompatibleFieldValue(
v,
"isTemporary",
"is_temporary"
);
const isGift = getCompatibleFieldValue(v, "isGift", "is_gift");
return (v.num || 0) >= 2 && !isTemporary && !isGift;
});
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoods = canUseGoods[canUseGoods.length - 1];
} else {
discountGoods = canUseGoods[0];
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1[0];
}
}
let discountPrice = 0;
let hasDiscountGoodsArr: BaseCartItem[] = [];
if (discountGoods) {
discountPrice = returnGoodsPrice(
discountGoods,
user,
shopInfo,
limitTimeDiscount
);
hasDiscountGoodsArr = [discountGoods];
}
//返回半价价格
return {
discountPrice:
discountPrice <= 0
? 0
: new BigNumber(discountPrice).dividedBy(2).toNumber(),
hasDiscountGoodsArr,
};
}
/**
* 返回可以抵扣优惠券的商品列表,过滤掉赠品、临时商品,价格从高到低排序
* @param arr 商品列表
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCanDikouGoods(
arr: BaseCartItem[],
user: ShopUserInfo,
shopInfo: ShopInfo,
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined
) {
const result = arr
.filter((v) => {
// 兼容 is_temporary/isTemporary 和 is_gift/isGift
const isTemporary = getCompatibleFieldValue(
v,
"isTemporary",
"is_temporary"
);
const isGift = getCompatibleFieldValue(v, "isGift", "is_gift");
return !isTemporary && !isGift;
})
.filter((v) => {
return (v.num || 0) > 0;
})
.sort((a, b) => {
return (
returnGoodsPrice(b, user, shopInfo, limitTimeDiscount) -
returnGoodsPrice(a, user, shopInfo, limitTimeDiscount)
);
});
return result;
}
export const utils = {
returnGoodsPrice,
returnGoodsGroupMap,
returnCoupType,
returnCanDikouGoods,
returnCanDikouGoodsArr,
returnCouponCanUse,
calcDiscountGoodsArrPrice,
returnCouponDiscount,
returnCouponProductDiscount,
returnCouponZhekouDiscount,
};
export default utils;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +0,0 @@
export * from "./types";
import OrderPriceCalculator from "./goods";
import couponUtils from "./coupon";
import limitUtils from "./limit";
export { OrderPriceCalculator, couponUtils, limitUtils };
export default {
OrderPriceCalculator,
couponUtils,
limitUtils,
};

View File

@@ -1,216 +0,0 @@
import BigNumber from "bignumber.js";
import _ from "lodash";
import {
BaseCartItem,
ShopUserInfo,
ShopInfo,
TimeLimitDiscountConfig,
CanReturnMemberPriceArgs,
returnPriceArgs,
} from "./types";
/**
* 判断商品是否可以使用限时折扣
* @param goods 商品对象
* @param limitTimeDiscountRes 限时折扣配置
* @param shopInfo 店铺信息
* @param shopUserInfo 店铺用户信息
* @param idKey 商品ID键名默认"id"
* @returns
*/
export function canUseLimitTimeDiscount(
goods: BaseCartItem,
limitTimeDiscountRes: TimeLimitDiscountConfig | null | undefined,
shopInfo: ShopInfo,
shopUserInfo: ShopUserInfo,
idKey = "id" as keyof BaseCartItem
) {
shopInfo = shopInfo || {};
shopUserInfo = shopUserInfo || {};
if(shopInfo.isMemberPrice){
shopUserInfo.isMemberPrice=1
}
if (!limitTimeDiscountRes || !limitTimeDiscountRes.id) {
return false;
}
const canUseFoods = (limitTimeDiscountRes.foods || "").split(",");
const goodsCanUse =
limitTimeDiscountRes.foodType == 1 ||
canUseFoods.includes(`${goods[idKey]}`);
if (!goodsCanUse) {
return false;
}
if (limitTimeDiscountRes.discountPriority == "limit-time") {
return true;
}
if (limitTimeDiscountRes.discountPriority == "vip-price") {
if (
shopUserInfo.isVip == 1 &&
shopUserInfo.isMemberPrice == 1 &&
goods.memberPrice * 1 > 0
) {
return false;
}
return true;
}
return false;
}
/**
* 返回商品显示价格
* @params {*} args 参数对象
* @params {*} args.goods 商品对象
* @params {*} args.shopInfo 店铺信息
* @params {*} args.limitTimeDiscountRes 限时折扣信息
* @params {*} args.shopUserInfo 店铺用户信息
* @returns
*/
export function returnPrice(args: returnPriceArgs) {
let {
goods,
shopInfo,
limitTimeDiscountRes,
shopUserInfo,
idKey = "product_id",
} = args;
limitTimeDiscountRes = limitTimeDiscountRes || {
foods: "",
foodType: 2,
discountPriority: "",
discountRate: 0,
id: 0,
shopId: 0,
useType: "",
};
const canUseFoods = (limitTimeDiscountRes.foods || "").split(",");
const includesGoods =
limitTimeDiscountRes.foodType == 1 ||
canUseFoods.includes("" + goods[idKey]);
shopInfo = shopInfo || {};
shopUserInfo = shopUserInfo || {};
if (
shopUserInfo.isMemberPrice == 1 &&
shopUserInfo.isVip == 1 &&
shopInfo.isMemberPrice == 1
) {
const memberPrice = goods.memberPrice || goods.salePrice;
//是会员而且启用会员价
if (limitTimeDiscountRes) {
//使用限时折扣
//限时折扣优先
if (limitTimeDiscountRes.discountPriority == "limit-time") {
if (includesGoods) {
return returnLimitPrice({
price: goods.salePrice,
limitTimeDiscountRes,
});
} else {
return memberPrice;
}
}
if (
limitTimeDiscountRes.discountPriority == "vip-price" &&
includesGoods
) {
if (goods.memberPrice * 1 > 0) {
//会员优先
return memberPrice;
} else {
const price = returnLimitPrice({
price: goods.salePrice,
limitTimeDiscountRes,
goods: goods,
});
return price;
}
} else {
return memberPrice;
}
} else {
//是会员没有限时折扣
return memberPrice;
}
} else {
//不是会员或者没有启用会员价
if (limitTimeDiscountRes && limitTimeDiscountRes.id && includesGoods) {
const price = returnLimitPrice({
price: goods.salePrice,
limitTimeDiscountRes,
goods: goods,
});
return price;
} else {
return goods.salePrice;
}
}
}
interface returnLimitPriceArgs {
limitTimeDiscountRes: TimeLimitDiscountConfig | null | undefined;
price: number;
goods?: BaseCartItem;
}
/**
* 返回限时折扣价格
* @params {*} args 参数对象
* @params {*} args.limitTimeDiscountRes 限时折扣信息
* @params {*} args.price 商品价格
* @param {*} args.goods 商品对象
* @returns
*/
export function returnLimitPrice(args: returnLimitPriceArgs) {
const { limitTimeDiscountRes, price, goods } = args;
const discountRate = new BigNumber(
limitTimeDiscountRes ? limitTimeDiscountRes.discountRate : 100
).dividedBy(100);
const result = BigNumber(price)
.times(discountRate)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
return result;
}
/**
* 判断是否返回会员价
* @param {*} args 参数对象
* @param {*} args.shopInfo 店铺信息
* @param {*} args.shopUserInfo 店铺用户信息
* @returns
*/
export function canReturnMemberPrice(args: CanReturnMemberPriceArgs) {
const { shopInfo, shopUserInfo } = args;
if (shopUserInfo.isMemberPrice == 1 && shopUserInfo.isVip == 1) {
return true;
} else {
return false;
}
}
/**
* 返回会员价格
* @param {*} goods
* @returns
*/
export function returnMemberPrice(goods: BaseCartItem) {
return goods.memberPrice || goods.salePrice;
}
export const utils = {
returnPrice,
canUseLimitTimeDiscount,
returnLimitPrice,
canReturnMemberPrice,
returnMemberPrice,
};
export default utils;

View File

View File

@@ -1,430 +0,0 @@
/** 商品类型枚举 */
export enum GoodsType {
NORMAL = "normal", // 普通商品
WEIGHT = "weight", // 称重商品
GIFT = "gift", // 赠菜(继承普通商品逻辑,标记用)
EMPTY = "", // 空字符串类型(后端未返回时默认归类为普通商品)
PACKAGE = "package", // 打包商品(如套餐/预打包商品,按普通商品逻辑处理,可扩展特殊规则)
}
/** 优惠券计算结果类型(新增细分字段) */
export interface CouponResult {
deductionAmount: number; // 抵扣金额
excludedProductIds: string[]; // 不适用商品ID列表注意是商品ID非购物车ID
usedCoupon: Coupon | undefined; // 实际使用的优惠券
productCouponDeduction: number; // 新增:商品优惠券抵扣(兑换券等)
fullCouponDeduction: number; // 新增:满减优惠券抵扣
}
/** 兑换券计算结果类型(新增细分字段) */
export interface ExchangeCalculationResult {
deductionAmount: number;
excludedProductIds: string[]; // 不适用商品ID列表商品ID
productCouponDeduction: number; // 新增:兑换券属于商品券,同步记录
}
export interface CouponTypes {
1: "满减券";
2: "商品券";
3: "折扣券";
4: "第二件半价券";
5: "消费送券";
6: "买一送一券";
7: "固定价格券";
8: "免配送费券";
}
/** 优惠券类型枚举 */
export enum CouponType {
FULL_REDUCTION = "full_reduction", // 满减券
DISCOUNT = "discount", // 折扣券
SECOND_HALF = "second_half", // 第二件半价券
BUY_ONE_GET_ONE = "buy_one_get_one", // 买一送一券
EXCHANGE = "exchange", // 商品兑换券
}
/** 后端返回的优惠券原始字段类型 */
export interface BackendCoupon {
id?: number; // 自增主键int64
shopId?: number; // 店铺IDint64
syncId?: number; // 同步Idint64
type?: number; // 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
name?: string; // 券名称
useShopType?: string; // 可用门店类型only-仅本店all-所有门店custom-指定门店
useShops?: string; // 可用门店(逗号分隔字符串,如"1,2,3"
useType?: string; // 可使用类型dine堂食/pickup自取/deliv配送/express快递
validType?: string; // 有效期类型fixed固定时间custom自定义时间
validDays?: number; // 有效期(天)
validStartTime?: string; // 有效期开始时间(如"2024-01-01 00:00:00"
validEndTime?: string; // 有效期结束时间
daysToTakeEffect?: number; // 隔天生效
useDays?: string; // 可用周期(如"周一,周二"
useTimeType?: string; // 可用时间段类型all-全时段custom-指定时段
useStartTime?: string; // 可用开始时间(每日)
useEndTime?: string; // 可用结束时间(每日)
getType?: string; // 发放设置:不可自行领取/no可领取/yes
getMode?: string; // 用户领取方式
giveNum?: number; // 总发放数量,-10086为不限量
getUserType?: string; // 可领取用户:全部/all新用户一次/new仅会员/vip
getLimit?: number; // 每人领取限量,-10086为不限量
useLimit?: number; // 每人每日使用限量,-10086为不限量
discountShare?: number; // 与限时折扣同享0-否1-是
vipPriceShare?: number; // 与会员价同享0-否1-是
ruleDetails?: string; // 附加规则说明
status?: number; // 状态0-禁用1-启用
useNum?: number; // 已使用数量
leftNum?: number; // 剩余数量
foods?: string; // 指定门槛商品(逗号分隔字符串,如"101,102"此处为商品ID
fullAmount?: number; // 使用门槛:满多少金额(元)
discountAmount?: number; // 使用门槛:减多少金额(元)
discountRate?: number; // 折扣%如90=9折
maxDiscountAmount?: number; // 可抵扣最大金额(元)
useRule?: string; // 使用规则price_asc-价格低到高price_desc-高到低
discountNum?: number; // 抵扣数量
otherCouponShare?: number; // 与其它优惠共享0-否1-是
createTime?: string; // 创建时间
updateTime?: string; // 更新时间
}
/** 营销活动类型枚举 */
export enum ActivityType {
TIME_LIMIT_DISCOUNT = "time_limit_discount", // 限时折扣
}
/** 基础购物车商品项核心修正新增product_id明确各ID含义 */
export interface BaseCartItem {
id: string | number; // 购物车ID唯一标识购物车中的条目如购物车项主键
product_id: string | number; // 商品ID唯一标识商品用于优惠券/活动匹配,必选)
productId?: string | number; // 商品ID
salePrice: number; // 商品原价(元)
number: number; // 商品数量
num?: number; // 商品数量
isTimeDiscount?: boolean; // 是否限时折扣商品默认false
is_time_discount?: boolean; // 是否限时折扣商品默认false
product_type: GoodsType; // 商品类型
is_temporary?: boolean; // 是否临时菜默认false
isTemporary?: boolean; // 是否临时菜默认false
is_gift?: boolean; // 是否赠菜默认false
isGift?: boolean; // 是否赠菜默认false
returnNum?: number; // 退货数量历史订单用默认0
memberPrice: number; // 商品会员价(元,优先级:商品会员价 > 会员折扣)
discountSaleAmount?: number; // 商家改价后单价(元,优先级最高)
discount_sale_amount?: number; // 商家改价后单价(元,优先级最高)
packFee?: number; // 单份打包费默认0
packNumber?: number; // 堂食打包数量默认0
activityInfo?: {
// 商品参与的营销活动(如限时折扣)
type: ActivityType;
discountRate: number; // 折扣率如0.8=8折
vipPriceShare: boolean; // 是否与会员优惠同享默认false
};
skuData?: {
// SKU扩展数据可选
id: string | number; // SKU ID唯一标识商品规格如颜色/尺寸)
memberPrice: number; // SKU会员价
salePrice?: number; // SKU原价
};
skuId?: string | number; // SKU ID唯一标识商品规格如颜色/尺寸)
couponType?: number; // 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
}
export interface CouponFoods {
id: string;
name: string;
images: string;
}
/** 基础优惠券接口(所有券类型继承,包含统一门槛商品字段) */
export interface BaseCoupon {
otherCouponShare?: number; // 与其它优惠共享0-否1-是
id: string | number; // 优惠券ID
type: number; // 工具库字符串枚举由后端couponType转换
name: string; // 对应后端title
available: boolean; // 基于BackendCoupon字段计算的可用性
useShopType?: string; // only-仅本店all-所有门店custom-指定门店
useShops: string[]; // 可用门店ID列表
discountShare: boolean; // 与限时折扣同享0-否1-是(后端字段转换为布尔值)
vipPriceShare: boolean; // 与会员价同享0-否1-是(后端字段转换为布尔值)
useType?: string[]; // 可使用类型dine堂食/pickup自取/deliv配送/express快递
isValid: boolean; // 是否在有效期内
discountAmount?: number; // 减免金额 (满减券有)
fullAmount?: number; // 使用门槛:满多少金额
maxDiscountAmount?: number; // 可抵扣最大金额 元
use: boolean;
discountNum?: number; // 抵扣数量
useRule?: string; // 使用规则price_asc-价格低到高price_desc-高到低
discountRate?: number; // 折扣%如90=9折
noUseRestrictions?: boolean; // 是不可用原因
thresholdFoods: CouponFoods[]; // 门槛商品ID列表空数组=全部商品,非空=指定商品ID
useFoods: CouponFoods[]; // 可用商品ID列表空数组=全部商品,非空=指定商品ID
}
export interface couponDiscount {
discountPrice: number;
hasDiscountGoodsArr: BaseCartItem[];
}
/** 满减券(适配后端字段) */
export interface FullReductionCoupon extends BaseCoupon {
fullAmount: number; // 对应后端fullAmount满减门槛
discountAmount: number; // 对应后端discountAmount减免金额
maxDiscountAmount?: number; // 对应后端maxDiscountAmount最大减免
discount?: couponDiscount;
}
/** 折扣券(适配后端字段) */
export interface DiscountCoupon extends BaseCoupon {
discountRate: number; // 后端discountRate%转小数如90→0.9
maxDiscountAmount: number; // 对应后端maxDiscountAmount最大减免
discount?: couponDiscount;
}
/** 第二件半价券(适配后端字段) */
export interface SecondHalfPriceCoupon extends BaseCoupon {
maxUseCountPerOrder?: number; // 对应后端useLimit-10086=不限)
discount?: couponDiscount;
}
/** 买一送一券(适配后端字段) */
export interface BuyOneGetOneCoupon extends BaseCoupon {
maxUseCountPerOrder?: number; // 对应后端useLimit-10086=不限)
discount?: couponDiscount;
}
/** 商品兑换券(适配后端字段) */
export interface ExchangeCoupon extends BaseCoupon {
deductCount: number; // 对应后端discountNum抵扣数量
sortRule: "low_price_first" | "high_price_first"; // 后端useRule转换
discount?: couponDiscount;
}
/** 所有优惠券类型联合 */
export type Coupon =
| FullReductionCoupon
| DiscountCoupon
| SecondHalfPriceCoupon
| BuyOneGetOneCoupon
| ExchangeCoupon;
/** 营销活动配置如限时折扣applicableProductIds为商品ID列表 */
export interface ActivityConfig {
type: ActivityType;
applicableProductIds?: string[]; // 适用商品ID列表与BaseCartItem.product_id匹配
discountRate: number; // 折扣率如0.8=8折
vipPriceShare: boolean; // 是否与会员优惠同享
}
/** 积分抵扣规则 */
export interface PointDeductionRule {
pointsPerYuan: number; // X积分=1元如100=100积分抵1元
maxDeductionAmount?: number; // 最大抵扣金额(元,默认不限)
}
/** 餐位费配置 */
export interface SeatFeeConfig {
pricePerPerson: number; // 每人餐位费(元)
personCount: number; // 用餐人数默认1
isEnabled: boolean; // 是否启用餐位费默认false
}
/** 商家减免类型枚举 */
export enum MerchantReductionType {
FIXED_AMOUNT = "fixed_amount", // 固定金额减免(如直接减 10 元)
DISCOUNT_RATE = "discount_rate", // 比例折扣减免(如打 9 折,即减免 10%
}
/** 商家减免配置(新增,替代原单一金额字段) */
export interface MerchantReductionConfig {
type: MerchantReductionType; // 减免类型(二选一)
fixedAmount?: number; // 固定减免金额(元,仅 FIXED_AMOUNT 生效≥0
discountRate?: number; // 折扣率(%,仅 DISCOUNT_RATE 生效0-100如 90 代表 9 折)
}
/**商家霸王餐配置 */
export interface FreeDineConfig {
enable: boolean; //是否开启
rechargeThreshold: number; //订单满多少元可以使用
rechargeTimes: number; //充值多少倍免单
withCoupon: boolean; //与优惠券同享
withPoints: boolean; //与积分同享
useType?: string[]; //使用类型 dine-in店内 takeout 自取 post快递takeaway外卖
useShopType?: string; //all 全部 part部分
shopIdList?: number[]; //可用门店id
}
//限时折扣配置
export interface TimeLimitDiscountConfig {
/**
* 折扣优先级 limit-time/vip-price
*/
discountPriority: string;
/**
* 折扣% 范围1-99
*/
discountRate: number;
/**
* 参与商品
*/
foods: string;
/**
* 参与商品 1全部 2部分
*/
foodType: number;
/**
* 自增主键
*/
id: number;
/**
* 店铺ID
*/
shopId: number;
/**
* 可使用类型:堂食 dine-in 外带 take-out 外卖 take-away 配送 post
*/
useType: string;
[property: string]: any;
}
//用户信息
export interface ShopUserInfo {
isVip: number | null; //是否会员
discount: number | null; //用户折扣
isMemberPrice: number | null; //会员折扣与会员价是否同时使用
id?: number; //用户ID
}
/** 订单额外费用配置 */
export interface OrderExtraConfig {
// merchantReduction: number; // 商家减免金额默认0
// 替换原单一金额字段,支持两种减免形式
merchantReduction: MerchantReductionConfig;
additionalFee: number; // 附加费如余额充值、券包默认0
pointDeductionRule: PointDeductionRule; // 积分抵扣规则
seatFeeConfig: SeatFeeConfig; // 餐位费配置
currentStoreId: string; // 当前门店ID用于验证优惠券适用门店
userPoints: number; // 用户当前积分(用于积分抵扣)
isMember: boolean; // 用户是否会员(用于会员优惠)
memberDiscountRate?: number; // 会员折扣率如0.95=95折无会员价时用
newUserDiscount?: number; // 新用户减免金额默认0
fullReductionActivities: FullReductionActivity[]; // 当前店铺的满减活动列表(后端返回结构)
currentDinnerType: "dine-in" | "take-out" | "take-away" | "post"; // 当前就餐类型匹配useType
isFreeDine?: boolean; //是否霸王餐
freeDineConfig?: FreeDineConfig;
limitTimeDiscount?: TimeLimitDiscountConfig; //限时折扣
shopUserInfo: ShopUserInfo; // 用户信息
}
/** 订单费用汇总(修改:补充商家减免类型和明细) */
export interface OrderCostSummary {
goodsList: BaseCartItem[];
// 商品总件数
goodsTotal: number;
totalDiscountAmount: number;
goodsRealAmount: number; // 商品真实原价总和
goodsOriginalAmount: number; // 商品原价总和
goodsDiscountAmount: number; // 商品折扣金额
couponDeductionAmount: number; // 优惠券总抵扣
productCouponDeduction: number; // 商品优惠券抵扣
fullCouponDeduction: number; // 满减优惠券抵扣
pointDeductionAmount: number; // 积分抵扣金额
seatFee: number; // 餐位费
packFee: number; // 打包费
scoreMaxMoney: number; // 积分最大可抵扣金额
// 新增:商家减免明细
merchantReduction: {
type: MerchantReductionType; // 实际使用的减免类型
originalConfig: MerchantReductionConfig; // 原始配置(便于前端展示)
actualAmount: number; // 实际减免金额计算后的值≥0
};
additionalFee: number; // 附加费
finalPayAmount: number; // 最终实付金额
couponUsed?: Coupon; // 实际使用的优惠券
pointUsed: number; // 实际使用的积分
newUserDiscount: number; // 新用户减免金额默认0
dinnerType?: "dine-in" | "take-out"; // 就餐类型(堂食/自取/配送/快递)
config: OrderExtraConfig; // 订单额外费用配置
//满减活动
fullReduction: {
usedFullReductionActivityFullAmount: number; // 计算出的满减活动的门槛金额
usedActivity?: FullReductionActivity; // 实际使用的满减活动
usedThreshold?: FullReductionThreshold; // 实际使用的满减阈值(多门槛中选最优)
actualAmount: number; // 满减实际减免金额(元)
};
vipDiscountAmount: number; //会员折扣减免金额
// 订单原支付金额
orderOriginFinalPayAmount: number; //订单原金额(包含打包费+餐位费)
}
/** 满减活动阈值单条满减规则满X减Y- 对应 MkDiscountThresholdInsertGroupDefaultGroup */
export interface FullReductionThreshold {
activityId?: number; // 关联满减活动ID
fullAmount?: number; // 满多少金额(元,必填)
discountAmount?: number; // 减多少金额(元,必填)
}
/** 满减活动主表 - 对应 Request 接口(后端真实字段) */
export interface FullReductionActivity {
id?: number; // 自增主键后端字段id
shopId?: number; // 店铺ID后端字段shopId
status?: number; // 活动状态1=未开始2=进行中3=已结束后端字段status
sort?: number; // 排序值越大优先级越高后端字段sort
createTime?: string; // 创建时间后端字段createTime格式如"2025-10-14 13:56:07"
updateTime?: string; // 最新修改时间后端字段updateTime用于优先级排序
validStartTime?: string; // 有效期开始时间后端字段validStartTime格式如"2025-10-14"
validEndTime?: string; // 有效期结束时间后端字段validEndTime格式如"2025-12-14"
useType?: string; // 可使用类型后端字段useType如"dine,pickup,deliv,express"
useDays?: string; // 可用周期后端字段useDays如"周一,周二,周三,周四,周五,周六,周日"
useTimeType?: string; // 可用时间段类型后端字段useTimeTypeall=全时段custom=指定时段)
useStartTime?: string; // 每日可用开始时间后端字段useStartTime如"09:00:00"仅custom时有效
useEndTime?: string; // 每日可用结束时间后端字段useEndTime如"22:00:00"仅custom时有效
couponShare?: number; // 与优惠券同享0=否1=是后端字段couponShare
discountShare?: number; // 与限时折扣同享0=否1=是后端字段discountShare
vipPriceShare?: number; // 与会员价同享0=否1=是后端字段vipPriceShare
pointsShare?: number; // 与积分抵扣同享0=否1=是后端字段pointsShare
thresholds?: FullReductionThreshold[]; // 满减阈值列表多门槛后端字段thresholds
isDel?: boolean; // 是否删除0=否1=是后端字段isDel默认false
}
// 辅助枚举星期映射用于useDays校验
export const WEEKDAY_MAP = {
周一: 1,
周二: 2,
周三: 3,
周四: 4,
周五: 5,
周六: 6,
周日: 0, // JS中getDay()返回0=周日
};
export interface ShopInfo {
isMemberPrice: number; // 是否开启会员价 1是开启
[property: string]: any;
}
export interface couponCalcParams {
canDikouGoodsArr: BaseCartItem[];
coupon: Coupon;
user: ShopUserInfo;
shopInfo: ShopInfo;
selCoupon: Coupon[];
goodsOrderPrice: number; //商品订单总价
isMemberPrice: number; // 是否开启会员价 1是开启
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined;
}
export interface CanDikouGoodsArrArgs {
canDikouGoodsArr: BaseCartItem[];
selCoupon: Coupon[];
user: ShopUserInfo;
shopInfo: ShopInfo;
limitTimeDiscount?: TimeLimitDiscountConfig | null | undefined;
}
export interface returnPriceArgs {
goods: BaseCartItem;
selCoupon: Coupon[];
user: ShopUserInfo;
shopInfo: ShopInfo;
shopUserInfo: ShopUserInfo;
limitTimeDiscountRes?: TimeLimitDiscountConfig | null | undefined;
idKey?: keyof BaseCartItem;
}
export interface CanReturnMemberPriceArgs {
shopInfo?: ShopInfo;
shopUserInfo: ShopUserInfo;
}

View File

@@ -1,33 +0,0 @@
/**
* 通用字段兼容工具函数:处理驼峰/下划线命名的字段取值
* @param obj 目标对象(如商品信息 BaseCartItem
* @param camelCaseKey 驼峰命名字段(如 'isTemporary'
* @param snakeCaseKey 下划线命名字段(如 'is_temporary'
* @param defaultValue 默认值(默认 false适配布尔类型字段
* @returns 字段值(优先取存在的字段,无则返回默认值)
*/
export function getCompatibleFieldValue(
obj: Record<string, any>,
camelCaseKey: string,
snakeCaseKey: string,
defaultValue: boolean = false
): boolean {
// 优先判断驼峰字段(如果存在且不是 undefined/null
if (
obj.hasOwnProperty(camelCaseKey) &&
obj[camelCaseKey] !== undefined &&
obj[camelCaseKey] !== null
) {
return Boolean(obj[camelCaseKey]);
}
// 再判断下划线字段
if (
obj.hasOwnProperty(snakeCaseKey) &&
obj[snakeCaseKey] !== undefined &&
obj[snakeCaseKey] !== null
) {
return Boolean(obj[snakeCaseKey]);
}
// 都不存在时返回默认值(布尔类型字段默认 false
return defaultValue;
}

16
main.js
View File

@@ -6,12 +6,6 @@ import storageManage from '@/commons/utils/storageManage.js'
import dict from '@/commons/utils/dict.js'
import {utils} from '@/commons/utils/index.js'
import uviewPlus from 'uview-plus'
import * as Pinia from 'pinia';
import {
createUnistorage
} from "pinia-plugin-unistorage";
uni.$utils=utils
// 设置node环境
envConfig.changeEnv(storageManage.env())
@@ -30,11 +24,6 @@ const app = new Vue({
})
app.$mount()
// #endif
// #ifdef VUE3
@@ -45,9 +34,6 @@ export function createApp() {
const app = createSSRApp(App)
app.use(uviewPlus)
const store = Pinia.createPinia();
store.use(createUnistorage());
app.use(store)
app.config.globalProperties.$appName = appConfig.appName
uni.$appName = appConfig.appName
app.config.globalProperties.$utils = utils
@@ -56,7 +42,7 @@ export function createApp() {
uni.$dict = dict
return {
app,Pinia
app
}
}
// #endif

36
package-lock.json generated
View File

@@ -5,19 +5,14 @@
"packages": {
"": {
"dependencies": {
"bignumber.js": "^9.3.1",
"clipboard": "^2.0.11",
"dayjs": "^1.11.13",
"gm-crypto": "^0.1.8",
"immutable": "^4.3.7",
"js-base64": "^3.7.2",
"jsbn": "^1.1.0",
"jsencrypt": "^3.3.2",
"lodash": "^4.17.21",
"pinia-plugin-unistorage": "^0.1.2",
"to-arraybuffer": "^1.0.1",
"uview-plus": "^3.3.32",
"ysk-utils": "^1.0.78"
"uview-plus": "^3.3.32"
},
"devDependencies": {
"copy-webpack-plugin": "^12.0.2",
@@ -455,14 +450,6 @@
"node": "*"
}
},
"node_modules/bignumber.js": {
"version": "9.3.1",
"resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.3.1.tgz",
"integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
"engines": {
"node": "*"
}
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -1084,12 +1071,6 @@
"node": ">=8.9.0"
}
},
"node_modules/loadsh": {
"version": "0.0.4",
"resolved": "https://registry.npmmirror.com/loadsh/-/loadsh-0.0.4.tgz",
"integrity": "sha512-U+wLL8InpfRalWrr+0SuhWgGt10M4OyAk6G8xCYo2rwpiHtxZkWiFpjei0vO463ghW8LPCdhqQxXlMy2qicAEw==",
"deprecated": "This is a typosquat on the popular Lodash package. This is not maintained nor is the original Lodash package."
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
@@ -1200,11 +1181,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pinia-plugin-unistorage": {
"version": "0.1.2",
"resolved": "https://registry.npmmirror.com/pinia-plugin-unistorage/-/pinia-plugin-unistorage-0.1.2.tgz",
"integrity": "sha512-WXit2cGnm5rG6CDTcLSLehNWhyJS/Yq7WEeeXAapZbCnqoPJxlszqg7rT8S+OP47az0h5nlajGo+LuyMxUQ2uw=="
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
@@ -1750,16 +1726,6 @@
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/ysk-utils": {
"version": "1.0.78",
"resolved": "https://registry.npmmirror.com/ysk-utils/-/ysk-utils-1.0.78.tgz",
"integrity": "sha512-Bgr5B3WWiy0nbgL91QVKoVPYm4wt13Rlav757zEjMVRHbmTjwFEhi3wJlYus0JGd52mbknSxXHMazAPHXwA7uQ==",
"dependencies": {
"bignumber.js": "^9.3.1",
"loadsh": "^0.0.4",
"lodash": "^4.17.21"
}
}
}
}

View File

@@ -1,19 +1,13 @@
{
"dependencies": {
"bignumber.js": "^9.3.1",
"clipboard": "^2.0.11",
"dayjs": "^1.11.13",
"gm-crypto": "^0.1.8",
"immutable": "^4.3.7",
"js-base64": "^3.7.2",
"jsbn": "^1.1.0",
"jsencrypt": "^3.3.2",
"lodash": "^4.17.21",
"marked": "4.x",
"pinia-plugin-unistorage": "^0.1.2",
"to-arraybuffer": "^1.0.1",
"uview-plus": "^3.3.32",
"ysk-utils": "^1.0.82"
"uview-plus": "^3.3.32"
},
"devDependencies": {
"copy-webpack-plugin": "^12.0.2",

View File

@@ -1,39 +1,36 @@
<template>
<view class="boxconstant min-page">
<view class="bg-fff u-flex u-m-b-32 top">
<image style="width: 60rpx; height: 60rpx" src="/pageBwc/static/images/bwc.png"></image>
<view class="u-flex-1 u-flex u-p-l-24">
<view class="u-font-28 u-flex-1 u-p-r-4">
<view class="color-333 font-bold">霸王餐</view>
<view class="color-666 u-m-t-4 u-font-24">设置充值消费的N倍当前订单立即免单</view>
</view>
<up-switch v-model="form.enable" size="18" :active-value="1" :inactive-value="0" :disabled="isMainShop() ? false : true"></up-switch>
<view class="boxconstant">
<view class="boxconstantbox"
style="border-radius: 18px 18px 0 0;padding:32rpx 24rpx;border-bottom: 2rpx solid #E5E5E5;">
<view class="boxconstantbox_one">
充值设置
</view>
<view class="boxconstantbox_tow">
<text>用户消费结账时成功充值</text>
<input class="text" type="number" :min='2' v-model="form.rechargeTimes" @change="rechargeTimesInput"/>
<text>倍的金额本单即可享受免单</text>
</view>
</view>
<view class="boxconstantbox">
<view class="boxconstantbox_one">充值设置</view>
<view class="u-flex u-m-t-32">
<input class="number-box" type="number" :min="2" v-model="form.rechargeTimes" @blur="rechargeTimesInput" />
<view class="bei"></view>
<view class="boxconstantbox" style="border-radius: 0 0 18px 18px;">
<view class="boxconstantbox_one">
充值门槛
</view>
<view class="color-666 u-font-28 u-m-t-16">用户消费结账时成功充值订单金额的X倍即可享受免单</view>
<view class="u-m-t-20 u-m-b-20">
<up-line class=""></up-line>
</view>
<view class="boxconstantbox_one">充值门槛</view>
<view class="u-flex u-m-t-32">
<input class="number-box" type="number" v-model="form.rechargeThreshold" @blur="rechargeThresholdInput" />
<view class="bei"></view>
</view>
<view class="color-666 u-font-28 u-m-t-16">订单的支付金额满足X元才能使用</view>
</view>
<view class="boxconstantbox">
<view class="boxconstantbox_one">可用门店</view>
<view class="u-m-t-16">
<my-shop-select @shop-select="shopSelect" v-model:selShops="form.shopIdList" v-model:useType="form.useShopType"></my-shop-select>
<view class="boxconstantbox_tow">
<text>订单支付金额需满</text>
<input class="text" type="digit" v-model="form.rechargeThreshold" @change="form.rechargeThreshold = $utils.isMoney(form.rechargeThreshold)"></input>
<!-- <input class="text" type="digit" v-model="form.rechargeThreshold" @input="form.rechargeThreshold = 2"></input> -->
<!-- <input class="text" type="digit" v-model.lazy="form.rechargeThreshold" @input="form.rechargeThreshold = $utils.debounce($utils.isMoney(form.rechargeThreshold))"></input> -->
<!-- <input class="text" type="digit" v-model="form.rechargeThreshold" @input="form.rechargeThreshold = $utils.isMoney(form.rechargeThreshold)"></input> -->
<text> 才能使用</text>
</view>
</view>
<!-- <view class="boxconstantbox"
<view class="oneboxconstant">
<view class="oneboxconstant_one">
功能启用
</view>
<up-switch v-model="form.enable" size="18"></up-switch>
</view>
<view class="boxconstantbox"
style="margin-top:24rpx; padding:32rpx 24rpx; border-radius: 0 0 18px 18px;">
<view class="boxconstantbox_one" style="margin-bottom: 15rpx;">
充值说明
@@ -41,239 +38,139 @@
<view>
<up-textarea v-model="form.rechargeDesc" placeholder="请输入内容"></up-textarea>
</view>
</view> -->
<view class="boxconstantbox">
<view class="boxconstantbox_one">可使用类型</view>
<view class="u-m-t-16">
<my-dine-types v-model="form.useType"></my-dine-types>
</view>
</view>
<view class="boxconstantbox">
<view class="u-flex u-col-center u-row-between">
<view>
<view class="color-333 u-font-32 font-bold">与优惠券同享</view>
<view class="color-666 u-font-24 u-m-t-4">开启后可与优惠券同时使用</view>
</view>
<up-switch v-model="form.withCoupon" size="18"></up-switch>
</view>
<view class="u-flex u-col-center u-row-between u-m-t-24">
<view>
<view class="color-333 u-font-32 font-bold">与积分抵扣同享</view>
<view class="color-666 u-font-24 u-m-t-4">开启后可与积分抵扣同时使用</view>
</view>
<up-switch v-model="form.withPoints" size="18"></up-switch>
</view>
<view class="save" @click="editFreeDing">
保存
</view>
<my-bottom-btn-group @cancel="cancel" @save="editFreeDing" isOpenPermission></my-bottom-btn-group>
<my-marketing-mask name="霸王餐" v-if="isMarketShow(configInfo, 'shopIdList', 'enable')"></my-marketing-mask>
</view>
</template>
<script setup>
import { onShow, onLoad } from '@dcloudio/uni-app';
import { reactive, ref, watch } from 'vue';
import { isMainShop, isMarketShow } from '@/store/account';
import { getFreeDing, updateFreeDing } from '@/http/api/freeDing.js';
const configInfo = ref({});
const form = reactive({
rechargeTimes: 2,
rechargeThreshold: '',
enable: false,
rechargeDesc: '',
useShopType: 'all',
useType: [],
shopIdList: [],
withCoupon: false,
withPoints: false
});
onLoad(() => {
// uni.$utils.inputReg.bind()()
});
onShow(() => {
getlist();
});
/**
* 获取配置信息
*/
const getlist = async () => {
let res = await getFreeDing();
configInfo.value = res;
res.shopIdList = res.shopIdList || [];
Object.assign(form, res);
};
let rechargeTimesInput = (e) => {
console.log(e);
if (e.detail.value == '' || e.detail.value < 2) {
form.rechargeTimes = 2;
uni.showToast({
title: '请输入大于等于2的整数',
icon: 'none'
});
return;
}
};
function rechargeThresholdInput(e) {
if (e.detail.value == '' || e.detail.value <= 0.01) {
form.rechargeThreshold = 0.01;
uni.showToast({
title: '请输入大于0的数字',
icon: 'none'
});
return;
}
}
/**
* 修改配置信息
*/
const editFreeDing = async () => {
if (!form.rechargeTimes) {
uni.showToast({
title: '请输入充值设置',
icon: 'none'
});
return;
}
if (!form.rechargeThreshold) {
uni.showToast({
title: '请输入充值门槛',
icon: 'none'
});
return;
}
if (form.useShopType != 'all' && !form.shopIdList.length) {
uni.showToast({
title: '请选择可用门店',
icon: 'none'
});
return;
}
if (form.useType.length == 0) {
uni.showToast({
title: '请选择可使用类型',
icon: 'none'
});
return;
}
let res = await updateFreeDing(form);
uni.showToast({
title: '保存成功'
import { onShow ,onLoad} from '@dcloudio/uni-app';
import { reactive, ref, watch } from 'vue';
import { getFreeDing, updateFreeDing } from '@/http/api/freeDing.js'
const form = reactive({
rechargeTimes: 2,
rechargeThreshold: '',
enable: false,
rechargeDesc: '',
});
Object.assign(form, res);
setTimeout(() => {
// uni.navigateBack();
}, 1500);
};
onLoad(()=>{
// uni.$utils.inputReg.bind()()
})
onShow(() => {
getlist()
})
/**
* 获取配置信息
*/
const getlist = async () => {
let res = await getFreeDing()
Object.assign(form, res)
}
let rechargeTimesInput = (e) => {
if( uni.$utils.isNumber(e.detail.value) == '' || uni.$utils.isNumber(e.detail.value) < 2 ){
form.rechargeTimes = 2;
return;
}
form.rechargeTimes = uni.$utils.isNumber(e.detail.value)
}
/**
* 修改配置信息
*/
const editFreeDing = async () => {
let res = await updateFreeDing(form)
uni.showToast({
title: '保存成功'
})
Object.assign(form, res)
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
function cancel() {
uni.navigateBack();
}
</script>
<style lang="scss" scoped>
.boxconstant {
background: #f7f7f7;
padding: 32rpx 28rpx;
page {
background: #F9F9F9;
}
.boxconstantbox {
padding: 32rpx 24rpx;
border-radius: 16rpx;
margin-top: 32rpx;
width: 100%;
background: #ffffff;
.boxconstant {
padding: 32rpx 28rpx;
.boxconstantbox_one {
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: bold;
font-size: 28rpx;
color: #333333;
}
.boxconstantbox {
padding: 32rpx 24rpx;
width: 100%;
background: #FFFFFF;
.boxconstantbox_tow {
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
font-size: 28rpx;
color: #333333;
// display: flex;
// justify-content: flex-start;
// align-items: center;
// flex-wrap: wrap;
// align-content: flex-start;
.text {
display: inline-flex;
text-align: center;
margin: 0 12rpx;
width: 118rpx;
height: 48rpx;
line-height: 48rpx;
background: #ffffff;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 2rpx solid #e5e5e5;
.boxconstantbox_one {
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: bold;
font-size: 28rpx;
color: #333333;
}
.boxconstantbox_tow {
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
font-size: 28rpx;
color: #333333;
// display: flex;
// justify-content: flex-start;
// align-items: center;
// flex-wrap: wrap;
// align-content: flex-start;
.text {
display:inline-flex;
text-align: center;
margin: 0 12rpx;
width: 118rpx;
height: 48rpx;
line-height: 48rpx;
background: #FFFFFF;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 2rpx solid #E5E5E5;
}
}
}
}
.oneboxconstant {
margin-top: 32rpx;
width: 100%;
background: #ffffff;
border-radius: 12rpx 12rpx 12rpx 12rpx;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
padding: 32rpx 24rpx;
.oneboxconstant {
margin-top: 32rpx;
width: 100%;
background: #FFFFFF;
border-radius: 12rpx 12rpx 12rpx 12rpx;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
padding: 32rpx 24rpx;
.oneboxconstant_one {
.oneboxconstant_one {
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
font-size: 28rpx;
color: #333333;
}
}
.save {
margin: 100rpx auto 50rpx auto;
width: 530rpx;
height: 80rpx;
background: #318AFE;
border-radius: 56rpx 56rpx 56rpx 56rpx;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
font-size: 28rpx;
color: #333333;
font-size: 32rpx;
color: #FFFFFF;
line-height: 80rpx;
text-align: center;
}
}
.save {
margin: 100rpx auto 50rpx auto;
width: 530rpx;
height: 80rpx;
background: #318afe;
border-radius: 56rpx 56rpx 56rpx 56rpx;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
font-size: 32rpx;
color: #ffffff;
line-height: 80rpx;
text-align: center;
}
}
.top {
padding: 24rpx 20rpx 28rpx 28rpx;
}
.number-box {
width: 260rpx;
font-size: 28rpx;
padding: 0 26rpx;
border-radius: 6rpx 0 0 6rpx;
border-top: 2rpx solid #d9d9d9;
border-bottom: 2rpx solid #d9d9d9;
border-left: 2rpx solid #d9d9d9;
background: #fff;
box-sizing: border-box;
height: 70rpx;
line-height: 70rpx;
}
.bei {
display: flex;
padding: 0 38rpx;
height: 70rpx;
line-height: 70rpx;
align-items: center;
border-radius: 0 6rpx 6rpx 0;
border: 2rpx solid #d9d9d9;
background: #f7f7fa;
font-size: 28rpx;
color: #999999;
}
</style>
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,694 +0,0 @@
<template>
<view class="min-page bg-f7 u-font-28 color-333" :style="pageHeight">
<view class="top u-flex u-row-between">
<view>
<text style="max-width: 286rpx" class="u-line-1 u-font-32">
{{ groupInfo.name }}
</text>
<text class="u-m-l-22">{{ membersRes.user_list.length }}</text>
</view>
<view class="" @click="toMore()">
<view class="u-flex u-row-center">
<up-icon name="more-dot-fill" color="#333" size="14"></up-icon>
</view>
<view class="color-666">更多</view>
</view>
</view>
<view class="box-1">
<!-- @refresherrefresh="refresherrefresh" -->
<scroll-view
scroll-y
refresher-background="transparent"
style="height: 100%"
@scrolltoupper="refresherrefresh"
:refresher-enabled="false"
:scroll-with-animation="false"
:refresher-triggered="scrollView.refresherTriggered"
:scroll-into-view="scrollView.intoView"
>
<view class="scroll-view-box">
<view class="talk-list">
<view :id="'msg-' + index" v-for="(item, index) in chatStore.chatList" :key="index">
<!-- 发消息 -->
<template v-if="item.operate_type == 'sendMsg' || item.is_user_send == 1 || item.from_id == shopInfo.id">
<view class="shop u-flex u-row-right">
<view class="u-p-r-18">
<view class="u-flex u-row-right name-wrap">
<view class="tag">商家</view>
<view class="name">{{ shopInfo.shopName }}</view>
</view>
<view class="u-m-t-14 msg" :class="['type' + item.msg_type]">
<chatItem :item="item"></chatItem>
</view>
<view class="text-center u-m-t-20" v-if="item.msg_type == 4 && item.hasGet">
<text>{{ item.hasGet || 0 }}人已领取</text>
<text class="color-main">优惠券</text>
</view>
</view>
<up-avatar size="70rpx" shape="square" bg-color="#fff" :src="shopInfo.logo"></up-avatar>
</view>
</template>
<!-- 收消息 -->
<template v-else>
<view class="user u-flex">
<up-avatar size="70rpx" :src="item.avatar" shape="square" bg-color="#fff"></up-avatar>
<view class="u-p-l-18">
<view class="name u-line-1">{{ item.nick }}</view>
<view class="u-m-t-14 msg u-p-l-30" :class="['type' + item.msg_type]">
<chatItem :item="item"></chatItem>
</view>
</view>
</view>
</template>
</view>
</view>
<up-loadmore v-if="isEnd" :status="isEnd ? 'nomore' : 'loading'"></up-loadmore>
</view>
</scroll-view>
</view>
<!-- <view :style="bottomSlotHeight"></view> -->
<view class="bottom" :class="[showMoreBtn ? '' : 'safe-bottom']">
<view class="u-flex" style="padding: 14rpx 28rpx">
<!-- <input
type="text"
class="u-flex-1 u-m-r-52 iput"
placeholder="请输入内容"
v-model="msg"
/> -->
<view class="u-flex-1 u-m-r-52 iput">
<up-input v-model="msg" border="none" placeholder="请输入内容"></up-input>
</view>
<button class="send-btn" v-if="msg.trim().length > 0" @click="sendText">发送</button>
<up-icon name="plus-circle" color="#666" size="60rpx" @click="showMoreBtnToggle" v-else></up-icon>
</view>
<view class="more-btn" v-if="showMoreBtn">
<view v-for="(item, index) in moreBtns" @click="moreBtnsClick(item, index)" class="u-flex-col u-row-center u-col-center">
<view class="u-flex icon">
<image :src="item.icon" class="img" mode="aspectFill"></image>
</view>
<view class="u-m-t-8">{{ item.title }}</view>
</view>
</view>
</view>
<!-- 发送优惠券 -->
<Modal title="发送优惠券" v-model="modalData.show" confirmText="确认发送" @close="closeModal" @confirm="confirmCoupon">
<view class="u-p-30">
<view class="font-bold u-m-b-16">自定义文案</view>
<up-input v-model="modalData.form.title" placeholder="请输入自定义文案" :placeholderStyle="placeholderStyle" maxlength="12"></up-input>
<view class="font-bold u-m-b-16 u-m-t-32">发放数量()</view>
<up-number-box v-model="modalData.form.giveNum" inputWidth="240rpx" @change="valChange"></up-number-box>
<view class="font-bold u-m-b-16 u-m-t-32">每人限领量()</view>
<up-number-box v-model="modalData.form.getLimit" :max="modalData.form.giveNum" inputWidth="240rpx" @change="valChange"></up-number-box>
<view class="font-bold u-m-b-16 u-m-t-32">选择优惠券</view>
<chooseCoupon v-model="modalData.form.couponId"></chooseCoupon>
</view>
</Modal>
</view>
</template>
<script setup>
import { onReady, onReachBottom, onLoad, onShow, onPageScroll } from '@dcloudio/uni-app';
import { ref, inject, onMounted, onUnmounted, nextTick, reactive, watch, computed } from 'vue';
import { useChatStore } from '@/store/chat';
import * as chatApi from '@/http/php/chat';
import * as chatCouponApi from '@/http/api/market/chat';
import { uploadFile } from '@/http/api/index.js';
import go from '@/commons/utils/go.js';
import Modal from './components/modal.vue';
import chooseCoupon from './components/coupon.vue';
import chatItem from './components/chat-item.vue';
const placeholderStyle = {
color: '#999',
fontSize: '28rpx'
};
function refresherrefresh() {
console.log('refresherrefresh');
// 关键:同时判断「正在加载」和「无更多数据」,只要一个为真就关闭刷新
if (isLoading.value || isEnd.value) {
scrollView.refresherTriggered = false; // 立即关闭刷新状态
if (isEnd.value) {
}
return; // 终止后续逻辑
}
query.page++;
getMsgList();
}
const scrollView = reactive({
refresherTriggered: false,
intoView: '',
safeAreaHeight: 0
});
const showMoreBtn = ref(false);
function showMoreBtnToggle() {
showMoreBtn.value = !showMoreBtn.value;
if (showMoreBtn.value) {
}
}
const modalData = reactive({
show: false,
form: {
title: '',
getLimit: 1,
giveNum: 1,
couponId: ''
}
});
const chatStore = useChatStore();
const handleReceiveMsg = (msg) => {
nextTick(() => {
scrollView.intoView = 'msg-0';
});
};
// 注册消息回调
chatStore.registerReceiveMsgCallback(handleReceiveMsg);
const msg = ref('');
const shopInfo = uni.getStorageSync('shopInfo');
const moreBtns = ref([
{
icon: '/pageChat/static/pic.png',
title: '发送照片',
value: 'pic'
},
{
icon: '/pageChat/static/video.png',
title: '发送视频',
value: 'video'
},
{
icon: '/pageChat/static/coupon.png',
title: '发送优惠券',
value: 'coupon'
}
]);
function moreBtnsClick(item, index) {
if (item.value == 'coupon') {
modalData.show = true;
return;
}
if (item.value == 'pic') {
sendImg();
}
if (item.value == 'video') {
sendVideo();
}
}
function videoErrorCallback(e) {
console.error('视频播放失败', e);
}
async function sendImg() {
try {
// 1. 调用图片选择API添加fail回调
const res = await new Promise((resolve, reject) => {
uni.chooseImage({
count: 3,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: resolve,
fail: reject // 捕获选择失败(含权限拒绝)
});
});
uni.showLoading({ title: '发送中' });
console.log('选择图片成功', res);
// 2. 批量上传图片(保持原有逻辑)
for (let i = 0; i < res.tempFiles.length; i++) {
const fileRes = await uploadFile(res.tempFiles[i]);
if (fileRes) {
sendMsg({
image_url: fileRes,
msg_type: 2
});
}
}
} catch (err) {
console.error('图片选择/发送失败', err);
// 3. 处理权限拒绝场景
handlePermissionError(err, '图片');
} finally {
// 4. 确保加载弹窗关闭(无论成功/失败)
uni.hideLoading();
}
}
// 视频选择与发送优化
async function sendVideo() {
try {
// 1. 调用视频选择API添加fail回调
const res = await new Promise((resolve, reject) => {
uni.chooseVideo({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: resolve,
fail: reject // 捕获选择失败(含权限拒绝)
});
});
uni.showLoading({ title: '发送中' });
console.log('选择视频成功', res);
// 2. 上传视频(保持原有逻辑)
const fileRes = await uploadFile({ path: res.tempFilePath });
if (fileRes) {
sendMsg({
image_url: fileRes,
msg_type: 5
});
} else {
uni.showToast({
title: '视频发送失败',
icon: 'none'
});
}
} catch (err) {
console.error('视频选择/发送失败', err);
// 3. 处理权限拒绝场景
handlePermissionError(err, '视频');
} finally {
// 4. 确保加载弹窗关闭(无论成功/失败)
uni.hideLoading();
}
}
// 通用权限错误处理函数
function handlePermissionError(err, mediaType) {
const errMsg = err.errMsg || '';
// 识别权限拒绝关键词(兼容不同平台)
const isAuthDenied = ['auth deny', 'permission denied', 'auth denied', '用户拒绝'].some((keyword) => errMsg.includes(keyword));
if (isAuthDenied) {
// 弹窗提示用户,并引导至设置页
uni.showModal({
title: '权限提示',
content: `需要${mediaType}权限才能使用该功能,请前往设置开启`,
confirmText: '去设置',
cancelText: '取消',
success: (modalRes) => {
if (modalRes.confirm) {
// 跳转到小程序设置页
uni.openSetting({
success: (settingRes) => {
console.log('设置页返回结果', settingRes);
// 可根据需要添加权限开启后的回调逻辑
}
});
}
}
});
} else {
// 其他错误(如取消选择),仅轻提示
if (!errMsg.includes('cancel')) {
uni.showToast({
title: `${mediaType}选择失败`,
icon: 'none'
});
}
}
}
const groupInfo = ref({});
async function init() {
const res = await chatApi.groupInfo({
group_id: options.group_id
});
console.log(res);
groupInfo.value = res || {};
if (res) {
chatStore.connectSocket();
// 确保状态监听已初始化
if (!chatStore._listenersInitialized) {
chatStore.initStateListeners();
chatStore._listenersInitialized = true;
}
}
getMsgList();
}
const query = reactive({
page: 1,
size: 10
});
async function getMsgList() {
// 提前拦截:已无更多数据,直接关闭状态
if (isEnd.value) {
scrollView.refresherTriggered = false;
isLoading.value = false;
uni.showToast({ title: '没有更多了', icon: 'none' });
return;
}
isLoading.value = true;
try {
const msgRes = await chatApi.messageHistory({
...query,
chat_type: '2',
session_id: options.session_id,
to_id: options.group_id,
shop_id: options.group_id,
group_id: options.group_id
});
const data = msgRes.list || [];
const selector = `msg-${query.page == 1 ? 0 : chatStore.chatList.length}`;
if (query.page == 1) {
chatStore.chatList = data;
} else {
chatStore.chatList.push(...data);
}
isEnd.value = chatStore.chatList.length >= msgRes.total;
nextTick(() => {
scrollView.intoView = selector;
console.log(selector);
});
} catch (err) {
console.error('加载历史消息异常', err);
// 加载失败回退页码,避免页码错乱
if (query.page > 1) query.page--;
} finally {
console.log('scrollView', scrollView);
setTimeout(() => {
scrollView.refresherTriggered = false;
isLoading.value = false;
}, 100);
}
}
const options = reactive({});
const membersRes = reactive({
user_list: []
});
onLoad((opt) => {
Object.assign(options, opt);
init();
chatApi.messageMarkReadAll({
session_ids: options.group_id
});
chatStore.group_id = options.group_id;
chatApi.groupMembers({ group_id: options.group_id }).then((res) => {
console.log(res);
membersRes.user_list = res.user_list || [];
});
// #ifdef H5
scrollView.safeAreaHeight = uni.getSystemInfoSync().safeArea.height;
// #endif
});
onShow(() => {
// 页面显示时,检查连接状态
if (!chatStore.isConnect || !chatStore.socketTask) {
console.log('聊天页显示检查Socket连接');
chatStore.forceReconnect();
}
});
function toMore() {
go.to('PAGES_CHAT_GROUP_INFO', {
group_id: groupInfo.value.id,
session_id: options.session_id
});
}
function sendText() {
if (!msg.value.trim().length) return;
sendMsg({ content: msg.value, msg_type: 1 });
msg.value = '';
}
function sendMsg(msg) {
chatStore.sendMessage({
to_id: groupInfo.value.id,
to_user_type: groupInfo.value.id,
chat_type: 2,
content: msg.value,
image_url: '',
order_id: '',
session_id: groupInfo.value.session_id || options.session_id,
...msg
});
}
function closeModal() {
modalData.form = {
title: '',
getLimit: 1,
giveNum: 1,
couponId: ''
};
}
function confirmCoupon() {
console.log(modalData.form);
// if (!modalData.form.title) {
// uni.showToast({
// title: "请输入自定义文案",
// icon: "none",
// });
// return;
// }
if (!modalData.form.couponId) {
uni.showToast({
title: '请选择优惠券',
icon: 'none'
});
return;
}
if (!modalData.form.giveNum) {
uni.showToast({
title: '请输入发放数量',
icon: 'none'
});
return;
}
if (!modalData.form.getLimit) {
uni.showToast({
title: '请输入每人限领量',
icon: 'none'
});
return;
}
chatCouponApi.chatCouponCreate(modalData.form).then((res) => {
if (res) {
modalData.show = false;
const couponJson = JSON.parse(res.couponJson);
sendMsg({
coupon: {
...couponJson,
title: modalData.form.title,
activity_id: res.id
},
chat_coupon_id: res.id,
msg_type: 4
});
closeModal();
} else {
uni.showToast({
title: '发送失败',
icon: 'none'
});
}
});
}
function previewImage(url) {
uni.previewImage({
urls: [url]
});
}
//是否到底了
const isBottom = ref(false);
const isLoading = ref(false);
//消息是否完了
const isEnd = ref(false);
watch(
() => chatStore.chatList.length,
(newVal, oldVal) => {}
);
const bottomSlotHeight = computed(() => {
if (showMoreBtn.value) {
return {
height: '180px'
};
} else {
return {
height: '88px'
};
}
});
onReady(() => {});
const pageHeight = computed(() => {
const safeAreaHeight = scrollView.safeAreaHeight;
if (safeAreaHeight > 0) {
return `height: calc(${safeAreaHeight}px - var(--window-top));`;
}
return '';
});
onUnmounted(() => {
// 组件卸载时,移除消息回调
chatStore.removeReceiveMsgCallback(handleReceiveMsg);
});
</script>
<style lang="scss" scoped>
$grayColor: #9e9e9e;
$grayBorderColor: #bebebe;
.top {
background-color: #fff;
padding: 40rpx 32rpx;
}
.min-page {
height: calc(100vh - var(--window-top));
display: flex;
flex-direction: column;
flex-wrap: nowrap;
align-content: center;
justify-content: space-between;
align-items: stretch;
}
.talk-list {
padding: 30rpx 28rpx 30rpx 26rpx;
display: flex;
flex-direction: column-reverse;
flex-wrap: nowrap;
align-content: flex-start;
justify-content: flex-end;
align-items: stretch;
// 添加弹性容器,让内容自动在顶部
&::before {
content: '.';
display: inline;
visibility: hidden;
line-height: 0;
font-size: 0;
flex: 1 0 auto;
height: 1px;
}
}
.box-1 {
width: 100%;
height: 0;
flex: 1 0 auto;
box-sizing: content-box;
}
.user {
margin-bottom: 88rpx;
align-items: flex-start;
.name {
margin-top: -4upx;
color: $grayColor;
font-size: 24upx;
}
.msg {
padding: 16rpx 20rpx;
background-color: #fff;
border-radius: 20rpx;
}
}
.shop {
margin-bottom: 88rpx;
align-items: flex-start;
.tag {
margin-right: 32rpx;
padding: 4rpx 12rpx;
border: 1px solid $grayBorderColor;
border-radius: 8rpx;
font-size: 20rpx;
color: $grayColor;
}
.name-wrap {
margin-top: -8upx;
}
.name {
color: $grayColor;
font-size: 24upx;
}
.msg {
padding: 16rpx 20rpx;
border-radius: 20rpx;
&.type1 {
background-color: #fff;
}
&.type4 {
background-color: #fff;
}
.img {
width: 50vw;
}
}
}
.bottom {
height: auto;
z-index: 2;
border-top: #e5e5e5 solid 1px;
box-sizing: content-box;
background-color: #f3f3f3;
/* 兼容iPhoneX */
padding-bottom: 0;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
&.safe-bottom {
padding-bottom: calc(env(safe-area-inset-bottom) + 4rpx);
}
.iput {
background-color: #f8f8f8;
padding: 16rpx 20rpx;
}
}
.send-btn {
background-color: $my-main-color;
color: #fff;
line-height: 1;
padding: 16rpx 20rpx;
border-radius: 8rpx;
font-size: 28rpx;
}
.more-btn {
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx;
background-color: #f0f0f0;
gap: 74rpx;
text-align: center;
color: #666;
padding-bottom: calc(env(safe-area-inset-bottom) + 4rpx);
.icon {
width: 100rpx;
height: 100rpx;
background-color: #fff;
border-radius: 16rpx;
display: flex;
justify-content: center;
align-items: center;
.img {
height: 70rpx;
width: 70rpx;
}
}
}
.scroll-view-box {
display: flex;
flex-direction: column-reverse;
}
</style>

View File

@@ -1,147 +0,0 @@
<template>
<text class="" v-if="item.msg_type == 1">{{ item.content }}</text>
<image
v-if="item.msg_type == 2"
:src="item.image_url"
class="img"
mode="widthFix"
@click="previewImage(item.image_url)"
></image>
<video
@error="videoErrorCallback"
v-if="item.msg_type == 5"
:src="item.image_url"
class="img"
mode="widthFix"
></video>
<view class="" v-if="item.msg_type == 4">
<view>{{ item.coupon.title }}</view>
<view class="u-m-t-16 bg-f7 coupon u-flex u-col-stretch" style="min-width: 500rpx;">
<template v-if="item.coupon.type == 1">
<view class="left">
<view class="price">
<text class="u-font-32">¥</text>
<text style="font-size: 72rpx">{{
item.coupon.discountAmount
}}</text>
</view>
<view class="u-font-24 color-999 no-wrap"
>{{ item.coupon.fullAmount }}可用</view
>
</view>
</template>
<template v-if="item.coupon.type == 2">
<view class="left">
<view class="price">
<text class="u-font-32"
>商品兑换券</text
>
</view>
<view class="u-font-24 color-999 no-wrap"
>{{ item.coupon.fullAmount }}可用</view
>
</view>
</template>
<template v-if="item.coupon.type == 3">
<view class="left">
<view class="price">
<text class="u-font-32"
>{{ item.coupon.discountRate / 100 }}</text
>
</view>
<view class="u-font-24 color-999 no-wrap"
>{{ item.coupon.fullAmount }}可用</view
>
</view>
</template>
<template v-if="item.coupon.type == 4">
<view class="left">
<view class="price">
<text class="u-font-32"
>第二件半价券</text
>
</view>
</view>
</template>
<template v-if="item.coupon.type == 6">
<view class="left">
<view class="price">
<text class="u-font-32"
>买一送一券</text
>
</view>
</view>
</template>
<view class="right u-p-l-28 u-flex u-col-center">
<view class="u-font-32">{{ item.coupon.couponName }}</view>
</view>
</view>
</view>
</template>
<script setup>
import dayjs from 'dayjs'
const props = defineProps({
item: {
type: Object,
default: () => {},
},
});
function previewImage(url) {
uni.previewImage({
urls: [url],
});
}
function returnTime(coupon){
// if(coupon.validType=="fixed"){
// return dayjs().add(coupon.daysToTakeEffect,'day').format('YYYY-MM-DD')
// }
let startTime = coupon.useStartTime;
let endTime = coupon.useEndTime;
if(startTime && endTime){
return startTime.split(' ')[0] + '-' + endTime.split(' ')[0]
}
}
</script>
<style lang="scss" scoped>
.img {
width: 50vw;
}
.coupon {
padding: 16rpx 10rpx;
border-radius: 16rpx;
.price {
color: #ff1c1c;
font-weight: 700;
}
.left {
width: 112rpx;
margin-right: 26rpx;
}
.right {
border-left: 1rpx solid #ededed;
}
}
.lingqu {
background-color: #e8ad7b;
line-height: 48rpx;
font-size: 28rpx;
padding: 6rpx 70rpx;
color: #fff;
border-radius: 140rpx;
&.hasGet {
background-color: #eee;
color: #999;
}
}
.u-col-stretch{
align-items: stretch;
}
</style>

View File

@@ -1,201 +0,0 @@
<template>
<view class="">
<up-popup :show="show" mode="bottom">
<view class="">
<view class="top u-flex u-row-between">
<text class="font-bold u-font-32 color-333">{{ title }}</text>
<up-icon size="18" name="close" @click="show = false"></up-icon>
</view>
<scroll-view
ref="couponScroll"
:scroll-y="true"
style="max-height: 50vh"
@scroll="scroll"
:scroll-top="scrollTop"
>
<view
v-for="(item, index) in list"
:key="index"
class="item"
@click="itemClick(item)"
:class="[selGoods && selGoods.id == item.id ? 'selected' : '']"
>
<view class="u-flex u-row-between">
<view class="u-flex gap-20">
<!-- <view class="u-flex" @click.stop="preview(item)">
<up-image
:src="item.coverImg"
width="80rpx"
height="80rpx"
></up-image>
</view> -->
<text class="u-font-32 color-333">{{ item.title }}</text>
</view>
<text class="u-font-32 color-red u-p-l-30"
></text
>
</view>
</view>
<up-empty v-if="list.length == 0" class="u-p-30" text="暂无数据"></up-empty>
</scroll-view>
<view class="bottom">
<view class="btn cancel" @click="close">{{ cancelText }}</view>
<view class="btn success" @click="confirm">{{ confirmText }}</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import { ref, onMounted, watch } from "vue";
const modelValue = defineModel({
type: String,
default: "",
});
const show = defineModel("show", {
type: String,
default: "",
});
const couponName = defineModel("couponName", {
type: String,
default: "",
});
const props = defineProps({
title: {
type: String,
default: "选择优惠券",
},
confirmText: {
type: String,
default: "确认",
},
cancelText: {
type: String,
default: "取消",
},
list: {
type: Array,
default: () => [],
},
});
const selGoods = ref("");
function itemClick(item) {
if (selGoods.value && selGoods.value.id == item.id) {
selGoods.value = "";
return;
}
selGoods.value = item;
}
const scrollTop = ref(0);
function scroll(e) {
scrollTop.value = e.detail.scrollTop;
}
function preview(item) {
uni.previewImage({
urls: item.images || [item.coverImg],
});
}
watch(
() => modelValue.value,
(newVal, oldVal) => {
console.log(newVal, oldVal);
selGoods.value = props.list.find((item) => item.id == newVal);
console.log(selGoods.value);
if (selGoods.value) {
couponName.value = selGoods.value.title;
}
}
);
watch(
() => props.list.length,
(newVal, oldVal) => {
selGoods.value = props.list.find((item) => item.id == modelValue.value);
console.log(selGoods.value);
if (selGoods.value) {
modelValue.value = selGoods.value.id;
couponName.value = selGoods.value.title;
}
}
);
function close() {
show.value = false;
}
const emit = defineEmits(["confirm"]);
function confirm() {
if (!selGoods.value) {
uni.showToast({
title: "请选择优惠券",
icon: "none",
});
return;
}
modelValue.value = selGoods.value.id;
show.value = false;
emit("confirm", selGoods.value);
}
</script>
<style lang="scss">
.popup-content {
background: #fff;
width: 640rpx;
border-radius: 18rpx;
}
.top {
padding: 40rpx 48rpx;
border-bottom: 1px solid #d9d9d9;
}
.bottom {
padding: 48rpx 52rpx;
display: flex;
justify-content: space-between;
border-top: 1px solid #d9d9d9;
gap: 50rpx;
.btn {
flex: 1;
text-align: center;
padding: 18rpx 60rpx;
border-radius: 100rpx;
font-size: 32rpx;
border: 2rpx solid transparent;
&.success {
background-color: $my-main-color;
color: #fff;
}
&.cancel {
border-color: #d9d9d9;
box-shadow: 0 4rpx 0 0 #00000005;
}
}
}
.item {
padding: 10rpx 30rpx;
border: 1px solid #d9d9d9;
margin: 10rpx;
border-radius: 8rpx;
transition: all 0.3s ease-in-out;
box-shadow: 0 0 10px transparent;
&.selected {
border-color: $my-main-color;
box-shadow: 0 0 10px $my-main-color;
}
}
.choose-goods {
display: flex;
padding: 24rpx;
align-items: center;
border-radius: 8rpx;
border: 2rpx solid #d9d9d9;
background: #fff;
font-size: 28rpx;
font-weight: 400;
}
</style>

View File

@@ -1,198 +0,0 @@
<template>
<view class="">
<view @click="show = true">
<slot v-if="$slots.default"> </slot>
<view v-else class="choose-goods u-flex u-row-between">
<text class="color-999" v-if="!modelValue">请选择商品</text>
<text class="color-333 u-m-r-32 u-line-1" v-else>{{ goodsName }}</text>
<up-icon size="14" name="arrow-down"></up-icon>
</view>
</view>
<up-popup :show="show" mode="bottom">
<view class="">
<view class="top u-flex u-row-between">
<text class="font-bold u-font-32 color-333">{{ title }}</text>
<up-icon size="18" name="close" @click="show = false"></up-icon>
</view>
<scroll-view :scroll-y="true" style="max-height: 50vh" @scroll="scroll" :scroll-top="scrollTop">
<view
v-for="(item, index) in list"
:key="index"
class="item"
@click="itemClick(item)"
:class="[selGoods&&selGoods.id == item.id ? 'selected' : '']"
>
<view class="u-flex u-row-between">
<view class="u-flex gap-20">
<view class="u-flex" @click.stop="preview(item)">
<up-image
:src="item.coverImg"
width="80rpx"
height="80rpx"
></up-image>
</view>
<text class="u-font-32 color-333">{{ item.name }}</text>
</view>
<text class="u-font-32 color-red u-p-l-30"
>¥{{ item.lowPrice }}</text
>
</view>
</view>
</scroll-view>
<view class="bottom">
<view class="btn cancel" @click="close">{{ cancelText }}</view>
<view class="btn success" @click="confirm">{{ confirmText }}</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import { ref, onMounted, watch } from "vue";
import { getProductList } from "@/http/api/product.js";
const show = ref(false);
const modelValue = defineModel({
type: String,
default: "",
});
const goodsName = defineModel("goodsName", {
type: String,
default: "",
});
const props = defineProps({
title: {
type: String,
default: "选择商品",
},
confirmText: {
type: String,
default: "确认",
},
cancelText: {
type: String,
default: "取消",
},
});
const selGoods = ref("");
function itemClick(item) {
if (selGoods.value&& selGoods.value.id == item.id) {
selGoods.value = "";
return;
}
selGoods.value = item;
}
const list = ref([]);
const scrollTop = ref(0);
function scroll(e) {
scrollTop.value = e.detail.scrollTop;
}
function preview(item) {
uni.previewImage({
urls: item.images || [item.coverImg],
});
}
watch(
() => modelValue.value,
(newVal, oldVal) => {
console.log(newVal, oldVal);
selGoods.value = list.value.find((item) => item.id == newVal);
console.log(selGoods.value);
if(selGoods.value){
goodsName.value = selGoods.value.name;
}
}
);
watch(()=>list.value.length,(newVal,oldVal)=>{
selGoods.value = list.value.find((item) => item.id == modelValue.value);
console.log(selGoods.value);
if(selGoods.value){
modelValue.value = selGoods.value.id;
goodsName.value = selGoods.value.name;
}
})
function close() {
show.value = false;
}
function confirm() {
if (!selGoods.value) {
uni.showToast({
title: "请选择商品",
icon: "none",
});
return;
}
modelValue.value = selGoods.value.id;
show.value = false;
}
onMounted(() => {
getProductList().then((res) => {
list.value = res;
});
});
</script>
<style lang="scss">
.popup-content {
background: #fff;
width: 640rpx;
border-radius: 18rpx;
}
.top {
padding: 40rpx 48rpx;
border-bottom: 1px solid #d9d9d9;
}
.bottom {
padding: 48rpx 52rpx;
display: flex;
justify-content: space-between;
border-top: 1px solid #d9d9d9;
gap: 50rpx;
.btn {
flex: 1;
text-align: center;
padding: 18rpx 60rpx;
border-radius: 100rpx;
font-size: 32rpx;
border: 2rpx solid transparent;
&.success {
background-color: $my-main-color;
color: #fff;
}
&.cancel {
border-color: #d9d9d9;
box-shadow: 0 4rpx 0 0 #00000005;
}
}
}
.item {
padding: 10rpx 30rpx;
border: 1px solid #d9d9d9;
margin: 10rpx;
border-radius: 8rpx;
transition: all 0.3s ease-in-out;
box-shadow: 0 0 10px transparent;
&.selected {
border-color: $my-main-color;
box-shadow: 0 0 10px $my-main-color;
}
}
.choose-goods {
display: flex;
padding: 24rpx;
align-items: center;
border-radius: 8rpx;
border: 2rpx solid #d9d9d9;
background: #fff;
font-size: 28rpx;
font-weight: 400;
}
</style>

View File

@@ -1,92 +0,0 @@
<template>
<view>
<view
class="u-flex u-row-between u-m-b-24"
style="gap: 40rpx"
v-for="(item, index) in modelValue"
:key="index"
>
<view
class="choose-coupon u-flex-1 u-flex u-row-between"
@click="showCoupon(item, index)"
>
<template v-if="item.title">
<text>{{ item.title }}</text>
<view class="u-flex" @click.stop="item.title = ''">
<up-icon name="close" size="14"></up-icon>
</view>
</template>
<template v-else>
<text class="color-999">选择赠送券</text>
<up-icon name="arrow-down" size="14"></up-icon>
</template>
</view>
<view class="u-flex-1 u-flex">
<view class="u-flex-1 choose-coupon u-flex">
<input
class="u-flex-1"
placeholder=""
type="number"
v-model="item.num"
placeholder-class="color-999 u-font-28"
/>
<text class="no-wrap color-999">/1个码</text>
</view>
<view class="u-m-l-20">
<up-icon name="minus-circle-fill" color="#EB4F4F" size="18" @click="removeCoupon(index)"></up-icon>
</view>
</view>
</view>
<chooseCoupon
v-model="chooseCouponData.couponId"
v-model:show="chooseCouponData.show"
@confirm="confirmCoupon"
:list="couponList"
></chooseCoupon>
</view>
</template>
<script setup>
import { reactive, ref, onMounted } from "vue";
import chooseCoupon from "./choose-coupon.vue";
import { couponPage } from "@/http/api/market/index.js";
const chooseCouponData = reactive({
couponId: "",
show: false,
index: -1,
item: null,
});
const modelValue = defineModel({
type: Array,
default: () => [],
});
const couponList = ref([]);
function showCoupon(item, index) {
chooseCouponData.couponId = item ? item.id : "";
chooseCouponData.show = true;
chooseCouponData.index = index;
chooseCouponData.item = item;
}
function confirmCoupon(e) {
modelValue.value[chooseCouponData.index].id = e.id;
modelValue.value[chooseCouponData.index].title = e.title;
}
function removeCoupon(index) {
modelValue.value.splice(index, 1);
}
onMounted(() => {
couponPage({ size: 999 }).then((res) => {
couponList.value = res.records;
});
});
</script>
<style lang="scss" scoped>
.choose-coupon {
padding: 10rpx 20rpx;
border-radius: 8rpx;
border: 1px solid #d9d9d9;
}
</style>

View File

@@ -1,75 +0,0 @@
<template>
<view>
<view class="u-flex u-row-between u-m-b-24" style="gap: 40rpx">
<view
class="choose-coupon u-flex-1 u-flex u-row-between"
@click="showCoupon(item, index)"
>
<template v-if="couponName">
<text>{{ couponName }}</text>
<view class="u-flex" @click.stop="modelValue = ''">
<up-icon name="close" size="14"></up-icon>
</view>
</template>
<template v-else>
<text class="color-999">选择赠送券</text>
<up-icon name="arrow-down" size="14"></up-icon>
</template>
</view>
</view>
<chooseCoupon
v-model="chooseCouponData.couponId"
v-model:show="chooseCouponData.show"
@confirm="confirmCoupon"
:list="couponList"
></chooseCoupon>
</view>
</template>
<script setup>
import { reactive, ref, onMounted, computed } from "vue";
import chooseCoupon from "./choose-coupon.vue";
import { chatCoupon } from "@/http/api/market/chatCoupon.js";
const chooseCouponData = reactive({
couponId: "",
show: false,
index: -1,
item: null,
});
const modelValue = defineModel({
default: "",
});
const couponName = computed(() => {
if (modelValue.value) {
const item = couponList.value.find((item) => item.id === modelValue.value);
return item?.title || "";
}
return "";
});
const couponList = ref([]);
function showCoupon(item, index) {
chooseCouponData.couponId = modelValue.value;
chooseCouponData.show = true;
}
function confirmCoupon(e) {
modelValue.value=e.id
}
function removeCoupon(index) {
modelValue.value.splice(index, 1);
}
onMounted(() => {
chatCoupon({ size: 999 }).then((res) => {
couponList.value = res.records;
});
});
</script>
<style lang="scss" scoped>
.choose-coupon {
padding: 10rpx 20rpx;
border-radius: 8rpx;
border: 1px solid #d9d9d9;
}
</style>

View File

@@ -1,108 +0,0 @@
<template>
<view>
<view @click="open">
<slot v-if="$slots.default"> </slot>
<view v-else class="choose-goods u-flex u-row-between">
<text class="color-999" v-if="!startTime && !endTime"
>请选择日期范围</text
>
<text class="color-333 u-font-24 u-m-r-32 " v-else
>{{ startTime }} - {{ endTime }}</text
>
<view class="u-flex" v-if="startTime&&endTime" @click.stop="clear">
<up-icon name="close" size="14"></up-icon>
</view>
<up-icon size="14" name="arrow-right" v-else ></up-icon>
</view>
</view>
<my-date-pickerview
@confirm="datePickerConfirm"
mode="all"
ref="datePicker"
></my-date-pickerview>
</view>
</template>
<script setup>
import { ref } from "vue";
const datePicker = ref(null);
const startTime = defineModel("startTime", {
default: () => "",
type: String,
});
const endTime = defineModel("endTime", {
default: () => "",
type: String,
});
function clear(){
startTime.value = "";
endTime.value = "";
}
function open() {
datePicker.value.toggle();
}
function datePickerConfirm(e) {
startTime.value = e.start;
endTime.value = e.end;
console.log(e);
}
</script>
<style lang="scss">
.popup-content {
background: #fff;
width: 640rpx;
border-radius: 18rpx;
}
.top {
padding: 40rpx 48rpx;
border-bottom: 1px solid #d9d9d9;
}
.bottom {
padding: 48rpx 52rpx;
display: flex;
justify-content: space-between;
border-top: 1px solid #d9d9d9;
gap: 50rpx;
.btn {
flex: 1;
text-align: center;
padding: 18rpx 60rpx;
border-radius: 100rpx;
font-size: 32rpx;
border: 2rpx solid transparent;
&.success {
background-color: $my-main-color;
color: #fff;
}
&.cancel {
border-color: #d9d9d9;
box-shadow: 0 4rpx 0 0 #00000005;
}
}
}
.item {
padding: 10rpx 30rpx;
border: 1px solid #d9d9d9;
margin: 10rpx;
border-radius: 8rpx;
transition: all 0.3s ease-in-out;
box-shadow: 0 0 10px transparent;
&.selected {
border-color: $my-main-color;
box-shadow: 0 0 10px $my-main-color;
}
}
.choose-goods {
display: flex;
padding: 24rpx;
align-items: center;
border-radius: 8rpx;
border: 2rpx solid #d9d9d9;
background: #fff;
font-size: 28rpx;
font-weight: 400;
min-height: 90rpx;
}
</style>

View File

@@ -1,85 +0,0 @@
<template>
<view>
<up-popup :show="show" mode="center">
<view class="popup-content">
<view class="top u-flex u-row-between">
<text class="font-bold u-font-32 color-333">{{ title }}</text>
<up-icon size="18" name="close" @click="close"></up-icon>
</view>
<up-line></up-line>
<scroll-view style="max-height: 50vh" :scroll-y="true">
<slot></slot>
</scroll-view>
<up-line></up-line>
<view class="bottom">
<view class="btn cancel" @click="close">{{ cancelText }}</view>
<view class="btn success" @click="confirm">{{ confirmText }}</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import { ref } from "vue";
const props = defineProps({
title: {
type: String,
default: "标题",
},
confirmText: {
type: String,
default: "保存",
},
cancelText: {
type: String,
default: "取消",
},
});
const show = defineModel({
type: Boolean,
default: false,
});
const emits = defineEmits(["close", "confirm"]);
function close() {
show.value = false;
emits("close");
}
function confirm() {
emits("confirm");
}
</script>
<style lang="scss">
.popup-content {
background: #fff;
width: 640rpx;
border-radius: 18rpx;
}
.top {
padding: 40rpx 48rpx;
}
.bottom {
padding: 48rpx 52rpx;
display: flex;
justify-content: space-between;
gap: 50rpx;
.btn {
flex: 1;
text-align: center;
padding: 18rpx 60rpx;
border-radius: 100rpx;
font-size: 32rpx;
border: 2rpx solid transparent;
white-space: nowrap;
&.success {
background-color: $my-main-color;
color: #fff;
}
&.cancel {
border-color: #d9d9d9;
box-shadow: 0 4rpx 0 0 #00000005;
}
}
}
</style>

View File

@@ -1,49 +0,0 @@
<template>
<view>
<up-action-sheet
:round="14"
:actions="list"
:title="title"
:show="show"
closeOnClickAction
cancelText="取消"
@close="close"
@select="sheetSelect"
></up-action-sheet>
</view>
</template>
<script setup >
import { ref, reactive, onMounted } from "vue";
import { getShopList } from "@/http/api/shop.js";
const props = defineProps({
title: {
type: String,
default: "请选择",
},
});
const list = ref([]);
const show=defineModel({
name: "show",
default: false,
})
function close() {
show.value = false;
}
const emit=defineEmits(["choose"]);
function sheetSelect(e) {
console.log(e);
emit("choose", e);
}
onMounted(() => {
getShopList().then((res) => {
list.value = res.map((v) => ({
...v,
value: v.shopId,
name: v.shopName,
}));
});
});
</script>

View File

@@ -1,230 +0,0 @@
<template>
<view class="min-page bg-f7 u-font-28 color-333 u-p-t-32">
<view class="bg-fff">
<view class="default-box u-flex u-row-between border-bottom">
<text class="font-bold">优惠券名称</text>
<text>{{ couponActivity.couponJson.couponName }}</text>
</view>
<view class="default-box u-flex u-row-between border-bottom">
<text class="font-bold">发放数量</text>
<text>{{ couponActivity.giveNum }}</text>
</view>
<view class="default-box u-flex u-row-between border-bottom">
<text class="font-bold">已领取数量</text>
<text>{{ couponActivity.giveNum - couponActivity.leftNum }}</text>
</view>
<view class="default-box u-flex u-row-between border-bottom">
<text class="font-bold">剩余数量</text>
<text>{{ couponActivity.leftNum }}</text>
</view>
<view class="default-box u-flex u-row-between border-bottom">
<text class="font-bold">已使用数量</text>
<text>{{ couponActivity.useNum }}</text>
</view>
</view>
<view class="u-flex u-row-right default-box u-p-r-32 bg-fff">
<up-select
:options="statusOptions"
@select="selectItem"
labelName="label"
>
<template #text>
<view class="select">
<text v-if="selStatus">{{ selStatus.label }}</text>
<text v-else class="color-999">请选择状态</text>
<up-icon name="arrow-down"></up-icon>
</view>
</template>
<template #icon> <view></view></template>
</up-select>
</view>
<view class="u-m-t-32 table bg-fff">
<view class="header">
<view class="user">用户</view>
<view class="gettime">领取时间</view>
<view class="usetime">使用时间</view>
<view class="status">状态</view>
<view class="operation">操作</view>
</view>
<view class="body">
<view class="item" v-for="(item, index) in list" :key="index">
<view class="user">
<view>
{{ item.nickName }}
</view>
<view> ({{ item.shopUserId }}) </view>
</view>
<view class="gettime">{{ item.createTime }}</view>
<view class="usetime">{{ item.useTime }}</view>
<view class="status">{{ returnStatus(item.status) }}</view>
<view class="operation status3">
<text v-if="item.status == 0" @click="deleteRecord(item)"
>失效</text
></view
>
</view>
</view>
</view>
<up-loadmore :status="isEnd ? 'nomore' : 'loading'"></up-loadmore>
<view style="height: 40px"></view>
</view>
</template>
<script setup>
import { onReachBottom, onLoad } from "@dcloudio/uni-app";
import { reactive, ref, watch } from "vue";
import { onShow } from "@dcloudio/uni-app";
import go from "@/commons/utils/go.js";
import * as chatCouponApi from "@/http/api/market/chat";
function deleteRecord(item) {
uni
.showModal({
title: "提示",
content: "确定要让该记录失效吗?",
})
.then((res) => {
if (res.confirm) {
chatCouponApi
.deleteRecord({
id: item.id,
})
.then((res) => {
uni.showToast({
title: "操作成功",
icon: "none",
});
setTimeout(() => {
refresh();
}, 1000);
});
}
});
}
const statusOptions = ref([
{ label: "全部", value: "" },
{ label: "未使用", value: 0 },
{ label: "已使用", value: 1 },
{ label: "已过期", value: 2 },
]);
function returnStatus(status) {
if (status == 0) {
return "未使用";
}
if (status == 1) {
return "已使用";
}
if (status == 2) {
return "已过期";
}
}
const selStatus = ref("");
const query = reactive({
page: 1,
pageSize: 10,
status: "",
});
const options = reactive({
id: "",
});
function selectItem(item) {
selStatus.value = item;
}
watch(
() => selStatus.value,
(newVal, oldVal) => {
query.status = newVal.value;
refresh();
}
);
function refresh() {
query.page = 1;
isEnd.value = false;
getList();
}
const couponActivity = ref({});
const list = ref([]);
const isEnd = ref(false);
function getList() {
chatCouponApi.chatCouponRecord({ ...query, id: options.id }).then((res) => {
if (query.page == 1) {
list.value = res.records;
} else {
list.value.push(...res.records);
}
isEnd.value = query.page >= res.totalPage * 1;
});
}
onReachBottom(() => {
if (!isEnd.value) {
query.page++;
getList();
}
});
onLoad((opt) => {
Object.assign(options, opt);
const item = uni.getStorageSync("cach_couponActivity");
couponActivity.value = item || {};
console.log(item);
getList();
});
</script>
<style lang="scss" scoped>
.default-box {
padding: 24rpx 28rpx;
}
.select {
padding: 20rpx;
border-radius: 8rpx;
border: 2rpx solid #d9d9d9;
min-width: 322rpx;
display: flex;
justify-content: space-between;
}
.table {
.user {
width: 142rpx;
}
.gettime {
width: 172rpx;
}
.usetime {
width: 158rpx;
}
.status {
width: 84rpx;
}
.operation {
width: 56rpx;
&.status3 {
color: #ff383c;
}
}
.header {
display: flex;
justify-content: space-between;
padding: 32rpx 16rpx;
color: #666;
border-bottom: 1px solid #e5e5e5;
}
.body {
.item {
display: flex;
justify-content: space-between;
padding: 24rpx 16rpx;
border-bottom: 1px solid #e5e5e5;
}
}
}
</style>

View File

@@ -1,312 +0,0 @@
<template>
<view class="min-page bg-f7">
<up-sticky>
<view class="top">
<my-tabs
:list="tabs.list"
v-model="tabs.selIndex"
@change="tabsChange"
textKey="label"
></my-tabs>
</view>
</up-sticky>
<view class="list">
<view class="item" v-for="(item, index) in list" :key="index">
<view class="u-flex u-row-between">
<view>{{ item.couponJson.couponName }}</view>
<view class="u-flex">
<view class="status" :class="['status' + item.status]">{{
returnStateText(item)
}}</view>
<view class="Id">ID:{{ item.id }}</view>
</view>
</view>
<view class="u-m-t-28 desc">
<view class="u-flex">
<text class="color-666 text-right u-m-r-48" style="min-width: 96rpx"
>使用门槛</text
>
<text class="color-333"
>{{ item.couponJson.fullAmount }}元可用</text
>
</view>
<view class="u-flex u-m-t-16">
<text class="color-666 text-right u-m-r-48" style="min-width: 96rpx"
>有效期</text
>
<text class="color-333"
>领券后{{ item.couponJson.validDays }}天过期</text
>
</view>
</view>
<view class="u-flex u-row-between u-m-t-24">
<view
class="u-flex u-flex-col u-row-center u-font-24 u-col-center text-center"
>
<view class="color-333 font-bold">{{ item.giveNum }}</view>
<view class="color-666 u-m-t-16">发放数量</view>
</view>
<view
class="u-flex u-flex-col u-row-center u-font-24 u-col-center text-center"
>
<view class="color-333 font-bold">{{
item.giveNum - item.leftNum
}}</view>
<view class="color-666 u-m-t-16">已领取</view>
</view>
<view
class="u-flex u-flex-col u-row-center u-font-24 u-col-center text-center"
>
<view class="color-333 font-bold">{{ item.leftNum }}</view>
<view class="color-666 u-m-t-16">剩余</view>
</view>
<view
class="u-flex u-flex-col u-row-center u-font-24 u-col-center text-center"
>
<view class="color-333 font-bold">{{ item.useNum }}</view>
<view class="color-666 u-m-t-16">已使用</view>
</view>
</view>
<view class="u-m-t-16 u-flex u-row-right" style="gap: 16rpx">
<view
class="btn share"
@click="toShare(item)"
v-if="item.status == 1"
>
分享</view
>
<view class="btn cancel" @click="cancel(item)">失效</view>
<view class="btn primary" @click="toDetail(item)"> 查看</view>
</view>
</view>
</view>
<up-loadmore :status="isEnd ? 'nomore' : 'loading'"></up-loadmore>
<view style="height: 40px"></view>
</view>
</template>
<script setup>
import * as chatApi from "@/http/php/chat";
import { onReachBottom, onLoad } from "@dcloudio/uni-app";
import { reactive, ref, watch } from "vue";
import { onShow } from "@dcloudio/uni-app";
import go from "@/commons/utils/go.js";
import * as chatCouponApi from "@/http/api/market/chat";
import { useChatStore } from "@/store/chat";
const chatStore = useChatStore();
chatStore.onReceiveMsg = (msg) => {};
chatStore.connectSocket();
function toDetail(item) {
uni.setStorageSync("cach_couponActivity", item);
go.to("PAGES_CHAT_COUPON_ACTIVITY_DETAIL", {
id: item.id,
});
}
const tabs = reactive({
list: [
{
label: "全部",
value: "",
},
{
label: "发放中",
value: 1,
},
{
label: "已失效",
value: 3,
},
],
selIndex: 0,
});
const query = reactive({
page: 1,
size: 10,
});
function cancelChatCoupon(item) {
chatCouponApi.chatCouponExpired(item.id).then((res) => {
if (res) {
uni.showToast({
title: "操作成功",
icon: "none",
});
refresh();
} else {
uni.showToast({
title: "取消失败",
icon: "none",
});
}
});
}
function sendMsg(msg) {
chatStore.sendMessage({
to_id: groupInfo.value.id,
to_user_type: groupInfo.value.id,
chat_type: 2,
content: msg.value,
image_url: "",
order_id: "",
session_id: options.session_id,
...msg,
});
}
function toShare(item) {
const hasGet=item.couponJson.giveNum-item.couponJson.leftNum
sendMsg({
coupon: { ...item.couponJson, title: item.title,activity_id:item.id, hasGet:hasGet<=0?0:hasGet} ,
chat_coupon_id:item.id,
msg_type: 4,
});
uni.navigateBack({
delta: 2,
});
}
const options = reactive({});
const groupInfo = ref({});
onLoad((opt) => {
Object.assign(options, opt);
chatApi
.groupInfo({
group_id: options.group_id,
})
.then((res) => {
groupInfo.value = res || {};
});
});
function cancel(item) {
uni.showModal({
title: "提示",
content: "确定取消该优惠券吗?",
confirmText: "确定",
cancelText: "取消",
success: function (res) {
if (res.confirm) {
cancelChatCoupon(item);
} else if (res.cancel) {
console.log("用户点击取消");
}
},
});
}
function returnStateText(item) {
if (item.status == 1) {
return "发放中";
} else if (item.status == 3) {
return "已失效";
}
}
function tabsChange(e) {
tabs.selIndex = e;
refresh();
}
function refresh() {
query.page = 1;
isEnd.value = false;
init();
}
const list = ref([]);
const isEnd = ref(false);
function init() {
chatCouponApi
.chatCouponPage({ ...query, status: tabs.list[tabs.selIndex].value })
.then((res) => {
console.log(res);
const arr = (res.records || []).map((v) => {
return {
...v,
couponJson: JSON.parse(v.couponJson),
};
});
if (query.page == 1) {
list.value = arr;
} else {
list.value.push(...arr);
}
console.log(list.value);
isEnd.value = query.page >= res.totalPage * 1;
});
}
onReachBottom(() => {
if (!isEnd.value) {
query.page++;
init();
}
});
onShow(init);
</script>
<style scoped lang="scss">
.top {
padding: 20rpx 62rpx;
background-color: #fff;
}
.list {
padding: 32rpx 28rpx;
.item {
padding: 32rpx;
background-color: #fff;
margin-bottom: 28rpx;
.desc {
padding: 32rpx 28rpx;
background: #f8f8f8;
font-size: 24rpx;
}
.status {
padding: 4rpx 12rpx;
border-radius: 8rpx;
font-size: 24rpx;
&.status1 {
color: rgba(52, 199, 89, 1);
background-color: rgba(52, 199, 89, 0.15);
}
&.status3 {
color: #999999;
background-color: rgba(153, 153, 153, 0.15);
}
}
.Id {
padding: 8rpx 6rpx;
margin-left: 36rpx;
font-size: 18rpx;
color: #999;
border-radius: 4rpx;
background-color: #f8f8f8;
}
}
}
.btn {
padding: 8rpx 42rpx;
border-radius: 100rpx;
font-size: 28rpx;
font-weight: 700;
&.cancel {
background-color: #f8f8f8;
color: #999999;
}
&.primary {
background-color: $my-main-color;
color: #fff;
}
&.share {
background-color: #ecf5ff;
color: $my-main-color;
border: 1px solid $my-main-color;
}
}
</style>

View File

@@ -1,250 +0,0 @@
<template>
<view class="min-page bg-f7 u-font-28">
<view class="user-list bg-fff">
<view class="u-flex u-row-between u-col-center">
<text class="color-000">群成员{{ allUser.length }}</text>
<text class="color-red" @click="showRemove = !showRemove">移除</text>
</view>
<view class="list u-m-t-26">
<view
class="u-flex u-flex-col u-row-center u-col-center relative"
v-for="(item, index) in userLists"
:key="index"
>
<up-avatar
size="104rpx"
shape="square"
:src="item.avatar"
round="8rpx"
></up-avatar>
<view class="u-m-t-8 color-000 u-line-1" style="max-width: 104rpx">{{
item.nick_name
}}</view>
<view
class="remove u-absolute"
v-if="showRemove && item.role != 1"
@click="removeMember(item)"
>
<up-icon
name="minus-circle-fill"
size="24rpx"
color="#FF2424"
></up-icon>
</view>
</view>
</view>
<view class="u-flex u-row-center color-666 u-m-t-30" v-if="hasMore">
<view class="u-flex" @click="loadMore">
<text class="u-m-r-20">查看更多</text>
<up-icon name="arrow-down" size="24rpx" color="#666"></up-icon>
</view>
</view>
</view>
<view class="bottom">
<view
class="u-flex u-row-between default-padding bg-fff border-bottom"
@click="toEditTitle"
>
<text>群聊名称</text>
<view class="u-flex color-666">
<text>{{ groupInfo.name }}</text>
<up-icon name="arrow-right" size="24rpx" color="#666"></up-icon>
</view>
</view>
<view class="u-flex u-row-between default-padding bg-fff border-bottom">
<view>
<view>禁言</view>
<view class="color-999 u-font-24">
开启后顾客将不能在群内发消息</view
>
</view>
<up-switch
v-model="groupInfo.is_mute"
:active-value="1"
:inactive-value="0"
@change="groupMuteChange"
></up-switch>
</view>
<view
class="u-flex u-row-between default-padding bg-fff"
@click="
go.to('PAGES_CHAT_COUPON_ACTIVITY', {
group_id: options.group_id,
session_id: options.session_id,
})
"
>
<text>优惠券领取记录</text>
<view class="u-flex color-666">
<text class="color-main">去查看</text>
<up-icon name="arrow-right" size="24rpx" color="#318AFE"></up-icon>
</view>
</view>
</view>
<Modal v-model="modalData.show" title="修改群名称" @confirm="modalConfirm">
<view class="u-p-40">
<view class="font-bold u-m-b-16">群名称</view>
<up-input
v-model="modalData.form.title"
placeholder="请输入群名称"
maxlength="20"
></up-input>
</view>
</Modal>
</view>
</template>
<script setup>
import go from "@/commons/utils/go.js";
import * as chatApi from "@/http/php/chat";
import { onShow, onLoad } from "@dcloudio/uni-app";
import { reactive, toRefs, computed, watch, onMounted, ref } from "vue";
import Modal from "@/pageChat/components/modal.vue";
const options = reactive({});
const groupInfo = ref({});
const modalData = reactive({
show: false,
form: {
title: "",
},
key: "editTitle",
});
function toEditTitle() {
modalData.show = true;
modalData.form.title = groupInfo.value.name || "";
}
function modalConfirm(e) {
if (modalData.key == "editTitle") {
if (!modalData.form.title.trim().length) {
uni.showToast({
title: "请输入群名称",
icon: "none",
});
return;
}
chatApi
.groupEditTitle({
group_id: options.group_id,
title: modalData.form.title,
})
.then((res) => {
if (res) {
groupInfo.value.name = modalData.form.title;
modalData.show = false;
uni.showToast({
title: "修改成功",
icon: "none",
});
}
});
}
}
function getGroupInfo() {
chatApi
.groupInfo({
group_id: options.group_id,
})
.then((res) => {
groupInfo.value = res || {};
});
}
onLoad((opt) => {
Object.assign(options, opt);
getGroupInfo();
});
function groupMuteChange(e) {
if (e) {
chatApi
.groupMute({
group_id: options.group_id,
})
.then((res) => {
if (res) {
uni.showToast({
title: "已禁言",
icon: "none",
});
}
});
return;
}
chatApi
.groupMunute({
group_id: options.group_id,
})
.then((res) => {
if (res) {
uni.showToast({
title: "禁言已取消",
icon: "none",
});
}
});
}
const showRemove = ref(false);
let allUser = ref([]);
const userLists = ref([]);
const hasMore = ref(false);
function getMembers() {
chatApi.groupMembers({ group_id: options.group_id }).then((res) => {
allUser.value = res.user_list || [];
hasMore.value = allUser.value.length > 20;
userLists.value = allUser.value.slice(0, 20);
});
}
function loadMore() {
userLists.value = allUser.value;
}
onShow(() => {
getMembers();
});
function removeMember(item) {
chatApi
.groupKick({ group_id: options.group_id, target_uid: item.user_id })
.then((res) => {
if (res) {
uni.showToast({
title: "移除成功",
icon: "none",
});
setTimeout(() => {
getMembers();
}, 1000);
}
});
}
</script>
<style lang="scss" scoped>
.user-list {
padding: 32rpx 28rpx;
.list {
gap: 20rpx;
display: grid;
grid-template-columns: repeat(5, 1fr);
}
}
.color-red {
color: #ff2424;
}
.bottom {
margin: 28rpx;
border-radius: 16rpx;
overflow: hidden;
}
.default-padding {
padding: 32rpx 28rpx;
}
.relative {
position: relative;
}
.remove {
top: -6rpx;
right: -6rpx;
}
</style>

View File

@@ -1,303 +0,0 @@
<template>
<view class="min-page bg-f7 color-333 u-font-28">
<up-sticky>
<view class="top u-flex u-row-between u-col-center">
<view style="width: 420rpx">
<up-search
v-model="query.key"
placeholder="搜索群名称"
:showAction="false"
@clear="throttleSearch"
@change="throttleSearch"
></up-search>
</view>
<view class="u-flex u-col-center" @click="clearAllmsg">
<text class="color-666 u-m-r-12">清空未读</text>
<image src="/pageChat/static/clear.png" class="clear"></image>
</view>
</view>
</up-sticky>
<view class="list">
<up-swipe-action>
<template v-for="(item, index) in list" :key="item.id">
<up-swipe-action-item
:options="options1"
v-model:show="item.showOptions"
@click="optionsClick($event, item, index)"
>
<view class="item u-flex" @click="toDetail(item)">
<view class="u-flex avatar">
<up-avatar
size="118rpx"
:src="item.avatar"
shape="square"
round="8rpx"
></up-avatar>
<view class="bandage" v-if="item.unread_count > 0">{{
item.unread_count >= 99 ? "99" : item.unread_count
}}</view>
</view>
<view class="u-flex-1 u-flex u-row-between u-p-l-14">
<view style="max-width: 364rpx">
<view class="color-000 u-line-1">{{ item.name }}</view>
<view class="u-m-t-28 u-line-1 u-font-24 color-999">{{
item.msg
}}</view>
</view>
<view class="color-333 u-font-24">{{ item.send_time }}</view>
</view>
</view>
</up-swipe-action-item>
<view style="height: 16rpx" class="bg-f7"></view>
</template>
</up-swipe-action>
</view>
</view>
</template>
<script setup>
import * as chatApi from "@/http/php/chat";
import { ref, reactive, onMounted, onUnmounted } from "vue";
import { onShow } from "@dcloudio/uni-app";
import { useChatStore } from "@/store/chat";
import dayjs from "dayjs";
const chatStore = useChatStore();
const originalOnReceiveMsg = chatStore.onReceiveMsg;
// 定义消息回调函数
const handleReceiveMsg = (msg) => {
// 先执行原始回调(如果有)
if (typeof originalOnReceiveMsg === "function") {
originalOnReceiveMsg(msg);
}
if (msg.operate_type == "receive_msg" && msg.chat_type == 2) {
const index = allList.value.findIndex((v) => v.group_id == msg.group_id);
if (index != -1) {
allList.value[index].unread_count += 1;
allList.value[index].msg = returnMsg(msg);
allList.value[index].send_time = msg.send_time;
allList.value[index].send_time_origin = msg.send_time_origin;
allList.value = listSort(allList.value);
}
const index1 = list.value.findIndex((v) => v.group_id == msg.group_id);
if (index1 != -1) {
list.value[index1].unread_count += 1;
list.value[index1].msg = returnMsg(msg);
list.value[index1].send_time = msg.send_time;
list.value[index1].send_time_origin = msg.send_time_origin;
list.value = listSort(list.value);
}
}
};
onMounted(() => {
chatStore.registerReceiveMsgCallback(handleReceiveMsg);
});
onUnmounted(() => {
// 移除消息回调,避免内存泄漏
chatStore.removeReceiveMsgCallback(handleReceiveMsg);
});
function returnMsg(msg) {
if (msg.msg_type == 1) {
return msg.nick + "" + msg.content;
}
if (msg.msg_type == 2) {
return msg.nick + "" + "[图片]";
}
if (msg.msg_type == 5) {
return msg.nick + "" + "[视频]";
}
if (msg.msg_type == 4) {
return msg.nick + "" + "[优惠券]";
}
}
chatStore.connectSocket();
// 使用 reactive 创建响应式对象
const options1 = reactive([
{
text: "删除",
style: {
backgroundColor: "#f56c6c",
color: "#fff",
},
},
]);
function optionsClick(e, item, index) {
if (e.index == 0) {
//删除
chatApi
.sessionlistdel({
session_id: item.session_id,
})
.then((res) => {
if (res) {
uni.showToast({
title: "删除成功",
icon: "none",
duration: 1000,
});
list.value.splice(index, 1);
setTimeout(() => {
getList();
}, 1000);
}
});
}
}
const list = ref([]);
let allList = ref([]);
const query = reactive({
key: "",
});
function throttle(fn, delay) {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
//使用节流函数
const throttleSearch = throttle(search, 500);
function search() {
const arr = allList.value
.filter((v) => v.name.includes(query.key.trim()))
.map((v) => {
return {
...v,
showOptions: false,
};
});
list.value = listSort(arr);
}
function listSort(arr) {
return arr.sort((a, b) => {
return dayjs(b.send_time_origin).unix() - dayjs(a.send_time_origin).unix();
});
}
async function getList() {
const res = await chatApi.messageSessionList({});
const arr = (res.list || []).filter((v) => !v.is_del);
allList.value = listSort(arr);
search();
}
async function clearAllmsg() {
uni.showModal({
title: "提示",
content: "确定要清空所有未读消息吗?",
success: async (res) => {
if (res.confirm) {
const res = await chatApi.messageMarkReadAll({
session_ids: list.value.map((v) => v.group_id).join(","),
});
if (res) {
uni.showToast({
title: "清空成功",
icon: "none",
duration: 2000,
});
setTimeout(() => {
getList();
}, 1000);
}
}
},
});
}
function toDetail(item) {
if (!item.is_th) {
return uni.showToast({
title: "你已不在该群内",
icon: "none",
duration: 1500,
});
}
uni.navigateTo({
url:
"/pageChat/chat" +
`?group_id=${item.group_id}&session_id=${item.session_id}`,
});
}
const messageUnreadCount = ref(0);
onShow(() => {
getList();
// 检查连接状态,确保连接活跃
if (!chatStore.isConnect || !chatStore.socketTask) {
console.log("列表页显示检查Socket连接");
chatStore.forceReconnect();
} else {
chatStore.shop_id = "";
chatStore.init();
}
// 获取未读消息总数
chatApi.messageUnreadCount({}).then((res) => {
console.log(res);
messageUnreadCount.value = res.total;
});
});
</script>
<style lang="scss" scoped>
.top {
padding: 32rpx 28rpx;
background-color: #fff;
}
.clear {
width: 38rpx;
height: 38rpx;
}
.list {
padding: 36rpx 28rpx;
.item {
padding: 32rpx 28rpx;
background-color: #fff;
border-radius: 16rpx;
.avatar {
position: relative;
.bandage {
position: absolute;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 50%;
background: #ff1c1c;
font-size: 24rpx;
color: #ffffff;
width: 38rpx;
height: 38rpx;
text-align: center;
line-height: 38rpx;
right: -10rpx;
top: -10rpx;
}
}
}
}
:deep(.u-swipe-action-item__content) {
background-color: transparent;
}
:deep(.u-swipe-action-item) {
border-radius: 16rpx;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Some files were not shown because too many files have changed in this diff Show More