2 Commits
prod ... 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
536 changed files with 17260 additions and 81292 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,162 +0,0 @@
<template>
<view class="upload-file" @click="chooseImage" :style="returnStyle('box')">
<slot v-if="$slots.default"></slot>
<view class="icon" v-if="!modelValue">+</view>
<image class="img" v-else :src="modelValue" :style="returnStyle('img')"></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 {
ref
} from 'vue';
const props = defineProps({
size: {
default: ''
},
// 图片最大上传大小单位M默认undefined不限制大小
maxSize: {
type: [Number, String], // 支持数字如2或字符串如"2")传参
default: undefined,
validator: (value) => {
// 校验传参必须是大于0的数字转换后否则视为不限制
const numValue = Number(value);
return value === undefined || (!isNaN(numValue) && numValue > 0);
}
}
})
function returnStyle(type) {
let size = null
if (!props.size) {
return
}
if (Number(props.size) === NaN) {
size = props.size
} else {
size = props.size + 'rpx'
}
return {
width: size,
height: size
}
}
const modelValue = defineModel({
type: String,
default: ''
});
const emits = defineEmits('uploadSuccess')
// ------------- 新增2工具函数将M转换为字节1M = 1024 * 1024 字节) -------------
/**
* 转换文件大小单位M → 字节)
* @returns {number} 最大允许的文件字节数返回0表示不限制
*/
function getMaxFileSizeInBytes() {
if (props.maxSize === undefined) return 0; // 未传参返回0不限制
const maxSizeNum = Number(props.maxSize);
// 无效数值返回0不限制
if (isNaN(maxSizeNum) || maxSizeNum <= 0) return 0;
// 转换公式1M = 1024KB1KB = 1024字节
return maxSizeNum * 1024 * 1024;
}
// ------------- 修改1在上传前增加文件大小校验 -------------
function chooseImage() {
uni.chooseImage({
count: 1, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'],
success: async function(res) {
// 1. 获取文件信息(大小、临时路径等)
const tempFile = res.tempFiles[0];
const maxFileSize = getMaxFileSizeInBytes();
// 2. 校验文件大小仅当maxFileSize>0时才校验
if (maxFileSize > 0 && tempFile.size > maxFileSize) {
uni.showToast({
title: `图片大小不能超过${props.maxSize}M`,
icon: 'none'
});
return; // 校验失败,终止后续上传逻辑
}
// 3. 校验通过,执行上传
uni.showLoading({
title: '上传中'
});
console.log(res);
const fileRes = await uploadFile(tempFile);
uni.hideLoading();
if (fileRes) {
modelValue.value = fileRes;
emits('uploadSuccess', 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,222 +0,0 @@
<template>
<view>
<view class="box" @click.stop="openPopup">
<text class="u-font-28 color-999 u-p-r-16" v-if="!modelValue||list.length==0">请选择用户</text>
<text class="u-font-28 color-333 u-p-r-16 u-line-1" v-else :style="{
maxWidth: maxWidth+'rpx'
}">{{returnLabel()}}</text>
<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>
<view class="u-m-t-24">
<up-search v-model="query.key" @clear="search" @custom="search" @search="search"></up-search>
</view>
<scroll-view scroll-with-animation :scroll-into-view="selId" class="scroll-view u-m-t-30"
scroll-y="true" style="max-height :60vh;" @scrolltolower="scrolltolower">
<view class="u-m-b-10 u-flex item" v-for="item in list" :key="item.id" @click="itemClick(item)"
:id="'item_'+item.id" :class="{active:selItem&&selItem.id==item.id}">
<view class="checkbox">
<up-icon name="checkbox-mark" color="#fff"></up-icon>
</view>
<view class="u-flex-1">{{item.nickName}}/{{item.phone}}</view>
</view>
<template v-if="search.key!==''">
<up-empty v-if="list.length==0" text="未搜到相关用户"></up-empty>
<up-loadmore v-else :status="isEnd?'nomore':'loading'"></up-loadmore>
</template>
<template v-else>
<up-empty v-if="list.length==0" text="暂无用户"></up-empty>
<up-loadmore v-else :status="isEnd?'nomore':'loading'"></up-loadmore>
</template>
</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 {
computed,
onMounted,
reactive,
ref,
watch
} from 'vue';
import {
getShopUser
} from '@/http/api/market/index.js';
const customStyle = ref({
marginRight: '20px'
});
const show = ref(false);
let modelValue = defineModel('modelValue', {
default: '',
});
const props = defineProps({
maxWidth: {
default: 240
}
})
const selId = ref('')
function returnLabel() {
const findItem = list.value.find(v => v.id == modelValue.value)
if (findItem) {
return findItem.nickName + (findItem.phone ? ('/' + findItem.phone) : '')
}
return ''
}
const selItem = ref(null)
function itemClick(item) {
selItem.value = item
}
function returnShopName(id) {
const item = list.value.find((v) => v.id == id);
return item?.shopName || '';
}
function close() {
show.value = false;
}
const emits = defineEmits(['change'])
function submit() {
if (!selItem.value) {
return uni.showToast({
title: '请选择用户'
})
}
if (modelValue.value != selItem.value.id) {
modelValue.value=selItem.value.id
emits('change')
}else{
modelValue.value=selItem.value.id
}
show.value = false;
}
const list = ref([]);
function openPopup() {
selId.value = 'item_' + modelValue.value
init()
show.value = true;
}
const isEnd = ref(false)
const query = reactive({
page: 1,
size: 10,
key: ''
})
function search() {
isEnd.value = false
query.page = 1
selItem.value = null
init()
}
async function init() {
const res = await getShopUser(query);
if (res) {
isEnd.value = query.page >= res.totalPage * 1
if (query.page == 1) {
list.value = res.records
} else {
list.value.push(...res.records)
}
}
}
function scrolltolower() {
if (!isEnd.value) {
query.page++
init()
}
}
</script>
<style lang="scss">
.box {
border-radius: 8upx;
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;
}
.scroll-view {
.item {
border: 1px solid #eee;
padding: 20rpx;
border-radius: 12rpx;
&.active {
border-color: $my-main-color;
}
}
}
.checkbox {
margin-right: 10rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 6rpx;
border: 1px solid #999;
}
.item {
&.active {
.checkbox {
background-color: $my-main-color;
border-color: $my-main-color;
}
}
}
</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,108 +1,29 @@
//当前环境 test,prod
export const ENV = 'test'
export const ENV_BASE_URL = {
java: {
prod: 'https://cashier.sxczgkj.com/',
test: 'http://192.168.1.42/',
h5ProdProxy: '/prodJavaApi/',
h5TestProxy: '/testJavaApi/',
},
php: {
prod: 'https://cashier.sxczgkj.com/',
test: 'http://192.168.1.42:8787/',
h5ProdProxy: '/prodPhpApi/api/',
h5TestProxy: '/testPhpApi/api/',
}
}
/**
* @param {String} env 环境,测试或者正式
* @param {String} apiType 语言java或者php
*/
export function returnBaseUrl(param) {
let {
env,
apiType
} = param
if(!env){
env=ENV
}
console.log('env', env);
console.log('apiType', apiType);
if (env === 'prod') {
//正式环境
// #ifdef H5
if (apiType === 'php') {
return ENV_BASE_URL.php.h5ProdProxy
}
if (apiType === 'java') {
return ENV_BASE_URL.java.h5ProdProxy
}
// #endif
if (apiType === 'php') {
return ENV_BASE_URL.php.prod
}
if (apiType === 'java') {
return ENV_BASE_URL.java.prod
}
} else {
//测试环境
// #ifdef H5
if (apiType === 'php') {
return ENV_BASE_URL.php.h5TestProxy
}
if (apiType === 'java') {
return ENV_BASE_URL.java.h5TestProxy
}
// #endif
if (apiType === 'php') {
return ENV_BASE_URL.php.test
}
if (apiType === 'java') {
return ENV_BASE_URL.java.test
}
}
}
const appConfig = {
// 项目名称
appName: '银收客',
// token取值key
tokenKey: 'iToken',
// tokenKey: 'satoken',
// 环境变量相关
env: {},
// wss: "wss://sockets.sxczgkj.com/wss", //测试环境
wss: "wss://czgeatws.sxczgkj.com/wss", //正式环境
// 环境变量常量
ENV_ENUM: {
DEVELOPMENT: 'development', // 本地调试地址
DEVELOPMENT: 'development', // 本地调试地址
TEST: 'test', // 测试地址
DEMO: 'demo', // 演示环境
PRODUCTION: 'production' // 生产环境
PRODUCTION: 'production' // 生产环境
},
returnBaseUrl: returnBaseUrl,
storeEnvEnumKey: 'currentEnvEnum', // 本地存储的envkey的值
encryptKey: '1234567890123456', // http数据加解密的key
baseUrl: "",
encryptKey: '1234567890123456' // http数据加解密的key
}
export default appConfig;

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,545 +0,0 @@
<template>
<view class="min-page bg-f7 u-font-28 color-333">
<steps v-model="step" @itemClick="stepItemClick" />
<view class="u-m-t-32">
<basicInfo v-if="step==0" :data="form.merchantBaseInfo" :maxSize="maxSize"
@update="update($event,'merchantBaseInfo')">
</basicInfo>
<legalPerpoleInfo v-if="step==1" :maxSize="maxSize" :data="form.legalPersonInfo"
@update="update($event,'legalPersonInfo')">
</legalPerpoleInfo>
<businessLicenceInfo v-if="step==2" :maxSize="maxSize" :data="form.businessLicenceInfo"
@update="update($event,'businessLicenceInfo')"></businessLicenceInfo>
<storeInfo v-if="step==3" :maxSize="maxSize" :data="form.storeInfo" @update="update($event,'storeInfo')">
</storeInfo>
<settlementInfo v-if="step==4" :maxSize="maxSize" :data="form.settlementInfo"
@update="update($event,'settlementInfo')">
</settlementInfo>
</view>
<bottomBtnGroup @save="saveClick" @cancel="cancelClick" :cancelText="cancelText" :confirmText="confirmText">
</bottomBtnGroup>
</view>
</template>
<script setup>
import steps from './components/steps.vue'
import basicInfo from './components/basic-info.vue'
import legalPerpoleInfo from './components/legalPerpole-info.vue'
import businessLicenceInfo from './components/business-licence-info.vue'
import storeInfo from './components/store-info.vue'
import settlementInfo from './components/settlement-info.vue'
import bottomBtnGroup from './components/bottom-btn-group.vue'
import dayjs from 'dayjs'
import {
onLoad
} from '@dcloudio/uni-app'
import {
rules,
isEmptyValue,
returnKey,
getNestedValue,
verifyValue,
verifyData
} from './data.js'
import {
reactive,
ref,
watch,
onMounted,
computed
} from 'vue';
const form = reactive({
"shopId": '',
"merchantCode": '',
"merchantBaseInfo": {
"userType": "0",
"shortName": "",
"mccCode": "",
"alipayAccount": "",
"contactPersonType": "LEGAL",
"contactName": "",
"certType": "0",
"contactPersonId": "",
"contactPersonIdStartDate": dayjs().valueOf(),
"contactPersonIdEndDate": dayjs().add(10, 'year').valueOf(),
"contactIdCardBackPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"contactIdCardFrontPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"contactPhone": "",
"contactAddr": "",
"contactEmail": "",
"companyChildType": "1"
},
"legalPersonInfo": {
"legalPersonName": "",
"legalPersonId": "",
"legalIdPersonStartDate": dayjs().valueOf(),
"legalPersonIdEndDate": dayjs().add(10, 'year').valueOf(),
"legalPersonPhone": "",
"legalPersonEmail": "",
"legalGender": "",
"legalAddress": "",
"idCardHandPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"idCardFrontPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"idCardBackPic": {
"url": "",
"wechatId": "",
"alipayId": ""
}
},
"businessLicenceInfo": {
"licenceName": "",
"licenceNo": "",
"licenceStartDate": dayjs().valueOf(),
"licenceEndDate": dayjs().add(10, 'year').valueOf(),
"registeredAddress": "",
"licensePic": {
"url": "",
"wechatId": "",
"alipayId": ""
}
},
"storeInfo": {
"mercProvCode": "",
"mercCityCode": "",
"mercAreaCode": "",
"mercProv": "",
"mercCity": "",
"mercArea": "",
"businessAddress": "",
"insidePic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"doorPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"cashierDeskPic": {
"url": "",
"wechatId": "",
"alipayId": ""
}
},
"settlementInfo": {
"settlementType": "0",
"noLegalName": "",
"noLegalId": "",
"settlementCardType": "21",
"settlementCardNo": "",
"settlementName": "",
"bankMobile": "",
"openAccProvinceId": "",
"openAccCityId": "",
"openAccAreaId": "",
"openAccProvince": "",
"openAccCity": "",
"openAccArea": "",
"bankName": "",
"bankInstId": "",
"bankType": "",
"bankBranchName": "",
"bankBranchCode": "",
"bankCardFrontPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"bankCardBackPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"openAccountLicencePic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"noLegalHandSettleAuthPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"noLegalSettleAuthPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"noLegalIdCardFrontPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"noLegalIdCardBackPic": {
"url": "",
"wechatId": "",
"alipayId": ""
}
}
})
import {
addEntryManager,
getEntryManager
} from '@/http/api/order/entryManager.js'
onLoad((opt) => {
if (opt.licenceNo && opt.shopId) {
getEntryManager(opt).then(res => {
res.merchantBaseInfo.contactPersonIdEndDate = dayjs(res.merchantBaseInfo
.contactPersonIdEndDate).valueOf()
res.merchantBaseInfo.contactPersonIdStartDate = dayjs(res.merchantBaseInfo
.contactPersonIdStartDate).valueOf()
res.legalPersonInfo.legalIdPersonStartDate = dayjs(res.legalPersonInfo
.legalIdPersonStartDate).valueOf()
res.legalPersonInfo.legalPersonIdEndDate = dayjs(res.legalPersonInfo
.legalPersonIdEndDate).valueOf()
res.businessLicenceInfo.licenceStartDate = dayjs(res.businessLicenceInfo
.licenceStartDate).valueOf()
res.businessLicenceInfo.licenceEndDate = dayjs(res.businessLicenceInfo
.licenceEndDate).valueOf()
Object.assign(form, res)
})
} else {
// const data = uni.getStorageSync('entryManager_submit_data')
// if (data) {
// Object.assign(form, data)
// }
form.shopId = opt.shopId || ''
}
watch(() => form, (newval) => {
uni.setStorageSync('entryManager_submit_data', form)
}, {
deep: true,
immediate: true
})
})
function returnSettlementInfoRule() {
let rule = {
...rules.settlementInfo
}
if (form.settlementType * 1 == 0) {
rule = {
...rule,
'noLegalHandSettleAuthPic.url': {
required: true,
errorMsg: '请上传非法人手持结算授权书',
},
'noLegalSettleAuthPic.url': {
required: true,
errorMsg: '请上传非法人结算授权书',
},
'noLegalIdCardFrontPic.url': {
required: true,
errorMsg: '请上传非法人身份证正面',
},
'noLegalIdCardBackPic.url': {
required: true,
errorMsg: '请上传非法人身份证反面',
},
noLegalName: {
required: true,
errorMsg: '请填写非法人姓名',
},
noLegalName: {
required: true,
errorMsg: '请填写非法人身份证号码',
}
}
}
if (form.settlementCardType * 1 == 21) {
rule = {
'bankCardBackPic.url': {
required: true,
errorMsg: '请上传银行卡反面照片',
}
}
}
return rule
}
function returnMerchantBaseInfoRule() {
let rule = rules.merchantBaseInfo
if (form.merchantBaseInfo.contactPersonType == 'SUPER') {
rule = {
...rule,
'contactIdCardFrontPic.url': {
required: true,
errorMsg: '请上传联系人身份证正面照片',
},
'contactIdCardBackPic.url': {
required: true,
errorMsg: '请上传联系人身份证反面照片',
},
contactName: {
required: true,
errorMsg: '请填写联系人姓名',
},
contactPersonId: {
required: true,
errorMsg: '请填写联系人身份证号',
},
contactPersonIdStartDate: {
required: true,
errorMsg: '请填写联系人身份证开始日期',
},
contactPersonIdEndDate: {
required: true,
errorMsg: '请填写联系人身份证到期日期',
},
contactPhone: {
required: true,
errorMsg: '请填写联系人电话',
},
contactAddr: {
required: true,
errorMsg: '请填写联系人通讯地址',
},
contactEmail: {
required: true,
errorMsg: '请填写联系人邮箱',
}
}
}
console.log('rule', rule);
return rule
}
const maxSize = ref(2)
const step = ref(0)
const ruleArr=['merchantBaseInfo','legalPersonInfo','businessLicenceInfo','storeInfo','settlementInfo']
function stepItemClick(newStep) {
const arr=ruleArr.slice(0,newStep-1)
let isPas = true
let result = null
for (let index in arr) {
const key=arr[index]
if (key == 'settlementInfo') {
result = verifyData(form[key], returnSettlementInfoRule())
} else if (key == 'merchantBaseInfo') {
result = verifyData(form[key], returnMerchantBaseInfoRule())
} else {
result = verifyData(form[key], rules[key])
}
if (!result.ispas) {
uni.showToast({
title: result.errorMsg || '请完善必填内容',
icon: 'none'
})
result.step = index
return result
}
}
step.value = newStep
}
function verifyForm(step = null) {
let result = {
ispas: true,
errorMsg: '',
step: -1,
}
if (step != null) {
if (step == 0) {
result = verifyData(form.merchantBaseInfo, returnMerchantBaseInfoRule())
result.step = 0;
}
if (step == 1) {
result = verifyData(form.legalPersonInfo, rules.legalPersonInfo)
result.step = 1;
}
if (step == 2) {
result = verifyData(form.businessLicenceInfo, rules.businessLicenceInfo)
result.step = 2;
}
if (step == 3) {
result = verifyData(form.storeInfo, rules.storeInfo)
result.step = 3;
}
if (step == 4) {
result = verifyData(form.storeInfo, returnSettlementInfoRule())
result.step = 4;
}
} else {
let isPas = true
let errorMsg = '';
let result = null
let index = -1;
for (let key in rules) {
index++
if (key == 'settlementInfo') {
result = verifyData(form[key], returnSettlementInfoRule())
} else if (key == 'merchantBaseInfo') {
result = verifyData(form[key], returnMerchantBaseInfoRule())
} else {
result = verifyData(form[key], rules[key])
}
if (!result.ispas) {
uni.showToast({
title: errorMsg || '请完善必填内容',
icon: 'none'
})
result.step = index
return result
}
}
}
return result;
}
function saveClick() {
if (step.value == 4) {
const {
ispas,
errorMsg,
step: errorStep
} = verifyForm()
console.log(form.settlementInfo);
if (!ispas) {
uni.showToast({
title: errorMsg || '请完善必填内容',
icon: 'none'
})
step.value = errorStep
return
}
}
if (step.value < 4) {
const {
ispas,
errorMsg,
step: errorStep
} = verifyForm(step.value)
console.log('ispas', ispas);
console.log('errorMsg', errorMsg);
console.log('errorStep', errorStep);
if (!ispas) {
uni.showToast({
title: errorMsg || '请完善必填内容',
icon: 'none'
})
return
}
}
if (step.value != 4) {
step.value++
return
}
const merchantBaseInfo = {
...form.merchantBaseInfo,
contactPersonIdEndDate: dayjs(form.merchantBaseInfo.contactPersonIdEndDate).format('YYY-MM-DD'),
contactPersonIdStartDate: dayjs(form.merchantBaseInfo.contactPersonIdStartDate).format('YYY-MM-DD'),
}
const legalPersonInfo = {
...form.legalPersonInfo,
legalIdPersonStartDate: dayjs(form.legalPersonInfo.legalIdPersonStartDate).format('YYYY-MM-DD'),
legalPersonIdEndDate: dayjs(form.legalPersonInfo.legalPersonIdEndDate).format('YYYY-MM-DD'),
}
const businessLicenceInfo = {
...form.businessLicenceInfo,
licenceStartDate: dayjs(form.businessLicenceInfo.licenceStartDate).format('YYYY-MM-DD'),
licenceEndDate: dayjs(form.businessLicenceInfo.licenceEndDate).format('YYYY-MM-DD'),
}
addEntryManager({
...form,
merchantBaseInfo,
legalPersonInfo,
businessLicenceInfo
}).then(res => {
if (res) {
uni.showToast({
title: '提交成功',
})
uni.removeStorageSync('entryManager_submit_data')
setTimeout(() => {
uni.navigateBack()
}, 1000)
} else {
uni.showToast({
title: '提交失败',
icon: 'error'
})
}
})
return
}
function cancelClick() {
if (step.value == 0) {
return uni.navigateBack()
}
step.value--;
}
function update(value, key) {
if (form.hasOwnProperty(key)) {
form[key] = value
}
}
const cancelText = computed(() => {
if (step.value == 0) {
return '返回'
}
return '上一步'
})
const confirmText = computed(() => {
if (step.value == 4) {
return '提交'
}
return '下一步'
})
onMounted(() => {
})
</script>
<style lang="scss" scoped>
.min-page {
padding: 32rpx 28rpx;
}
.container {
padding: 32rpx 28rpx;
border-radius: 16rpx;
margin-bottom: 32rpx;
background-color: #fff;
}
</style>

View File

@@ -1,253 +0,0 @@
<template>
<view>
<view class="box" @click.stop="openPopup">
<text class="u-font-28 color-999 u-p-r-16" v-if="!modelValue">请选择</text>
<text class="u-font-28 color-333 u-p-r-16" v-else>{{returnLabel()}}</text>
<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>
<view class="u-m-t-24">
<up-search v-model="query.bankName" @search="search" @change="bankNameChange" @custom="search"
@clear="search"></up-search>
</view>
<scroll-view @scrolltolower="scrolltolower" scroll-with-animation :scroll-into-view="selid"
class="scroll-view u-m-t-30" scroll-y="true" style="max-height :60vh;">
<view class="u-m-b-10 u-flex item" v-for="item in list" :key="item.id" @click="itemClick(item)"
:id="'shop_'+item.id" :class="{active:selItem&&selItem.id==item.id}">
<view class="checkbox">
<up-icon name="checkbox-mark" color="#fff"></up-icon>
</view>
<view class="u-flex-1">{{item.bankAlias}}</view>
</view>
<up-loadmore :status="isEnd?'nomore':'loading'"></up-loadmore>
</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 {
computed,
nextTick,
onMounted,
reactive,
ref,
watch
} from 'vue';
import {
bankInfo
} from '@/http/api/system/common.js';
const customStyle = ref({
marginRight: '20px'
});
const show = ref(false);
const modelValue = defineModel();
const bankInstId = defineModel('bankInstId');
const bankAliasCode=defineModel('bankAliasCode')
const wxProvinceCode=defineModel('wxProvinceCode')
const selid = ref('')
function returnLabel() {
const findShop = list.value.find(v => v.bankAlias == modelValue.value)
return findShop ? findShop.bankAlias : ''
}
const selItem = ref(null)
function itemClick(bank) {
selItem.value = bank;
}
function close() {
show.value = false;
}
function submit() {
modelValue.value = selItem.value.bankAlias
bankInstId.value = selItem.value.bankCode
bankAliasCode.value=selItem.value.bankAliasCode;
console.log('modelValue', modelValue.value);
console.log('bankInstId', bankInstId.value);
show.value = false;
}
function search() {
isEnd.value = false
query.page = 1
init()
}
const list = ref([]);
watch(() => modelValue.value, (newval) => {
if (newval) {
const findShop = list.value.find(v => v.bankAlias == modelValue.value)
if (findShop) {
selid.value = 'shop_' + findShop.id
selItem.value = findShop
bankInstId.value = findShop.bankCode
bankAliasCode.value = findShop.bankAliasCode
wxProvinceCode.value=findShop.wxProvinceCode
}
}
})
function openPopup() {
const findShop = list.value.find(v => v.bankAlias == modelValue.value)
if (findShop) {
selid.value = 'shop_' + findShop.id
selItem.value = findShop
}
show.value = true;
}
// --------------- 核心新增:节流函数实现 ---------------
/**
* 节流函数:限制函数在指定时间内只触发一次
* @param {Function} fn - 要节流的函数
* @param {number} delay - 节流延迟时间(毫秒)
* @returns {Function} 节流后的函数
*/
function throttle(fn, delay = 300) {
let timer = null; // 定时器标识
return function(...args) {
// 如果已有定时器,直接返回(未到触发时间)
if (timer) return;
// 执行函数并设置新定时器
fn.apply(this, args);
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
}, delay);
};
}
// --------------- 核心修改创建节流后的search方法 ---------------
// 300ms内只触发一次search可根据需求调整delay值如500ms
const throttledSearch = throttle(search, 300);
// --------------- 改造bankNameChange调用节流后的搜索 ---------------
function bankNameChange() {
// 输入变化时,调用节流后的搜索方法
throttledSearch();
}
const query = reactive({
page: 1,
size: 10,
bankName: ''
})
const isEnd = ref(false)
function scrolltolower() {
if (isEnd.value) {
return
}
query.page++
init()
}
async function init() {
const res = await bankInfo(query);
isEnd.value = query.page >= res.totalPage * 1 ? true : false
if (res) {
if (query.page == 1) {
list.value = res.records
} else {
list.value = list.value.concat(res.records)
}
}
}
onMounted(init);
</script>
<style lang="scss">
.box {
border-radius: 8upx;
display: flex;
flex-direction: row;
align-items: top;
flex-wrap: wrap;
padding: 20rpx 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;
}
.scroll-view {
.item {
border: 1px solid #eee;
padding: 20rpx;
border-radius: 12rpx;
&.active {
border-color: $my-main-color;
}
}
}
.checkbox {
margin-right: 10rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 6rpx;
border: 1px solid #999;
}
.item {
&.active {
.checkbox {
background-color: $my-main-color;
border-color: $my-main-color;
}
}
}
</style>

View File

@@ -1,292 +0,0 @@
<template>
<view>
<view class="box" @click.stop="openPopup">
<text class="u-font-28 color-999 u-p-r-16" v-if="!modelValue">请选择</text>
<text class="u-font-28 color-333 u-p-r-16" v-else>{{returnLabel()}}</text>
<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>
<view class="u-m-t-24">
<up-search v-model="keywords" @search="search" @change="search" @custom="search"
@clear="search"></up-search>
</view>
<scroll-view scroll-with-animation :scroll-into-view="selid" class="scroll-view u-m-t-30"
scroll-y="true" style="max-height :60vh;">
<template v-if="list.length">
<view class="u-m-b-10 u-flex item" v-for="item in list" :key="item.bankCode"
@click="itemClick(item)" :id="'shop_'+item.bankCode"
:class="{active:selItem&&selItem.bankCode==item.bankCode}">
<view class="checkbox">
<up-icon name="checkbox-mark" color="#fff"></up-icon>
</view>
<view class="u-flex-1">{{item.branchName}}</view>
</view>
<view class="u-p-20 u-flex u-row-center">
<up-loadmore status="nomore"></up-loadmore>
</view>
</template>
<template v-else>
<template v-if="keywords">
<up-empty text="未搜索到相关支行"></up-empty>
</template>
<up-empty v-else text="暂无支行"></up-empty>
</template>
</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 {
computed,
onMounted,
reactive,
ref,
watch
} from 'vue';
import {
bankBranchList
} from '@/http/api/order/entryManager.js';
const customStyle = ref({
marginRight: '20px'
});
const show = ref(false);
const modelValue = defineModel();
const bankBranchName = defineModel('bankBranchName', {
default: '',
});
const selid = ref('')
function returnLabel() {
const findShop = list.value.find(v => v.bankCode == modelValue.value)
return findShop ? findShop.branchName : ''
}
const selItem = ref(null)
function itemClick(data) {
selItem.value = data
}
function returnbranchName(bankCode) {
const item = list.value.find((v) => v.bankCode == bankCode);
return item?.branchName || '';
}
function close() {
show.value = false;
}
function submit() {
modelValue.value = selItem.value.bankCode
bankBranchName.value = selItem.value.branchName
show.value = false;
}
const keywords = ref('')
function search() {
list.value = matchStr(allList, keywords.value)
}
/**
* 模糊匹配对象数组中每个对象的全部属性,按匹配度由高到低排序返回,未匹配到的对象直接过滤(不返回)
* @param {Array<Object>} arr - 待匹配的对象数组(必传,非数组返回空数组)
* @param {string} str - 待匹配的目标字符串(空字符串返回原数组浅拷贝)
* @returns {Array<Object>} 按匹配度降序排序后的对象数组(仅包含匹配项,不修改原数组)
*/
function matchStr(arr, str) {
// ------------- 步骤1严格边界处理避免运行报错 -------------
if (!Array.isArray(arr)) {
console.warn('入参arr必须是对象数组');
return [];
}
const targetStr = String(str || '').trim().toLowerCase();
if (!targetStr) {
return [...arr]; // 空字符串返回原数组浅拷贝(保持原有逻辑)
}
// ------------- 步骤2定义匹配度得分规则 -------------
const getSinglePropScore = (propValue) => {
const propStr = String(propValue).trim().toLowerCase();
if (propStr === targetStr) return 3;
if (propStr.startsWith(targetStr)) return 2;
if (propStr.includes(targetStr)) return 1;
return 0;
};
// ------------- 步骤3遍历数组计算每个对象的总匹配度得分 -------------
const arrWithScore = arr.map(item => {
if (typeof item !== 'object' || item === null) {
return {
originItem: item,
totalScore: 0
};
}
let totalScore = 0;
Object.keys(item).forEach(propKey => {
const propValue = item[propKey];
totalScore += getSinglePropScore(propValue);
});
return {
originItem: item,
totalScore: totalScore
};
});
// ------------- 步骤4先过滤仅保留得分>0的项再排序最后返回原对象格式 -------------
return arrWithScore
.filter(item => item.totalScore > 0) // 核心新增:过滤未匹配项(总得分=0的对象不保留
.sort((a, b) => b.totalScore - a.totalScore) // 仅对匹配项进行降序排序
.map(item => item.originItem); // 剔除得分字段,返回原对象格式
}
const list = ref([]);
function openPopup() {
selid.value = 'shop_' + modelValue.value
const findShop = list.value.find(v => v.bankCode == modelValue.value)
selItem.value=findShop?findShop:null
show.value = true;
}
// --------------- 核心新增:节流函数实现 ---------------
/**
* 节流函数:限制函数在指定时间内只触发一次
* @param {Function} fn - 要节流的函数
* @param {number} delay - 节流延迟时间(毫秒)
* @returns {Function} 节流后的函数
*/
function throttle(fn, delay = 300) {
let timer = null; // 定时器标识
return function(...args) {
// 如果已有定时器,直接返回(未到触发时间)
if (timer) return;
// 执行函数并设置新定时器
fn.apply(this, args);
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
}, delay);
};
}
// --------------- 核心修改创建节流后的search方法 ---------------
// 300ms内只触发一次search可根据需求调整delay值如500ms
const throttledSearch = throttle(search, 300);
// --------------- 改造bankNameChange调用节流后的搜索 ---------------
function bankNameChange() {
// 输入变化时,调用节流后的搜索方法
throttledSearch();
}
const props = defineProps({
query: {
type: Object,
default: () => ({
province: '',
city: '',
instId: '',
})
}
})
watch(() => show.value, (newval) => {
init()
})
let allList = []
async function init() {
const res = await bankBranchList(props.query);
list.value = res
allList = res
}
</script>
<style lang="scss">
.box {
border-radius: 8upx;
display: flex;
flex-direction: row;
align-items: top;
flex-wrap: wrap;
padding: 20rpx 40rpx 20rpx 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;
}
.scroll-view {
.item {
border: 1px solid #eee;
padding: 20rpx;
border-radius: 12rpx;
&.active {
border-color: $my-main-color;
}
}
}
.checkbox {
margin-right: 10rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 6rpx;
border: 1px solid #999;
}
.item {
&.active {
.checkbox {
background-color: $my-main-color;
border-color: $my-main-color;
}
}
}
</style>

View File

@@ -1,306 +0,0 @@
<template>
<view>
<view class="u-font-32 font-bold u-m-32 text-center">商户基础信息</view>
<view class="container">
<!-- <view class="form-item">
<view class="font-bold u-m-b-16">店铺</view>
<shopSelect></shopSelect>
</view> -->
<view class="form-item required">
<view class="title">商户类型</view>
<up-radio-group v-model="form.userType">
<up-radio v-for="(value,key) in userTypes" :label="value" :name="key">
</up-radio>
</up-radio-group>
</view>
<view class="form-item required">
<view class="title">企业类型</view>
<up-radio-group v-model="form.companyChildType">
<up-radio v-for="(value,key) in companyChildTypes" :label="value" :name="key">
</up-radio>
</up-radio-group>
</view>
<view class="form-item required">
<view class="title">商户简称</view>
<up-input placeholder="商户简称" :placeholder-class="placeholderClass" v-model="form.shortName"></up-input>
</view>
<view class="form-item required">
<view class="title"> 行业类目</view>
<mccCategory v-model="form.mccCode"></mccCategory>
</view>
<view class="form-item required">
<view class="title"> 支付宝账号</view>
<up-input placeholder="支付宝账号" :placeholder-class="placeholderClass"
v-model="form.alipayAccount"></up-input>
</view>
<view class="form-item required">
<view class="title">联系人类型</view>
<up-radio-group v-model="form.contactPersonType">
<up-radio v-for="(value,key) in contactPersonTypes" :label="value" :name="key">
</up-radio>
</up-radio-group>
</view>
<template v-if="form.contactPersonType=='SUPER'">
<view class="form-item required">
<view class="title"> 联系人身份证正面国徽</view>
<my-upload-img v-model="form.contactIdCardFrontPic.url" :size="200"
@uploadSuccess="uploadSuccess($event,'IdCard','contactIdCardFrontPic')"></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 联系人身份证背面头像</view>
<my-upload-img v-model="form.contactIdCardBackPic.url" :size="200"
@uploadSuccess="uploadSuccess($event,'IdCard','contactIdCardBackPic')"></my-upload-img>
</view>
<!-- <view class="form-item required">
<view class="title">证件类型</view>
<up-radio-group v-model="form.certType">
<up-radio v-for="(value,key) in certTypes" :label="value" :name="key">
</up-radio>
</up-radio-group>
</view> -->
<view class="form-item required">
<view class="title"> 联系人姓名</view>
<up-input placeholder="联系人姓名" :placeholder-class="placeholderClass"
v-model="form.contactName"></up-input>
</view>
<view class="form-item required">
<view class="title"> 联系人身份证号</view>
<up-input placeholder="联系人身份证号" :placeholder-class="placeholderClass"
v-model="form.contactPersonId"></up-input>
</view>
<view class="form-item required">
<view class="title"> 联系人身份证开始日期</view>
<up-datetime-picker hasInput :minDate="minDate" :maxDate="maxDate" format="YYYY-MM-DD"
placeholder="请选择" v-model="form.contactPersonIdStartDate" mode="date">
</up-datetime-picker>
</view>
<view class="form-item required">
<view class="title"> 联系人身份证到期日期</view>
<view class="u-m-b-16">
<up-radio-group v-model="contactPersonIdEndDateType">
<up-radio :name="1" label="有结束日期"></up-radio>
<up-radio :name="2" label="长期有效"></up-radio>
</up-radio-group>
</view>
<template v-if="contactPersonIdEndDateType==1">
<up-datetime-picker hasInput :minDate="endDataMinDate" :maxDate="maxDate" format="YYYY-MM-DD"
placeholder="请选择" v-model="form.contactPersonIdEndDate" mode="date">
</up-datetime-picker>
</template>
</view>
<view class="form-item required ">
<view class="title"> 联系人电话</view>
<up-input placeholder="联系人电话" :placeholder-class="placeholderClass"
v-model="form.contactPhone"></up-input>
</view>
<view class="form-item required">
<view class="title"> 联系人通讯地址</view>
<up-input placeholder="联系人通讯地址" :placeholder-class="placeholderClass"
v-model="form.contactAddr"></up-input>
</view>
<view class="form-item required">
<view class="title"> 联系人邮箱
</view>
<up-input placeholder="联系人邮箱" :placeholder-class="placeholderClass"
v-model="form.contactEmail"></up-input>
</view>
</template>
</view>
</view>
</template>
<script setup>
import {
computed,
onMounted,
reactive,
ref,
watch
} from 'vue';
import shopSelect from './shop-select.vue'
import mccCategory from '@/entryManager/components/mcc-category.vue'
import {
userTypes,
contactPersonTypes,
companyChildTypes,
certTypes
} from '@/entryManager/data.js'
import {
getInfoByImg
} from '@/http/api/order/entryManager.js'
import dayjs from 'dayjs';
const minDate = dayjs('1970-01-01 00:00:00').valueOf()
const maxDate = dayjs('2099-12-31 23:59:59').valueOf()
const endDataMinDate = computed(() => {
if (!form.contactPersonIdStartDate) {
return minDate
}
if (form.contactPersonIdStartDate) {
return dayjs(form.contactPersonIdStartDate).add(10, 'year').valueOf()
}
return minDate
})
const contactPersonIdEndDateType = ref(1)
watch(() => contactPersonIdEndDateType.value, (newval) => {
if (newval == 2) {
form.contactPersonIdEndDate = maxDate
} else {
form.contactPersonIdEndDate = dayjs().valueOf()
}
})
function uploadSuccess(url, type, key) {
uni.showLoading({
type: '识别中,请稍等……!'
})
getInfoByImg({
url,
type
}).then(res => {
uni.hideLoading()
if (res) {
const data = res.subImages[0].kvInfo.data
if (key == 'contactIdCardFrontPic') {
if (data.validPeriod) {
const [start, end] = data.validPeriod.split('-')
if (start) {
form.contactPersonIdStartDate = dayjs(start).valueOf()
}
if (end) {
if (end.includes('长期')) {
contactPersonIdEndDateType.value = 2
} else {
form.contactPersonIdEndDate = dayjs(end).valueOf()
}
}
}
}
if (key == 'contactIdCardBackPic') {
form.contactName = data.name
form.contactPersonId = data.idNumber
form.contactAddr = data.address
}
}
})
}
const form = reactive({
userType: '0',
shortName: '',
mccCode: '',
alipayAccount: '',
contactPersonType: 'LEGAL',
contactName: '',
certType: '0',
contactPersonId: '',
contactPersonIdStartDate: '',
contactPersonIdEndDate: '',
contactIdCardBackPic: {
url: ''
},
contactIdCardFrontPic: {
url: ''
},
contactPhone: '',
contactAddr: '',
contactEmail: '',
companyChildType: '1',
})
const placeholderClass = ref('u-font-28')
const props = defineProps({
data: {
type: Object,
default: () => {
}
}
})
const emits = defineEmits(['update'])
watch(() => form, (newval) => {
console.log('form', form);
emits('update', newval)
}, {
deep: true,
immediate: true
})
watch(() => props.data, (newval) => {
for (let key in form) {
if (props.data.hasOwnProperty(key)) {
form[key] = props.data[key]
}
}
}, {
deep: true,
immediate: true
})
watch(()=>form.contactPersonIdEndDate,(newval)=>{
if(dayjs(newval).format('YYYY-MM-DD')==='2099-12-31'){
contactPersonIdEndDateType.value=2
}
})
onMounted(() => {
})
</script>
<style lang="scss">
.container {
padding: 32rpx 28rpx;
border-radius: 16rpx;
margin-bottom: 32rpx;
background-color: #fff;
}
.form-item {
margin-bottom: 32rpx;
.title {
font-weight: 700;
margin-bottom: 16rpx;
}
&.required {
.title::before {
content: '*';
color: red;
}
}
&:last-child {
margin-bottom: 0;
}
}
</style>

View File

@@ -1,81 +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 bgColor="#fff" type="default" @click="cancel" shape="circle">
{{cancelText}}
</my-button>
</view>
<view class="u-flex-1">
<my-button type="primary" @click="save" shape="circle">
{{confirmText}}
</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,
},
cancelText:{
type: String,
default: "上一步",
},
confirmText:{
type: String,
default: "下一步",
},
//方向 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,213 +0,0 @@
<template>
<view>
<view class="u-font-32 font-bold u-m-32 text-center">营业执照信息</view>
<view class="container">
<view class="form-item " :class="isRequired">
<view class="title"> 营业执照</view>
<my-upload-img v-model="form.licensePic.url" :size="200"
@uploadSuccess="uploadSuccess($event,'BusinessLicense')"></my-upload-img>
</view>
<view class="form-item " :class="isRequired">
<view class="title"> 营业执照全称</view>
<up-input placeholder="营业执照全称" :placeholder-class="placeholderClass"
v-model="form.licenceName"></up-input>
</view>
<view class="form-item " :class="isRequired">
<view class="title"> 营业执照号码</view>
<up-input placeholder="营业执照号码" :placeholder-class="placeholderClass"
v-model="form.licenceNo"></up-input>
</view>
<view class="form-item " :class="isRequired">
<view class="title"> 营业执照开始日期</view>
<up-datetime-picker hasInput :minDate="minDate" :maxDate="dayjs().valueOf()" format="YYYY-MM-DD"
placeholder="请选择" v-model="form.licenceStartDate" mode="date">
</up-datetime-picker>
<!-- <up-input placeholder="营业执照开始日期" :placeholder-class="placeholderClass"
v-model="form.licenceStartDate"></up-input> -->
</view>
<view class="form-item " :class="isRequired">
<view class="title"> 营业执照结束日期</view>
<view class="u-m-b-16">
<up-radio-group v-model="licenceEndDateType">
<up-radio :name="1" label="有结束日期"></up-radio>
<up-radio :name="2" label="长期有效"></up-radio>
</up-radio-group>
</view>
<template v-if="licenceEndDateType==1">
<up-datetime-picker hasInput :minDate="form.licenceStartDate||minDate" :maxDate="maxDate"
format="YYYY-MM-DD" placeholder="请选择" v-model="form.licenceEndDate" mode="date">
</up-datetime-picker>
</template>
</view>
<view class="form-item " :class="isRequired">
<view class="title"> 营业执照注册地址</view>
<up-input placeholder="营业执照注册地址" :placeholder-class="placeholderClass"
v-model="form.registeredAddress"></up-input>
</view>
</view>
</view>
</template>
<script setup>
import dayjs from 'dayjs';
const minDate = dayjs('1970-01-01 00:00:00').valueOf()
const maxDate = dayjs('2099-12-31 23:59:59').valueOf()
const licenceEndDateType = ref(1)
import {
reactive,
watch,
ref
} from 'vue';
import shopSelect from './shop-select.vue'
import {
userTypes,
sexs,
contactPersonTypes,
companyChildTypes,
certTypes
} from '@/entryManager/data.js'
const form = reactive({
"licenceName": "",
"licenceNo": "",
"licenceStartDate": "",
"licenceEndDate": "",
"registeredAddress": "",
"licensePic": {
"url": "",
"wechatId": "",
"alipayId": ""
}
})
import {
getInfoByImg
} from '@/http/api/order/entryManager.js'
import {
includes
} from 'lodash';
watch(() => licenceEndDateType.value, (newval) => {
if (newval == 2) {
form.licenceEndDate = maxDate
} else {
form.licenceEndDate = dayjs().add(10, 'year').valueOf()
}
})
function uploadSuccess(url, type, key) {
uni.showLoading({
type: '识别中,请稍等……!'
})
getInfoByImg({
url,
type
}).then(res => {
uni.hideLoading()
if (res) {
const data = res.subImages[0].kvInfo.data
form.licenceName = data.companyName
form.licenceNo = data.creditCode
form.registeredAddress = data.businessAddress
if (data.validFromDate) {
form.licenceStartDate = dayjs(data.validFromDate).valueOf()
}
if (data.validToDate) {
form.licenceEndDate = dayjs(data.validToDate).valueOf()
}
// console.log(dayjs(form.licenceEndDate).format('YYYY-MM-DD'));
if (data.validPeriod.includes('长期')) {
licenceEndDateType.value = 2;
}
}
})
}
const placeholderClass = ref('u-font-28')
const isRequired = ref('required')
const props = defineProps({
data: {
type: Object,
default: () => {
}
}
})
watch(() => props.data, (newval) => {
console.log('触发父数据更新')
for (let key in form) {
if (props.data.hasOwnProperty(key)) {
form[key] = props.data[key]
}
}
}, {
deep: true,
immediate: true
})
const emits = defineEmits(['update'])
watch(() => form, (newval) => {
emits('update', newval)
}, {
deep: true
})
watch(()=>form.licenceEndDate,(newval)=>{
if(dayjs(newval).format('YYYY-MM-DD')==='2099-12-31'){
licenceEndDateType.value=2
}
})
</script>
<style lang="scss">
.container {
padding: 32rpx 28rpx;
border-radius: 16rpx;
margin-bottom: 32rpx;
background-color: #fff;
}
.form-item {
margin-bottom: 32rpx;
.title {
font-weight: 700;
margin-bottom: 16rpx;
}
&.required {
.title::before {
content: '*';
color: red;
}
}
&:last-child {
margin-bottom: 0;
}
}
.input-box {
padding: 9px 10px;
border-radius: 4px;
border: 1px solid #dadbde;
}
</style>

View File

@@ -1,289 +0,0 @@
<template>
<view>
<view class="u-font-32 font-bold u-m-32 text-center">法人信息</view>
<view class="container">
<view class="form-item required">
<view class="title"> 身份证正面国徽</view>
<my-upload-img v-model="form.idCardFrontPic.url" :size="200" :maxSize="maxSize"
@uploadSuccess="uploadSuccess($event,'IdCard','idCardFrontPic')"
></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 身份证反面国徽</view>
<my-upload-img v-model="form.idCardBackPic.url" :size="200" :maxSize="maxSize"
@uploadSuccess="uploadSuccess($event,'IdCard','idCardBackPic')"
></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 身份证手持 图片</view>
<my-upload-img v-model="form.idCardHandPic.url" :size="200" :maxSize="maxSize"></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 法人姓名</view>
<up-input placeholder="法人姓名" :placeholder-class="placeholderClass"
v-model="form.legalPersonName"></up-input>
</view>
<view class="form-item required">
<view class="title">法人性别</view>
<up-radio-group v-model="form.legalGender">
<up-radio v-for="(value,key) in sexs" :label="value" :name="key">
</up-radio>
</up-radio-group>
</view>
<view class="form-item required">
<view class="title"> 法人身份证号</view>
<up-input placeholder="法人身份证号" :placeholder-class="placeholderClass"
v-model="form.legalPersonId"></up-input>
</view>
<view class="form-item required">
<view class="title"> 法人身份证开始日期</view>
<up-datetime-picker hasInput :minDate="minDate" :maxDate="dayjs().valueOf()" format="YYYY-MM-DD"
placeholder="请选择" v-model="form.legalIdPersonStartDate" mode="date">
</up-datetime-picker>
</view>
<view class="form-item required">
<view class="title"> 法人身份证到期日期</view>
<view class="u-m-b-16">
<up-radio-group v-model="endDateType">
<up-radio :name="1" label="有结束日期"></up-radio>
<up-radio :name="2" label="长期有效"></up-radio>
</up-radio-group>
</view>
<template v-if="endDateType==1">
<up-datetime-picker hasInput :minDate="endDataMinDate"
:maxDate="maxDate" format="YYYY-MM-DD" placeholder="请选择"
v-model="form.legalPersonIdEndDate" mode="date">
</up-datetime-picker>
</template>
</view>
<view class="form-item required">
<view class="title"> 法人电话</view>
<up-input placeholder="法人电话" :placeholder-class="placeholderClass"
v-model="form.legalPersonPhone"></up-input>
</view>
<view class="form-item required">
<view class="title">法人地址 </view>
<up-input placeholder="法人地址" :placeholder-class="placeholderClass"
v-model="form.legalAddress"></up-input>
</view>
<view class="form-item required">
<view class="title">法人邮箱 </view>
<up-input placeholder="法人邮箱" :placeholder-class="placeholderClass"
v-model="form.legalPersonEmail"></up-input>
</view>
</view>
</view>
</template>
<script setup>
import {
reactive,
watch,computed ,
ref
} from 'vue';
import shopSelect from './shop-select.vue'
import {
userTypes,
sexs,
contactPersonTypes,
companyChildTypes,
certTypes
} from '@/entryManager/data.js'
const form = reactive({
"legalPersonName": "",
"legalPersonId": "",
"legalIdPersonStartDate":'',
"legalPersonIdEndDate": '',
"legalPersonPhone": "",
"legalPersonEmail": "",
"legalGender": "",
"legalAddress": "",
"idCardHandPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"idCardFrontPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"idCardBackPic": {
"url": "",
"wechatId": "",
"alipayId": ""
}
})
import {
getInfoByImg
} from '@/http/api/order/entryManager.js'
import dayjs from 'dayjs';
import { includes } from 'lodash';
const minDate = dayjs('1970-01-01 00:00:00').valueOf()
const maxDate = dayjs('2099-12-31 23:59:59').valueOf()
const endDataMinDate = computed(() => {
if (!form.legalIdPersonStartDate) {
return maxDate
}
if (form.legalIdPersonStartDate) {
return dayjs(form.legalIdPersonStartDate).add(10, 'year').valueOf()
}
})
const endDateType = ref(1)
watch(() => endDateType.value, (newval) => {
if (newval == 2) {
form.legalPersonIdEndDate = maxDate
} else {
form.legalPersonIdEndDate = dayjs().valueOf()
}
})
function uploadSuccess(url, type, key) {
uni.showLoading({
type: '识别中,请稍等……!'
})
getInfoByImg({
url,
type
}).then(res => {
uni.hideLoading()
if (res) {
const data = res.subImages[0].kvInfo.data
if (key == 'idCardFrontPic') {
if (data.validPeriod) {
const [start, end] = data.validPeriod.split('-')
if (start) {
form.legalIdPersonStartDate = dayjs(start).valueOf()
}
if (end) {
if (end.includes('长期')) {
endDateType.value = 2
} else {
form.legalPersonIdEndDate = dayjs(end).valueOf()
}
}
}
}
if (key == 'idCardBackPic') {
form.legalPersonName = data.name
form.legalPersonId = data.idNumber
form.legalAddress = data.address
if(data.sex.includes('男')){
form.legalGender='0'
}
if(data.sex.includes('女')){
form.legalGender='1'
}
}
}
})
}
const placeholderClass = ref('u-font-28')
const props = defineProps({
data: {
type: Object,
default: () => {
}
},
maxSize:{
}
})
watch(() => props.data, (newval) => {
for (let key in form) {
if (props.data.hasOwnProperty(key)) {
form[key] = props.data[key]
}
}
}, {
deep: true,
immediate: true
})
const emits = defineEmits(['update'])
watch(() => form, (newval) => {
emits('update', newval)
}, {
deep: true
})
watch(()=>form.legalPersonIdEndDate,(newval)=>{
if(dayjs(newval).format('YYYY-MM-DD')==='2099-12-31'){
endDateType.value=2
}
})
</script>
<style lang="scss">
.container {
padding: 32rpx 28rpx;
border-radius: 16rpx;
margin-bottom: 32rpx;
background-color: #fff;
}
.form-item {
margin-bottom: 32rpx;
.title {
font-weight: 700;
margin-bottom: 16rpx;
}
&.required {
.title::before {
content: '*';
color: red;
}
}
&:last-child {
margin-bottom: 0;
}
}
</style>

View File

@@ -1,362 +0,0 @@
<template>
<view>
<view class="u-font-32 font-bold u-m-32 text-center">结算信息</view>
<view class="container">
<view class="form-item required">
<view class="title"> 结算类型</view>
<up-radio-group v-model="form.settlementType">
<up-radio v-for="(value,key) in settlementTypes" :label="value" :name="key">
</up-radio>
</up-radio-group>
</view>
<template v-if="form.settlementType*1==0">
<view class="form-item required">
<view class="title"> 非法人手持结算授权书</view>
<my-upload-img v-model="form.noLegalHandSettleAuthPic.url" :size="200"></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 非法人结算授权书</view>
<my-upload-img v-model="form.noLegalSettleAuthPic.url" :size="200"></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 非法人身份证正面</view>
<my-upload-img v-model="form.noLegalIdCardFrontPic.url" :size="200"></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 非法人身份证反面</view>
<my-upload-img v-model="form.noLegalIdCardBackPic.url" :size="200"></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 非法人姓名
</view>
<up-input placeholder="非法人姓名" :placeholder-class="placeholderClass"
v-model="form.noLegalName"></up-input>
</view>
<view class="form-item required">
<view class="title"> 非法人身份证号码
</view>
<up-input placeholder="非法人身份证号码" :placeholder-class="placeholderClass"
v-model="form.noLegalId"></up-input>
</view>
</template>
<view class="form-item required" >
<view class="title"> 结算卡类型</view>
<up-radio-group v-model="form.settlementCardType">
<up-radio v-for="(value,key) in settlementCardTypes" :label="value" :name="key">
</up-radio>
</up-radio-group>
</view>
<view class="form-item required">
<view class="title"> 银行卡正面</view>
<my-upload-img @uploadSuccess="uploadSuccess($event,'BankCard','bankCardFrontPic')"
v-model="form.bankCardFrontPic.url" :size="200"></my-upload-img>
</view>
<view class="form-item " :class="{required:form.settlementCardType==11}">
<view class="title"> 银行卡反面</view>
<my-upload-img v-model="form.bankCardBackPic.url" :size="200"></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 地区</view>
<view class="input-box u-flex u-row-between u-col-center" @click="showCitySelect=true">
<text class="color-999" v-if="!pro_city_area">请选择</text>
<text class="color-333" v-else>{{pro_city_area}}</text>
<up-icon name="arrow-down"></up-icon>
</view>
</view>
<view class="form-item required">
<view class="title"> 银行</view>
<bankSelect v-model="form.bankName" v-model:bankInstId="form.bankInstId"
v-model:wxProvinceCode="wxProvinceCode"
v-model:bankAliasCode="form.bankType"
></bankSelect>
</view>
<view class="form-item " v-if="pro_city_area&&form.bankName">
<view class="title"> 支行</view>
<bankBranchList :query="bankBranchListQuery" v-model:bankBranchName="form.bankBranchName"
v-model:bankBranchCode="form.bankBranchCode"></bankBranchList>
</view>
<view class="form-item required">
<view class="title"> 结算账户卡号</view>
<up-input placeholder="结算账户卡号" :placeholder-class="placeholderClass"
v-model="form.settlementCardNo"></up-input>
</view>
<view class="form-item required">
<view class="title"> 结算账户户名</view>
<up-input placeholder="结算账户户名" :placeholder-class="placeholderClass"
v-model="form.settlementName"></up-input>
</view>
<view class="form-item required">
<view class="title"> 结算银行预留手机号</view>
<up-input placeholder="结算银行预留手机号" :placeholder-class="placeholderClass"
v-model="form.bankMobile"></up-input>
</view>
<!-- <view class="form-item ">
<view class="title"> 开户行行别名称</view>
<up-input placeholder="开户行行别名称" :placeholder-class="placeholderClass"
v-model="form.bankName"></up-input>
</view> -->
<!--
<view class="form-item ">
<view class="title"> 开户行缩写</view>
<up-input placeholder="开户行缩写" :placeholder-class="placeholderClass"></up-input>
</view>
<view class="form-item ">
<view class="title"> 开户行编号
</view>
<up-input placeholder="开户行编号" :placeholder-class="placeholderClass" v-model="form.bankType"></up-input>
</view>
<view class="form-item ">
<view class="title"> 支行开户行行别名称
</view>
<up-input placeholder="支行开户行行别名称" :placeholder-class="placeholderClass"
v-model="form.bankBranchName"></up-input>
</view>
<view class="form-item ">
<view class="title"> 支行开户行编号
</view>
<up-input placeholder="支行开户行编号" :placeholder-class="placeholderClass"
v-model="form.bankBranchCode"></up-input>
</view>
-->
<template v-if="form.settlementCardType*1==21">
<view class="form-item required">
<view class="title"> 开户许可证</view>
<my-upload-img v-model="form.openAccountLicencePic.url" :size="200"></my-upload-img>
</view>
</template>
</view>
<citySelect v-model="showCitySelect" @city-change="cityChange"></citySelect>
</view>
</template>
<script setup>
import {
computed,
reactive,
watch,
ref
} from 'vue';
import shopSelect from './shop-select.vue'
import citySelect from '../../components/u-city-select.vue'
import bankSelect from './bank-select.vue'
import bankBranchList from './bankBranchList.vue'
import {
userTypes,
sexs,
contactPersonTypes,
companyChildTypes,
settlementTypes,
settlementCardTypes,
certTypes
} from '@/entryManager/data.js'
import {
getInfoByImg
} from '@/http/api/order/entryManager.js'
const showCitySelect = ref(false)
const showBankSelect = ref(true)
const wxProvinceCode=ref('')
function uploadSuccess(url, type, key) {
uni.showLoading({
type: '识别中,请稍等……!'
})
getInfoByImg({
url,
type
}).then(res => {
uni.hideLoading()
if (res) {
form.bankName = res.subImages[0].kvInfo.data.bankName
form.settlementCardNo = res.subImages[0].kvInfo.data.cardNumber
}
})
}
function basicSelectChange(e) {
}
function cityChange(e) {
console.log('cityChange', e);
form.openAccProvince = e.province.regionName;
form.openAccCity = e.city.regionName;
form.openAccArea = e.area.regionName;
form.openAccProvinceId = e.province.regionId;
form.openAccCityId = e.city.regionId;
form.openAccAreaId = e.area.regionId;
console.log('form', form);
}
const pro_city_area = computed(() => {
if (form.openAccProvince && form.openAccCity && form.openAccArea) {
const text = form.openAccProvince + '-' + form.openAccCity + '-' + form.openAccArea
console.log('text', text);
return text
}
return ''
})
const form = reactive({
"settlementType": "0",
"noLegalName": "",
"noLegalId": "",
"settlementCardType": "11",
"settlementCardNo": "",
"settlementName": "",
"bankMobile": "",
"openAccProvinceId": "",
"openAccCityId": "",
"openAccAreaId": "",
"openAccProvince": "",
"openAccCity": "",
"openAccArea": "",
"bankName": "",
"bankInstId": "",
"bankType": "",
"bankBranchName": "",
"bankBranchCode": "",
"bankCardFrontPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"bankCardBackPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"openAccountLicencePic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"noLegalHandSettleAuthPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"noLegalSettleAuthPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"noLegalIdCardFrontPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"noLegalIdCardBackPic": {
"url": "",
"wechatId": "",
"alipayId": ""
}
})
const placeholderClass = ref('u-font-28')
const isRequired = ref('required')
const bankBranchListQuery = computed(() => {
return {
province: form.openAccProvince,
city: form.openAccCity,
instId: form.bankInstId,
}
})
const props = defineProps({
data: {
type: Object,
default: () => {
}
}
})
watch(() => props.data, (newval) => {
console.log('watch 变', newval);
for (let key in form) {
if (props.data.hasOwnProperty(key)) {
form[key] = props.data[key]
}
}
console.log(form);
}, {
deep: true,
immediate: true
})
const emits = defineEmits(['update'])
watch(() => form, (newval) => {
emits('update', newval)
}, {
deep: true
})
</script>
<style lang="scss">
.container {
padding: 32rpx 28rpx;
border-radius: 16rpx;
margin-bottom: 32rpx;
background-color: #fff;
}
.form-item {
margin-bottom: 32rpx;
.title {
font-weight: 700;
margin-bottom: 16rpx;
}
&.required {
.title::before {
content: '*';
color: red;
}
}
&:last-child {
margin-bottom: 0;
}
}
.input-box {
padding: 10px 10px;
border-radius: 4px;
border: 1px solid #dadbde;
}
</style>

View File

@@ -1,165 +0,0 @@
<template>
<view>
<view class="box" @click.stop="openPopup">
<text class="u-font-28 color-999 u-p-r-16" v-if="!modelValue">请选择</text>
<text class="u-font-28 color-333 u-p-r-16" v-else>{{returnLabel()}}</text>
<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 scroll-with-animation :scroll-into-view="selShopId" class="scroll-view u-m-t-30"
scroll-y="true" style="max-height :60vh;">
<view class="u-m-b-10 u-flex item" v-for="item in list" :key="item.shopId" @click="itemClick(item)"
:id="'shop_'+item.shopId" :class="{active:modelValue==item.shopId}">
<view class="checkbox">
<up-icon name="checkbox-mark" color="#fff"></up-icon>
</view>
<view class="u-flex-1">{{item.shopName}}</view>
</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 {
computed,
onMounted,
reactive,
ref,
watch
} from 'vue';
import {
adminShopList
} from '@/http/api/shop.js';
const customStyle = ref({
marginRight: '20px'
});
const show = ref(false);
let modelValue = defineModel('modelValue', {
default: '',
});
const selShopId = ref('')
function returnLabel() {
const findShop = list.value.find(v => v.shopId == modelValue.value)
return findShop ? findShop.shopName : ''
}
function itemClick(shop) {
modelValue.value = shop.shopId
}
function returnShopName(shopId) {
const item = list.value.find((v) => v.shopId == shopId);
return item?.shopName || '';
}
function close() {
show.value = false;
}
function submit() {
show.value = false;
}
const list = ref([]);
function openPopup() {
selShopId.value = 'shop_' + modelValue.value
show.value = true;
}
async function init() {
const res = await adminShopList({
page: 1,
size: 99999
});
if (res) {
list.value = res.records.map((item) => ({
shopId: item.id,
shopName: item.shopName,
}));
}
}
onMounted(init);
</script>
<style lang="scss">
.box {
border-radius: 8upx;
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;
}
.scroll-view {
.item {
border: 1px solid #eee;
padding: 20rpx;
border-radius: 12rpx;
&.active {
border-color: $my-main-color;
}
}
}
.checkbox {
margin-right: 10rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 6rpx;
border: 1px solid #999;
}
.item {
&.active {
.checkbox {
background-color: $my-main-color;
border-color: $my-main-color;
}
}
}
</style>

View File

@@ -1,193 +0,0 @@
<template>
<scroll-view scroll-x="true" scroll-with-animation :scroll-left="scrollLeft" class="steps-scroll-container"
ref="scrollViewRef">
<view class="steps-content" ref="contentRef">
<view v-for="(item,index) in list" class="step-item" :key="index" :data-index="index"
:class="'step_'+index"
ref="stepItemRefs">
<view class="step-inner">
<view class="index" :class="{active:index<=cur}">
<text>{{index+1}}</text>
</view>
<view class="step-text color-999" :class="{'color-main':index<=cur}" @click="handleClick(index)">
{{item}}
</view>
</view>
<view class="step-arrow" v-if="index!=list.length-1">
<up-icon name="arrow-rightward" size="16" :color="isActive(index)?'#318AFE':'#999'"></up-icon>
</view>
</view>
</view>
</scroll-view>
</template>
<script setup>
import {
ref,
nextTick,
getCurrentInstance,
watch
} from 'vue';
const cur = defineModel({
default: 0
})
const scrollLeft = ref(0)
const scrollViewRef = ref(null)
const contentRef = ref(null) // 内容容器ref
const stepItemRefs = ref([]) // 步骤项ref数组
const instance = getCurrentInstance()
function isActive(index) {
return index <= cur.value
}
// 核心:精准居中计算(基于元素相对于内容容器的偏移)
const calcScrollCenter = async (index) => {
try {
console.log('calcScrollCenter');
const query = uni.createSelectorQuery().in(instance)
// 1. 获取滚动容器宽度
const [scrollViewRect] = await new Promise(resolve => {
query.select('.steps-scroll-container').boundingClientRect(rect => resolve([
rect
])).exec()
})
const scrollViewWidth = scrollViewRect?.width || 0
// 2. 获取当前步骤项的布局信息
const [itemRect] = await new Promise(resolve => {
query.select(`.step_${index}`).boundingClientRect(rect =>
resolve([rect])).exec()
})
console.log('itemRect',itemRect);
// 3. 获取内容容器的布局信息
const [contentRect] = await new Promise(resolve => {
query.select('.steps-content').boundingClientRect(rect => resolve([rect]))
.exec()
})
if (!itemRect || !contentRect) return
// 关键修正:元素相对于内容容器的左偏移(而非视口)
const itemOffsetLeft = itemRect.left - contentRect.left
// 居中公式:滚动距离 = 元素偏移 - (容器宽度/2) + (元素宽度/2)
let targetScrollLeft = itemOffsetLeft - (scrollViewWidth / 2) + (itemRect.width / 2)
// 4. 计算最大可滚动距离(边界限制)
const maxScrollLeft = Math.max(0, contentRect.width - scrollViewWidth)
// 限制滚动范围,避免超出边界
targetScrollLeft = Math.max(0, Math.min(targetScrollLeft, maxScrollLeft))
// 5. 设置滚动距离(强制整数,避免小数导致的偏移)
scrollLeft.value = Math.round(targetScrollLeft)
console.log('scrollLeft', scrollLeft.value);
} catch (e) {
console.error('计算居中失败:', e)
}
}
const emits = defineEmits(['itemClick'])
// 点击事件
const handleClick = (index) => {
if (cur.value === index) return
if (index > cur.value) {
emits('itemClick', index)
return
}
cur.value = index
// calcScrollCenter(index)
}
// 初始化居中
nextTick(() => {
calcScrollCenter(cur.value)
})
watch(() => cur.value, (newval) => {
calcScrollCenter(newval)
})
// 步骤列表
const list = ref(['基础信息', '法人信息', '营业执照信息', '门店信息', '结算信息'])
</script>
<style lang="scss" scoped>
// 滚动容器:固定宽度,隐藏滚动条
.steps-scroll-container {
width: 100%;
white-space: nowrap;
box-sizing: border-box;
overflow-x: auto;
// 隐藏滚动条(三端兼容)
::-webkit-scrollbar {
display: none;
}
-ms-overflow-style: none;
scrollbar-width: none;
}
// 内容容器inline-flex宽度自适应
.steps-content {
display: inline-flex;
align-items: center;
padding: 10rpx 0;
}
// 单个步骤项:统一间距和布局
.step-item {
display: inline-flex;
align-items: center;
margin: 0 10rpx; // 统一间距
box-sizing: border-box;
}
// 步骤内部布局
.step-inner {
display: inline-flex;
align-items: center;
}
// 数字索引
.index {
border: 1px solid #999;
width: 40rpx;
height: 40rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
margin-right: 10rpx;
color: #999;
&.active {
border-color: $my-main-color;
color: $my-main-color;
}
}
// 步骤文字
.step-text {
white-space: nowrap;
font-size: 28rpx;
}
// 箭头容器
.step-arrow {
padding: 0 10rpx;
margin-top: 2rpx;
}
// 样式补充
.color-main {
color: $my-main-color !important;
}
.color-999 {
color: #999;
}
</style>

View File

@@ -1,178 +0,0 @@
<template>
<view>
<view class="u-font-32 font-bold u-m-32 text-center">门店信息</view>
<view class="container">
<view class="form-item required">
<view class="title"> 归属地</view>
<view class="input-box u-flex u-row-between u-col-center" @click="showCitySelect=true">
<text class="color-999" v-if="!pro_city_area">请选择</text>
<text class="color-333" v-else>{{pro_city_area}}</text>
<up-icon name="arrow-down"></up-icon>
</view>
</view>
<view class="form-item required">
<view class="title"> 营业地址</view>
<up-input placeholder="营业地址" :placeholder-class="placeholderClass"
v-model="form.businessAddress"></up-input>
</view>
<view class="form-item required">
<view class="title"> 经营场所内设照片</view>
<my-upload-img v-model="form.insidePic.url" :size="200"></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 门头照</view>
<my-upload-img v-model="form.doorPic.url" :size="200"></my-upload-img>
</view>
<view class="form-item required">
<view class="title"> 收银台照片</view>
<my-upload-img v-model="form.cashierDeskPic.url" :size="200"></my-upload-img>
</view>
</view>
<citySelect v-model="showCitySelect" @city-change="cityChange"></citySelect>
</view>
</template>
<script setup>
import {
computed,
reactive,
ref,watch
} from 'vue';
import shopSelect from './shop-select.vue'
import citySelect from '../../components/u-city-select.vue'
import {
userTypes,
sexs,
contactPersonTypes,
companyChildTypes,
certTypes
} from '@/entryManager/data.js'
const showCitySelect = ref(false)
function cityChange(e) {
console.log('cityChange', e);
form.mercProv = e.province.regionName;
form.mercCity = e.city.regionName;
form.mercArea = e.area.regionName;
form.mercProvCode = e.province.regionId;
form.mercCityCode = e.city.regionId;
form.mercAreaCode = e.area.regionId;
console.log('form', form);
}
const pro_city_area = computed(() => {
if (form.mercProv && form.mercCity && form.mercArea) {
const text = form.mercProv + '-' + form.mercCity + '-' + form.mercArea
console.log('text', text);
return text
}
return ''
})
const form = reactive({
"mercProvCode": "",
"mercCityCode": "",
"mercAreaCode": "",
"mercProv": "",
"mercCity": "",
"mercArea": "",
"businessAddress": "",
"insidePic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"doorPic": {
"url": "",
"wechatId": "",
"alipayId": ""
},
"cashierDeskPic": {
"url": "",
"wechatId": "",
"alipayId": ""
}
})
const placeholderClass = ref('u-font-28')
const isRequired = ref('required')
const props=defineProps({
data:{
type:Object,
default:()=>{
}
}
})
watch(()=>props.data,(newval)=>{
for(let key in form){
if(props.data.hasOwnProperty(key)){
form[key]=props.data[key]
}
}
},{
deep:true,immediate:true
})
const emits=defineEmits(['update'])
watch(()=>form,(newval)=>{
emits('update',newval)
},{
deep:true
})
</script>
<style lang="scss">
.container {
padding: 32rpx 28rpx;
border-radius: 16rpx;
margin-bottom: 32rpx;
background-color: #fff;
}
.form-item {
margin-bottom: 32rpx;
.title {
font-weight: 700;
margin-bottom: 16rpx;
}
&.required {
.title::before {
content: '*';
color: red;
}
}
&:last-child {
margin-bottom: 0;
}
}
.input-box {
padding: 10px 10px;
border-radius: 4px;
border: 1px solid #dadbde;
}
</style>

View File

@@ -1,288 +0,0 @@
export const rules = {
merchantBaseInfo: {
userType: {
required: true,
errorMsg: '请选择商户类型',
},
shortName: {
required: true,
errorMsg: '请填写商户简称',
},
mccCode: {
required: true,
errorMsg: '请选择行业类目',
},
alipayAccount: {
required: true,
errorMsg: '请填写支付宝账号',
}
},
legalPersonInfo: {
'idCardHandPic.url': {
required: true,
errorMsg: '请上传身份证手持图片',
},
'idCardFrontPic.url': {
required: true,
errorMsg: '请上传身份证正面图片',
},
'idCardBackPic.url': {
required: true,
errorMsg: '请上传身份证反面图片',
},
legalPersonName: {
required: true,
errorMsg: '请填写法人姓名',
},
legalPersonId: {
required: true,
errorMsg: '请填写法人身份证号',
},
legalIdPersonStartDate: {
required: true,
errorMsg: '请填写法人身份证开始日期',
},
legalPersonIdEndDate: {
required: true,
errorMsg: '请填写法人身份证到期日期',
},
legalPersonPhone: {
required: true,
errorMsg: '请填写法人电话',
},
legalPersonEmail: {
required: true,
errorMsg: '请填写法人邮箱',
},
legalGender: {
required: true,
errorMsg: '请填写法人性别',
},
legalAddress: {
required: true,
errorMsg: '请填写法人地址',
},
},
businessLicenceInfo:{
'licensePic.url':{
required: true,
errorMsg:'请上传营业执照照片'
},
licenceName:{
required: true,
errorMsg:'请输入营业执照全称'
},
licenceNo:{
required: true,
errorMsg:'请输入营业执照号码'
},
licenceStartDate:{
required: true,
errorMsg:'请选择营业执照开始日期'
},
registeredAddress:{
required: true,
errorMsg:'请填写营业执照注册地址'
}
},
storeInfo: {
mercProvCode: {
required: true,
errorMsg: '请选择归属地',
},
mercCityCode: {
required: true,
errorMsg: '请选择归属地',
},
mercAreaCode: {
required: true,
errorMsg: '请选择归属地',
},
mercProv: {
required: true,
errorMsg: '请选择归属地',
},
mercCity: {
required: true,
errorMsg: '请选择归属地',
},
mercArea: {
required: true,
errorMsg: '请选择归属地',
},
businessAddress: {
required: true,
errorMsg: '请填写营业地址',
},
'insidePic.url': {
required: true,
errorMsg: '请上传经营场所内设照片',
},
'doorPic.url': {
required: true,
errorMsg: '请上传门头照',
},
'cashierDeskPic.url': {
required: true,
errorMsg: '请上传收银台照片',
}
},
settlementInfo: {
settlementType: {
required: true,
errorMsg: '请选择结算类型',
},
settlementCardType: {
required: true,
errorMsg: '请选择结算卡类型',
},
'bankCardFrontPic.url': {
required: true,
errorMsg: '请上传银行卡正面照片',
},
openAccProvinceId: {
required: true,
errorMsg: '请选择地区',
},
openAccCityId: {
required: true,
errorMsg: '请选择地区',
},
openAccAreaId: {
required: true,
errorMsg: '请选择地区',
},
openAccProvince: {
required: true,
errorMsg: '请选择地区',
},
openAccCity: {
required: true,
errorMsg: '请选择地区',
},
openAccArea: {
required: true,
errorMsg: '请选择地区',
},
bankType: {
required: true,
errorMsg: '请选择银行',
},
bankInstId: {
required: true,
errorMsg: '请选择银行',
},
bankName: {
required: true,
errorMsg: '请选择银行',
},
settlementCardNo: {
required: true,
errorMsg: '请填写结算账户卡号',
},
settlementName: {
required: true,
errorMsg: '请填写结算账户户名',
},
bankMobile: {
required: true,
errorMsg: '请填写结算银行预留手机号',
}
}
}
export function isEmptyValue(val) {
if (val === '' || val === undefined || val === null) {
return true
}
return false
}
/**
* 解析属性路径,返回数组格式的路径片段
* @param {string} str - 属性路径(如 'userType' 或 'idCardHandPic.url'
* @returns {Array<string>} 属性路径片段数组(如 ['userType'] 或 ['idCardHandPic', 'url']
*/
export function returnKey(str) {
// 无论是否包含'.',都返回数组,方便后续统一处理
return str.includes('.') ? str.split('.') : [str];
}
/**
* 根据属性路径数组,安全获取对象的嵌套属性值
* @param {Object} obj - 目标对象(如 form.legalPersonInfo
* @param {Array<string>} keyPath - 属性路径数组(如 ['idCardHandPic', 'url']
* @returns {*} 嵌套属性的值(若路径不存在,返回 undefined
*/
export function getNestedValue(obj, keyPath) {
// 边界处理obj不是对象直接返回undefined
if (typeof obj !== 'object' || obj === null) {
return undefined;
}
// 逐层遍历属性路径,获取最终值
return keyPath.reduce((currentObj, key) => {
// 中间层级不存在直接返回undefined避免报错
if (currentObj === undefined || currentObj === null) {
return undefined;
}
return currentObj[key];
}, obj);
}
export function verifyValue(val, ruleItem) {
const isEmpty = isEmptyValue(val)
let result = {
ispas: true,
errorMsg: ''
}
if (ruleItem.required) {
if (isEmpty) {
result.ispas = false
result.errorMsg = ruleItem.errorMsg
return result
}
}
return result
}
export function verifyData(data, rule) {
// 边界处理data不是对象直接返回校验失败若有必填规则
if (typeof data !== 'object' || data === null) {
// 遍历规则,返回第一个必填项的错误信息
for (let ruleKey in rule) {
const ruleItem = rule[ruleKey];
if (ruleItem.required) {
return {
ispas: false,
errorMsg: ruleItem.errorMsg || '数据格式错误,无法校验'
};
}
}
return {
ispas: true,
errorMsg: ''
};
}
for (let ruleKey in rule) {
const ruleItem = rule[ruleKey];
// 1. 获取属性路径数组(如 ['idCardHandPic', 'url']
const keyPath = returnKey(ruleKey);
// 2. 安全获取嵌套属性值(核心:支持深层级属性)
const targetValue = getNestedValue(data, keyPath);
// 3. 校验属性值
const result = verifyValue(targetValue, ruleItem);
// 4. 校验失败,直接返回结果
if (!result.ispas) {
return result;
}
}
return {
ispas: true,
errorMsg: ''
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
var provinceData=[{"label":"北京市","value":"11"},{"label":"天津市","value":"12"},{"label":"河北省","value":"13"},{"label":"山西省","value":"14"},{"label":"内蒙古自治区","value":"15"},{"label":"辽宁省","value":"21"},{"label":"吉林省","value":"22"},{"label":"黑龙江省","value":"23"},{"label":"上海市","value":"31"},{"label":"江苏省","value":"32"},{"label":"浙江省","value":"33"},{"label":"安徽省","value":"34"},{"label":"福建省","value":"35"},{"label":"江西省","value":"36"},{"label":"山东省","value":"37"},{"label":"河南省","value":"41"},{"label":"湖北省","value":"42"},{"label":"湖南省","value":"43"},{"label":"广东省","value":"44"},{"label":"广西壮族自治区","value":"45"},{"label":"海南省","value":"46"},{"label":"重庆市","value":"50"},{"label":"四川省","value":"51"},{"label":"贵州省","value":"52"},{"label":"云南省","value":"53"},{"label":"西藏自治区","value":"54"},{"label":"陕西省","value":"61"},{"label":"甘肃省","value":"62"},{"label":"青海省","value":"63"},{"label":"宁夏回族自治区","value":"64"},{"label":"新疆维吾尔自治区","value":"65"},{"label":"台湾","value":"66"},{"label":"香港","value":"67"},{"label":"澳门","value":"68"}];export default provinceData;

View File

@@ -1,302 +0,0 @@
<template>
<view>
<view class="box" @click.stop="openPopup">
<text class="u-font-28 color-999 u-p-r-16" v-if="selArr[0]===null||selArr[1]===null">请选择</text>
<text class="u-font-28 color-333 u-p-r-16" v-else>{{returnLabel()}}</text>
<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>
<view class="u-m-t-24">
<up-search v-model="query.bankName" @search="search" @change="bankNameChange" @custom="search"
@clear="search"></up-search>
</view>
<view class="u-flex u-m-t-30 gap-20 u-col-top">
<view class="u-flex-1">
<view class="u-p-b-24 font-bold text-center">一级类目</view>
<scroll-view @scrolltolower="scrolltolower" scroll-with-animation :scroll-into-view="oneSelId"
class="scroll-view " scroll-y="true" style="max-height :60vh;">
<view class="u-m-b-10 u-flex item" v-for="(item,index) in list" :key="item.id"
@click="oneCategoryClick(index)" :id="'cateOne_'+index"
:class="{active:selArr[0]===index}">
<view class="checkbox">
<up-icon name="checkbox-mark" color="#fff"></up-icon>
</view>
<view class="u-flex-1">{{item.firstCategory}}</view>
</view>
<up-empty v-if="list.length==0" text="暂无相关分类"></up-empty>
</scroll-view>
</view>
<view class="u-flex-1">
<view class="u-p-b-24 font-bold text-center">二级类目</view>
<scroll-view @scrolltolower="scrolltolower" scroll-with-animation :scroll-into-view="twoSelId"
class="scroll-view " scroll-y="true" style="max-height :60vh;">
<view class="u-m-b-10 u-flex item" v-for="(item,index) in sendList" :key="item.id"
@click="selArr[1]=index" :id="'cateTwo_'+index" :class="{active:selArr[1]==index}">
<view class="checkbox">
<up-icon name="checkbox-mark" color="#fff"></up-icon>
</view>
<view class="u-flex-1">{{item.secondCategory}}</view>
</view>
<up-empty v-if="list.length==0" text="暂无相关分类"></up-empty>
</scroll-view>
</view>
</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 {
computed,
nextTick,
onMounted,
reactive,
ref,
watch
} from 'vue';
import {
mccCategory
} from '@/http/api/system/common.js';
const customStyle = ref({
marginRight: '20px'
});
const show = ref(false);
const modelValue = defineModel();
const oneSelId = ref('')
const twoSelId = ref('')
const allCategoryArr = computed(() => {
return list.value.reduce((prve, cur) => {
prve.push(...cur.child)
return prve
}, [])
})
function returnLabel() {
const [index1, index2] = selArr.value
console.log('selArr', selArr.value);
if (index1 !== null && index2 !== null) {
return list.value[index1].firstCategory + '/' + list.value[index1].child[index2].secondCategory
}
return ''
}
function oneCategoryClick(index) {
selArr.value[0] = index
selArr.value[1] = null
}
const selItem = ref(null)
function itemClick(bank) {
selItem.value = bank;
}
function close() {
show.value = false;
}
function submit() {
if (selArr.value[0] === null || selArr.value[1] === null) {
return uni.showToast({
title: '请选择行业类目',
icon: 'none'
})
}
const [oneIndex, twoIndex] = selArr.value
const item = list.value[oneIndex].child[twoIndex]
modelValue.value = item.firstCategoryCode + '_' + item.secondCategoryCode
show.value = false;
}
function search() {
init()
}
const list = ref([]);
function openPopup() {
show.value = true;
}
// --------------- 核心新增:节流函数实现 ---------------
/**
* 节流函数:限制函数在指定时间内只触发一次
* @param {Function} fn - 要节流的函数
* @param {number} delay - 节流延迟时间(毫秒)
* @returns {Function} 节流后的函数
*/
function throttle(fn, delay = 300) {
let timer = null; // 定时器标识
return function(...args) {
// 如果已有定时器,直接返回(未到触发时间)
if (timer) return;
// 执行函数并设置新定时器
fn.apply(this, args);
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
}, delay);
};
}
// --------------- 核心修改创建节流后的search方法 ---------------
// 300ms内只触发一次search可根据需求调整delay值如500ms
const throttledSearch = throttle(search, 300);
// --------------- 改造bankNameChange调用节流后的搜索 ---------------
function bankNameChange() {
// 输入变化时,调用节流后的搜索方法
throttledSearch();
}
const query = reactive({
bankName: ''
})
const isEnd = ref(false)
function scrolltolower() {
}
async function init() {
const res = await mccCategory(query);
if (res) {
list.value = res.map(v => {
return {
...v,
firstCategoryCode: v.child[0].firstCategoryCode
}
})
startWatch()
}
}
const selArr = ref([null, null])
const sendList = computed(() => {
if (selArr.value[0] !== null) {
return list.value[selArr.value[0]].child
}
})
function startWatch() {
watch(() => modelValue.value, (newval) => {
if (newval) {
const arr = modelValue.value.split('_')
const [oneCode, twoCode] = arr
console.log('oneCode',oneCode);
console.log('twoCode',twoCode);
const oneIndex = list.value.findIndex(v => v.firstCategoryCode == oneCode)
if (oneIndex != -1) {
selArr.value[0] = oneIndex
oneSelId.value = 'cateOne_' + oneIndex
const twoIndex = list.value[oneIndex].child.findIndex(v => v.secondCategoryCode==twoCode)
if (twoIndex != -1) {
selArr.value[1] = twoIndex
twoSelId.value = 'cateTwo_' + twoIndex
}
}
console.log('watch selArr',selArr.value);
} else {
selArr.value = [null, null]
}
}, {
immediate: true
})
}
onMounted(init);
</script>
<style lang="scss">
.box {
border-radius: 8upx;
display: flex;
flex-direction: row;
align-items: top;
flex-wrap: wrap;
padding: 20rpx 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;
}
.scroll-view {
.item {
border: 1px solid #eee;
padding: 20rpx;
border-radius: 12rpx;
&.active {
border-color: $my-main-color;
}
}
}
.checkbox {
margin-right: 10rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 6rpx;
border: 1px solid #999;
}
.item {
&.active {
.checkbox {
background-color: $my-main-color;
border-color: $my-main-color;
}
}
}
</style>

View File

@@ -1,203 +0,0 @@
<template>
<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>
<view class="u-m-t-24">
<up-search v-model="query.shopName" @search="search" @clear="search" @custom="search"></up-search>
</view>
<scroll-view @scrolltolower="scrolltolower" scroll-with-animation :scroll-into-view="selShopId"
class="scroll-view u-m-t-30" scroll-y="true" style="max-height :60vh;">
<view class="u-m-b-10 u-flex item" v-for="item in list" :key="item.shopId" @click="itemClick(item)"
:id="'shop_'+item.shopId" :class="{active:selShop==item.shopId}">
<view class="checkbox">
<up-icon name="checkbox-mark" color="#fff"></up-icon>
</view>
<view class="u-flex-1">{{item.shopName}}</view>
</view>
<template v-if="query.shopName!==''">
<up-empty v-if="list.length==0" text="未搜索到相关店铺"></up-empty>
<up-loadmore v-else :status="isEnd?'nomor':'loading'"></up-loadmore>
</template>
<template v-else>
<up-loadmore :status="isEnd?'nomor':'loading'"></up-loadmore>
</template>
</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 {
computed,
onMounted,
reactive,
ref,
watch
} from 'vue';
import {
adminShopList
} from '@/http/api/shop.js';
const customStyle = ref({
marginRight: '20px'
});
const show = defineModel(false)
let selShop = defineModel('selShop', {
default: '',
});
const selShopId = ref('')
function returnLabel() {
const findShop = list.value.find(v => v.shopId == selShop.value)
return findShop ? findShop.shopName : ''
}
function itemClick(shop) {
selShop.value = shop.shopId
}
function returnShopName(shopId) {
const item = list.value.find((v) => v.shopId == shopId);
return item?.shopName || '';
}
function close() {
show.value = false;
}
const emits=defineEmits(['confirm'])
function submit() {
show.value = false;
if(!selShop.value){
return uni.showToast({
title:'请选择门店',
icon:'none'
})
}
const findShop = list.value.find(v => v.shopId == selShop.value)
emits('confirm',findShop)
}
const list = ref([]);
function openPopup() {
selShopId.value = 'shop_' + selShop.value
show.value = true;
}
const query = reactive({
page: 1,
size: 10,
shopName: '',
})
const isEnd = ref(false)
function scrolltolower() {
if (!isEnd.value) {
query.page++
init()
}
}
function search() {
selShop.value = '';
query.page = 1;
isEnd.value = false
init()
}
async function init() {
const res = await adminShopList(query);
if (res) {
const arr = res.records.map((item) => ({
shopId: item.id,
shopName: item.shopName,
}));
isEnd.value = query.page >= res.totalPage * 1
if (query.page == 1) {
list.value = arr
} else {
list.value.push(...arr)
}
}
}
onMounted(init);
</script>
<style lang="scss">
.box {
border-radius: 8upx;
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;
}
.scroll-view {
.item {
border: 1px solid #eee;
padding: 20rpx;
border-radius: 12rpx;
&.active {
border-color: $my-main-color;
}
}
}
.checkbox {
margin-right: 10rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 6rpx;
border: 1px solid #999;
}
.item {
&.active {
.checkbox {
background-color: $my-main-color;
border-color: $my-main-color;
}
}
}
</style>

View File

@@ -1,257 +0,0 @@
<template>
<up-popup :show="modelValue" mode="bottom" :popup="false"
:mask="true" :closeable="true" :safe-area-inset-bottom="true"
close-icon-color="#ffffff" :z-index="uZIndex"
:maskCloseAble="maskCloseAble" @close="close">
<up-tabs v-if="modelValue" :list="genTabsList"
:scrollable="true" :current="tabsIndex" @change="tabsChange" ref="tabs"></up-tabs>
<view class="area-box">
<view class="u-flex" :class="{ 'change':isChange }">
<view class="area-item">
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
<scroll-view :scroll-y="true" style="height: 100%">
<up-cell-group>
<up-cell v-for="(item,index) in provinces"
:title="item.regionName" :arrow="false"
:index="index" :key="index"
@click="provinceChange(index)">
<template v-slot:right-icon>
<up-icon v-if="isChooseP&&province===index"
size="17" name="checkbox-mark"></up-icon>
</template>
</up-cell>
</up-cell-group>
</scroll-view>
</view>
</view>
<view class="area-item">
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
<scroll-view :scroll-y="true" style="height: 100%">
<up-cell-group v-if="isChooseP">
<up-cell v-for="(item,index) in citys"
:title="item.regionName" :arrow="false"
:index="index" :key="index"
@click="cityChange(index)">
<template v-slot:right-icon>
<up-icon v-if="isChooseC&&city===index"
size="17" name="checkbox-mark"></up-icon>
</template>
</up-cell>
</up-cell-group>
</scroll-view>
</view>
</view>
<view class="area-item">
<view class="u-padding-10 u-bg-gray" style="height: 100%;">
<scroll-view :scroll-y="true" style="height: 100%">
<up-cell-group v-if="isChooseC">
<up-cell v-for="(item,index) in areas"
:title="item.regionName" :arrow="false"
:index="index" :key="index"
@click="areaChange(index)">
<template v-slot:right-icon>
<up-icon v-if="isChooseA&&area===index"
size="17" name="checkbox-mark"></up-icon>
</template>
</up-cell>
</up-cell-group>
</scroll-view>
</view>
</view>
</view>
</view>
</up-popup>
</template>
<script>
import {region} from '@/http/api/system/region.js'
import provinces from "../common/province.js";
import citys from "../common/city.js";
import areas from "../common/area.js";
/**
* city-select 省市区级联选择器
* @property {String Number} z-index 弹出时的z-index值默认1075
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker默认true
* @property {String} default-region 默认选中的地区,中文形式
* @property {String} default-code 默认选中的地区,编号形式
*/
export default {
name: 'u-city-select',
props: {
// 通过双向绑定控制组件的弹出与收起
modelValue: {
type: Boolean,
default: false
},
// 默认显示的地区,可传类似["河北省", "秦皇岛市", "北戴河区"]
defaultRegion: {
type: Array,
default () {
return [];
}
},
// 默认显示地区的编码defaultRegion和areaCode同时存在areaCode优先可传类似["13", "1303", "130304"]
areaCode: {
type: Array,
default () {
return [];
}
},
// 是否允许通过点击遮罩关闭Picker
maskCloseAble: {
type: Boolean,
default: true
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
}
},
data() {
return {
cityValue: "",
isChooseP: false, //是否已经选择了省
province: 0, //省级下标
provinces: [],
isChooseC: false, //是否已经选择了市
city: 0, //市级下标
citys: citys[0],
isChooseA: false, //是否已经选择了区
area: 0, //区级下标
areas: areas[0][0],
tabsIndex: 0,
list:[]
}
},
async mounted() {
await this.getRegon()
this.init();
},
computed: {
isChange() {
return this.tabsIndex > 1;
},
genTabsList() {
let tabsList = [{
name: "请选择"
}];
if (this.isChooseP) {
console.log(this.province)
tabsList[0]['name'] = this.provinces[this.province]['regionName'];
tabsList[1] = {
name: "请选择"
};
}
if (this.isChooseC) {
tabsList[1]['name'] = this.citys[this.city]['regionName'];
tabsList[2] = {
name: "请选择"
};
}
if (this.isChooseA) {
tabsList[2]['name'] = this.areas[this.area]['regionName'];
}
return tabsList;
},
uZIndex() {
// 如果用户有传递z-index值优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
}
},
emits: ['city-change'],
methods: {
async getRegon(){
const res=await region()
this.provinces=res||[]
},
init() {
if (this.areaCode.length == 3) {
this.setProvince("", this.areaCode[0]);
this.setCity("", this.areaCode[1]);
this.setArea("", this.areaCode[2]);
} else if (this.defaultRegion.length == 3) {
this.setProvince(this.defaultRegion[0], "");
this.setCity(this.defaultRegion[1], "");
this.setArea(this.defaultRegion[2], "");
};
},
setProvince(regionName = "", value = "") {
this.provinces.map((v, k) => {
if (value ? v.value == value : v.regionName == regionName) {
this.provinceChange(k);
}
})
},
setCity(regionName = "", value = "") {
this.citys.map((v, k) => {
if (value ? v.value == value : v.regionName == regionName) {
this.cityChange(k);
}
})
},
setArea(regionName = "", value = "") {
this.areas.map((v, k) => {
if (value ? v.value == value : v.regionName == regionName) {
this.isChooseA = true;
this.area = k;
}
})
},
close() {
this.$emit('update:modelValue', false);
},
tabsChange(index) {
this.tabsIndex = index;
},
provinceChange(index) {
this.isChooseP = true;
this.isChooseC = false;
this.isChooseA = false;
this.province = index;
this.citys =this.provinces[index].children
this.tabsIndex = 1;
},
cityChange(index) {
this.isChooseC = true;
this.isChooseA = false;
this.city = index;
this.areas =this.provinces[this.province].children[index].children
this.tabsIndex = 2;
},
areaChange(index) {
this.isChooseA = true;
this.area = index;
let result = {};
result.province = this.provinces[this.province];
result.city = this.citys[this.city];
result.area = this.areas[this.area];
this.$emit('city-change', result);
this.close();
}
}
}
</script>
<style lang="scss">
.area-box {
width: 100%;
overflow: hidden;
height: 800rpx;
>view {
width: 150%;
transition: transform 0.3s ease-in-out 0s;
transform: translateX(0);
&.change {
transform: translateX(-33.3333333%);
}
}
.area-item {
width: 33.3333333%;
height: 800rpx;
}
}
</style>

View File

@@ -1,35 +0,0 @@
export const userTypes={
'0':'个体商户',
'1':'企业商户',
}
export const contactPersonTypes={
'LEGAL':'经营者/法定代表人',
'SUPER':'经办人',
}
export const certTypes={
'0':'身份证'
}
export const companyChildTypes={
'1':'普通企业',
'2':'事业单位',
'3':'政府机关',
'4':'社会组织',
}
export const sexs={
'0':'男',
'1':'女'
}
export const settlementTypes={
'0':'非法人结算',
'1':'法人结算'
}
export const settlementCardTypes={
'11':'对私借记卡',
'21':'对公借记卡',
}

View File

@@ -1,357 +0,0 @@
<template>
<view class="min-page bg-f7 u-font-28 color-333">
<up-sticky>
<view class="top u-flex">
<up-select :options="statusList" @select="statusListSelect">
<template #text>
<text v-if="query.status">{{returnStatusLabel(query.status)}}</text>
<text v-else>状态</text>
</template>
</up-select>
<view class="u-flex-1 u-p-l-32">
<up-search placeholder="店铺名称" v-model="query.shopName" @search="search" @clear="search"
@custom="search"></up-search>
</view>
</view>
</up-sticky>
<view class="box">
<view class="container" v-for="(item,index) in list" :key="index">
<view>
<view class="">
<text class="color-666">商户号</text>
<text class="font-bold">{{item.merchantCode}}</text>
</view>
<view class="u-m-t-24">
<text class="color-666">商户简称</text>
<text class="font-bold">{{item.shortName}}</text>
</view>
<view class="u-m-t-24">
<text class="color-666">店铺名称</text>
<text class="font-bold">{{item.shopName}}</text>
</view>
<view class="u-m-t-24 u-flex u-col-center">
<text class="color-666">商户类型</text>
<view class="types font-bold">{{returnType(item.userType)}}</view>
</view>
<view class="status">
<view class="u-flex u-row-between ">
<text class="font-bold">支付宝进件状态</text>
<view class="state" :class="returnStatusClass(item.alipayStatus)">
{{returnStatusLabel(item.alipayStatus)}}
</view>
</view>
<view v-if="item.alipayStatus==='SIGN'" class="u-m-t-16">
<my-button @click="showCode(item,'aliPay')">查看签约码</my-button>
</view>
<view class="u-m-t-14" v-if="item.alipayErrorMsg">
<up-alert title="拒绝原因" type="error" :description="item.alipayErrorMsg"></up-alert>
</view>
</view>
<view class="status">
<view class="">
<view class="u-flex u-row-between">
<text class="font-bold">微信进件状态</text>
<view class="state" :class="returnStatusClass(item.wechatStatus)">
{{returnStatusLabel(item.wechatStatus)}}
</view>
</view>
<view v-if="item.wechatStatus==='SIGN'" class="u-m-t-16">
<my-button @click="showCode(item,'wx')">查看签约码</my-button>
</view>
</view>
<view class="u-m-t-14" v-if="item.wechatErrorMsg">
<up-alert title="拒绝原因" type="error" :description="item.wechatErrorMsg"></up-alert>
</view>
</view>
<view class="u-m-t-24 u-flex u-col-center">
<text class="color-666">最后提交时间</text>
<view class=" font-bold">{{item.updateTime}}</view>
</view>
<view class="u-m-t-24 u-flex u-col-center">
<text class="color-666">创建时间</text>
<view class=" font-bold">{{item.createTime}}</view>
</view>
<view class="u-flex u-m-t-32 u-row-right gap-20">
<view style="min-width: 160rpx;" v-if="
item.wechatStatus == 'INIT' || item.wechatStatus == 'AUDIT'||item.wechatStatus == 'SIGN' || item.alipayStatus == 'INIT' || item.alipayStatus == 'AUDIT' ||item.alipayStatus == 'SIGN'
">
<my-button @click="queryStatus(item)">查询</my-button>
</view>
<view style="min-width: 160rpx;">
<my-button @click="toEdit(item)">编辑</my-button>
</view>
</view>
</view>
</view>
<template v-if="query.shopName">
<up-empty v-if="list.length<=0" text="未搜索到相关信息"></up-empty>
<up-loadmore :status="isEnd?'nomore':'loading'" v-else></up-loadmore>
</template>
<template v-else>
<up-empty v-if="list.length<=0" text="未搜索到相关信息"></up-empty>
<up-loadmore v-else :status="isEnd?'nomore':'loading'"></up-loadmore>
</template>
</view>
<view style="height: 140rpx;"></view>
<view class="bottom">
<my-button @click="showShopSelect=true">添加进件</my-button>
</view>
<shopSelect v-model="showShopSelect" @confirm="toAdd"></shopSelect>
<up-popup :show="codeShow" mode="center" @close="codeShow=false"
round="16rpx"
:close-on-click-overlay="true">
<view style="border-radius: 16rpx;overflow: hidden;" class="u-p-b-30">
<up-image width="200" height="200" :src="code"></up-image>
<view class="u-m-t-0 text-center" v-if="codeType=='wx'">请打开微信扫码</view>
<view class="u-m-t-0 text-center" v-if="codeType=='aliPay'">请打开支付宝扫码</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import {
userTypes
} from '../data.js'
const code = ref('')
const codeShow = ref(false)
const codeType=ref('')
function returnType(type) {
if (userTypes[type]) {
return userTypes[type]
}
return ''
}
import {
reactive,
ref,
watch
} from 'vue';
import shopSelect from '../components/shop-select.vue'
import {
onReachBottom,
onShow
} from '@dcloudio/uni-app'
import {
getList,
queryEntry
} from '@/http/api/order/entryManager.js'
// WAIT 待提交
// INIT 待处理
// AUDIT 待审核
// SIGN 待签约
// FINISH 已完成
// REJECTED 失败
const statusList = [{
value: 'WAIT',
name: '待提交',
class: 'gray'
},
{
value: 'INIT',
name: '待处理',
class: 'warning'
},
{
value: 'AUDIT',
name: '待审核',
class: 'warning'
},
{
value: 'SIGN',
name: '待签约',
class: 'warning'
},
{
value: 'FINISH',
name: '已完成',
class: 'success'
},
{
value: 'REJECTED',
name: '失败',
class: 'error'
},
]
function statusListSelect(e) {
query.status = e.value
}
const statusLabelJson = {
'REJECTED': '已拒绝'
}
const statusClassJson = {
'REJECTED': 'error'
}
function returnStatusLabel(state) {
const item = statusList.find(v => v.value == state)
if (item) {
return item.name
}
return ''
}
function returnStatusClass(state) {
const item = statusList.find(v => v.value == state)
if (item) {
return item.class
}
return ''
}
function showCode(item, type) {
if (type == 'wx') {
code.value = item.wechatSignUrl
}
if (type == 'aliPay') {
code.value = item.alipaySignUrl
}
codeType.value=type
codeShow.value = true
}
const showShopSelect = ref(false)
const query = reactive({
page: 1,
size: 10,
shopName: '',
status: ''
})
watch(() => query.status, (newval) => {
search()
})
const isEnd = ref(false)
const list = ref([])
function search() {
isEnd.value = false
query.page = 1
getData()
}
function toAdd(shop) {
console.log(shop)
uni.navigateTo({
url: '/entryManager/add/add?shopId=' + shop.shopId
})
}
function toEdit(shop) {
console.log(shop)
uni.navigateTo({
url: '/entryManager/add/add?shopId=' + shop.shopId + '&licenceNo=' + shop.licenceNo
})
}
function getData() {
getList(query).then(res => {
isEnd.value = query.page >= res.totalPage * 1
if (query.page == 1) {
list.value = res.records
} else {
list.value.push(...res.records)
}
})
}
function queryStatus(item) {
queryEntry({
licenceNo: item.licenceNo,
shopId: item.shopId
}).then(res => {
isEnd.value = false
query.page = 1;
getData()
})
}
onReachBottom(() => {
if (!isEnd.value) {
query.page++
}
})
onShow(getData)
</script>
<style lang="scss" scoped>
.min-page {
.box {
padding: 32rpx 28rpx;
}
}
.container {
padding: 32rpx 28rpx;
border-radius: 16rpx;
margin-bottom: 32rpx;
background-color: #fff;
}
.bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
z-index: 100;
padding: 32rpx 28rpx;
padding-bottom: 40rpx;
}
.types {}
.status {
margin-top: 24rpx;
background-color: #f7f7f7;
padding: 24rpx 28rpx;
border-radius: 4rpx;
.state {
padding: 8rpx 18rpx;
border-radius: 8rpx;
border: 2rpx solid #333;
&.success {
border-color: rgba(123, 209, 54, 1);
color: rgba(123, 209, 54, 1);
background: rgba(123, 209, 54, 0.12);
}
&.warning {
border-color: rgba(255, 141, 40, 1);
color: rgba(255, 141, 40, 1);
background: rgba(255, 141, 40, 0.12);
}
&.error {
border-color: #FF1C1C;
color: #FF1C1C;
background: rgba(255, 28, 28, 0.18);
}
&.gray {
color: #bbb;
background-color: #f7f7f7;
border-color: #bbb;
}
}
}
.top {
padding: 32rpx 28rpx;
background-color: #fff;
}
</style>

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,200 +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
});
}
export function getShopUser(data) {
return request({
url: `account/admin/shopUser`,
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,17 +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 pointsConfig(data) {
return request({
url: MARKET_URL+`/admin/points/config`,
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,71 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "order";
export function getList(data) {
return request({
url: urlType + '/admin/data/entryManager/list',
method: "GET",
data: {
...data,
},
});
}
// ocr识别填充
export function getInfoByImg(data) {
return request({
url: urlType + '/admin/data/entryManager/getInfoByImg',
method: "GET",
data: {
...data,
},
});
}
// 查询银行支行列表
export function bankBranchList(data) {
return request({
url: urlType + '/admin/data/entryManager/bankBranchList',
method: "GET",
data: {
...data,
},
});
}
// 获取进件信息
export function getEntryManager(data) {
return request({
url: urlType + '/admin/data/entryManager',
method: "GET",
data: {
...data,
},
});
}
//主动查询进件信息状态
export function queryEntry(data) {
return request({
url: urlType + '/admin/data/entryManager/queryEntry',
method: "GET",
data: {
...data,
},
});
}
//申请进件
export function addEntryManager(data) {
return request({
url: urlType + '/admin/data/entryManager',
method: "POST",
data: {
...data,
},
});
}

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

@@ -15,17 +15,6 @@ export function getShopInfo(data, urlType = 'account') {
})
}
export function adminShopList(data, urlType = 'account') {
return request({
url: `${urlType}/admin/shopInfo`,
method: "get",
data: {
...data
}
})
}
/**
* 修改店铺详情
* @returns
@@ -67,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

@@ -1,23 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "system";
export function bankInfo(data) {
return request({
url: urlType + '/admin/common/bankInfo',
method: "GET",
data: {
...data,
},
});
}
export function mccCategory(data) {
return request({
url: urlType + '/admin/common/category',
method: "GET",
data: {
...data,
},
});
}

View File

@@ -1,12 +0,0 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "system";
export function region(data) {
return request({
url: urlType + '/admin/common/region',
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,262 +1,243 @@
/**
* 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())
let baseUrl = appConfig.returnBaseUrl({apiType:'java'});
const loadingShowTime = 200;
// 测试服
// #ifdef H5
let baseUrl = '/api/'
// #endif
// #ifndef H5
// let baseUrl = 'https://tapi.cashier.sxczgkj.cn/'
//预发布
// let baseUrl = 'https://pre-cashieradmin.sxczgkj.cn'
function getHeader() {
const headerObject = {};
headerObject["token"] = storageManage.token();
headerObject["shopId"] = uni.getStorageSync("shopInfo").id;
headerObject["platformType"] = "APP";
//正式
// let baseUrl = 'https://cashier.sxczgkj.com/'
let baseUrl = appConfig.env.JEEPAY_BASE_URL
// #endif
return headerObject;
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;
// 判断是否请求完成(用作 是否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 = "/prodPhpApi/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,218 +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';
let baseUrl = appConfig.returnBaseUrl({apiType:'java'});
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

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