13 Commits

50 changed files with 8253 additions and 2163 deletions

View File

@@ -1,8 +1,8 @@
<template>
<view class="upload-file" @click="chooseImage">
<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"></image>
<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>
@@ -11,79 +11,152 @@
</template>
<script setup>
import { uploadFile } from '@/http/api/index.js';
import {
uploadFile
} from '@/http/api/index.js';
import {
ref
} from 'vue';
import { reactive, ref, watch } from 'vue';
const modelValue = defineModel({
type: String,
default: ''
});
function chooseImage() {
uni.chooseImage({
count: 1, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'],
success: async function (res) {
uni.showLoading({
title: '上传中'
});
console.log(res);
const fileRes = await uploadFile(res.tempFiles[0]);
uni.hideLoading();
if (fileRes) {
modelValue.value = fileRes;
} else {
uni.showToast({
title: '上传失败',
icon: 'none'
});
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 clearImg() {
modelValue.value = '';
}
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 {
.upload-file {
$size: 128rpx;
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;
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>
</style>

View File

@@ -0,0 +1,222 @@
<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,29 +1,108 @@
//当前环境 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
encryptKey: '1234567890123456', // http数据加解密的key
baseUrl: "",
}
export default appConfig;

545
entryManager/add/add.vue Normal file
View File

@@ -0,0 +1,545 @@
<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

@@ -0,0 +1,253 @@
<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 : modelValue.value
}
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

@@ -0,0 +1,292 @@
<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

@@ -0,0 +1,306 @@
<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

@@ -0,0 +1,81 @@
<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

@@ -0,0 +1,213 @@
<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

@@ -0,0 +1,289 @@
<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

@@ -0,0 +1,362 @@
<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

@@ -0,0 +1,165 @@
<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

@@ -0,0 +1,193 @@
<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

@@ -0,0 +1,178 @@
<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>

288
entryManager/add/data.js Normal file
View File

@@ -0,0 +1,288 @@
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

@@ -0,0 +1 @@
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

@@ -0,0 +1,302 @@
<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

@@ -0,0 +1,203 @@
<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

@@ -0,0 +1,257 @@
<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>

35
entryManager/data.js Normal file
View File

@@ -0,0 +1,35 @@
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

@@ -0,0 +1,357 @@
<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

@@ -188,4 +188,13 @@ export function shopUserFlow(data) {
method: 'get',
data
});
}
}
export function getShopUser(data) {
return request({
url: `account/admin/shopUser`,
method: 'get',
data
});
}

17
http/api/market/points.js Normal file
View File

@@ -0,0 +1,17 @@
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

@@ -0,0 +1,71 @@
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

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

23
http/api/system/common.js Normal file
View File

@@ -0,0 +1,23 @@
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,
},
});
}

12
http/api/system/region.js Normal file
View File

@@ -0,0 +1,12 @@
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

@@ -16,22 +16,9 @@ import go from "@/commons/utils/go.js";
import { reject } from "lodash";
// 设置node环境
// envConfig.changeEnv(storageManage.env('production')) //正式
envConfig.changeEnv(storageManage.env("development")); //测试
// 测试服
// #ifdef H5
let baseUrl = "/javaapi/";
// #endif
// #ifndef H5
// let baseUrl = 'https://tapi.cashier.sxczgkj.cn/'
//预发布
// let baseUrl = 'https://pre-cashieradmin.sxczgkj.cn'
//正式
// let baseUrl = 'https://cashier.sxczgkj.com/'
let baseUrl = appConfig.env.JEEPAY_BASE_URL;
// #endif
// envConfig.changeEnv(storageManage.env("development")); //测试
let baseUrl = appConfig.returnBaseUrl({apiType:'java'});
const loadingShowTime = 200;
function getHeader() {

View File

@@ -2,7 +2,7 @@
// const baseURL : string = 'https://newblockwlx.sxczgkj.cn/index.php/api/'
let baseURL: string = "http://192.168.1.42:8787/api/";
// #ifdef H5
baseURL = "/phpapi/api/";
baseURL = "/prodPhpApi/api/";
// #endif
import go from "@/commons/utils/go.js";

View File

@@ -14,24 +14,7 @@ import storageManage from '@/commons/utils/storageManage.js'
import infoBox from "@/commons/utils/infoBox.js"
import go from '@/commons/utils/go.js';
import { reject } from 'lodash';
// 设置node环境
envConfig.changeEnv(storageManage.env('production'))
// envConfig.changeEnv(storageManage.env('development'))
// 测试服
// #ifdef H5
let baseUrl = '/api/'
// #endif
// #ifndef H5
// let baseUrl = 'https://tapi.cashier.sxczgkj.cn/'
//预发布
// let baseUrl = 'https://pre-cashieradmin.sxczgkj.cn'
//正式
// let baseUrl = 'https://cashier.sxczgkj.com/'
let baseUrl = appConfig.env.JEEPAY_BASE_URL
// #endif
let baseUrl = appConfig.returnBaseUrl({apiType:'java'});
const loadingShowTime = 200
function getHeader(){

View File

@@ -0,0 +1,219 @@
<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:'请选择用户'
})
}
modelValue.value = selItem.value.id
if(modelValue.value!=selItem.value.id){
emits('change')
}
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,34 +1,25 @@
<template>
<view class="min-page u-font-28">
<up-sticky>
<view class="bg-fff top">
<view class="bg-fff container u-flex u-m-b-48">
<image
style="width: 60rpx; height: 60rpx"
src="/pageMarket/static/images/distribution.png"
></image>
<view class="u-flex-1 u-flex u-p-l-24">
<view class="u-font-28 u-flex-1 u-p-r-4">
<view class="color-333 font-bold">分销</view>
<view class="color-666 u-m-t-4 u-font-24"
>用户成为业务员可促进消费
</view>
</view>
<up-switch
v-model="distributionStore.config.isEnable"
size="18"
:active-value="1"
:inactive-value="0"
></up-switch>
</view>
</view>
<my-tabs v-model="active" :list="tabs" textKey="label"></my-tabs>
<view
v-if="active == 1 || active == 2"
class="u-flex u-row-between u-m-t-32"
style="gap: 58rpx"
>
<view class="u-flex-1 filter-box" style="border-radius: 100rpx">
<view class="min-page u-font-28">
<up-sticky>
<view class="bg-fff top">
<view class="bg-fff container u-flex u-m-b-48">
<image style="width: 60rpx; height: 60rpx" src="/pageMarket/static/images/distribution.png"></image>
<view class="u-flex-1 u-flex u-p-l-24">
<view class="u-font-28 u-flex-1 u-p-r-4">
<view class="color-333 font-bold">分销</view>
<view class="color-666 u-m-t-4 u-font-24">用户成为业务员可促进消费
</view>
</view>
<up-switch v-model="distributionStore.config.isEnable" size="18" :active-value="1"
:inactive-value="0"></up-switch>
</view>
</view>
<my-tabs v-model="active" :list="tabs" textKey="label"></my-tabs>
<view v-if="active == 1 || active == 2" class="u-flex u-row-between u-m-t-32" style="gap: 58rpx">
<view class="u-flex-1">
<fenXiaoUserSelect v-model="userId" @change="getList()" />
</view>
<!-- <view class="u-flex-1 filter-box" style="border-radius: 100rpx">
<up-icon name="search" size="18"></up-icon>
<input
class="u-m-l-10 u-font-28"
@@ -39,476 +30,439 @@
@blur="keyWordBlur"
/>
<up-icon v-if="keyWord" name="close" size="14" @click="clearKeyWord"></up-icon>
</view>
<view
class="u-flex-1 u-font-28 filter-box u-flex u-row-between"
@click="showTimeArea = true"
>
<template
v-if="userComponentQuery.startTime && userComponentQuery.endTime"
>
<text class="u-font-20">
{{ userComponentQuery.startTime }} -
{{ userComponentQuery.endTime }}
</text>
<view @click.stop="clearTime">
<up-icon name="close" size="14"></up-icon>
</view>
</template>
<template v-else>
<text class="color-999">请选择日期范围</text>
<up-icon name="arrow-right" size="12"></up-icon>
</template>
</view>
</view>
<view
v-if="active == 3"
class="u-flex u-row-between u-m-t-32"
style="gap: 30rpx"
>
<view
class="u-font-28 filter-box u-flex u-row-between"
@click="showActions = true"
>
<template v-if="selActions && selActions.value">
<text class="u-font-28 u-m-r-10">{{ selActions.name }} </text>
</template>
<template v-else>
<text class="color-999 u-m-r-10">全部</text>
</template>
<up-icon name="arrow-down" size="12"></up-icon>
</view>
<view class="u-flex-1 filter-box" style="border-radius: 100rpx">
<up-icon name="search" size="18"></up-icon>
<input
class="u-m-l-10 u-font-28"
type="text"
placeholder-class="color-999 u-font-28"
placeholder="分销员昵称/手机号"
v-model="keyWord"
@blur="keyWordBlur"
/>
</view>
<view
class="u-flex-1 u-font-28 filter-box u-flex u-row-between"
@click="showTimeArea = true"
>
<template
v-if="userComponentQuery.startTime && userComponentQuery.endTime"
>
<text class="u-font-20">
{{ userComponentQuery.startTime }} -
{{ userComponentQuery.endTime }}
</text>
<view @click.stop="clearTime">
<up-icon name="close" size="14"></up-icon>
</view>
</template>
<template v-else>
<text class="color-999">请选择日期</text>
<up-icon name="arrow-right" size="12"></up-icon>
</template>
</view>
</view>
<view v-if="active == 2" class="u-flex u-p-l-32 u-p-r-32 u-m-t-32">
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
</view> -->
<view class="u-flex-1 u-font-28 filter-box u-flex u-row-between" @click="showTimeArea = true">
<template v-if="userComponentQuery.startTime && userComponentQuery.endTime">
<text class="u-font-20">
{{ userComponentQuery.startTime }} -
{{ userComponentQuery.endTime }}
</text>
<view @click.stop="clearTime">
<up-icon name="close" size="14"></up-icon>
</view>
</template>
<template v-else>
<text class="color-999">请选择日期范围</text>
<up-icon name="arrow-right" size="12"></up-icon>
</template>
</view>
</view>
<view v-if="active == 3" class="u-flex u-row-between u-m-t-32" style="gap: 30rpx">
<view class="u-font-28 filter-box u-flex u-row-between" @click="showActions = true">
<template v-if="selActions && selActions.value">
<text class="u-font-28 u-m-r-10">{{ selActions.name }} </text>
</template>
<template v-else>
<text class="color-999 u-m-r-10">全部</text>
</template>
<up-icon name="arrow-down" size="12"></up-icon>
</view>
<view class="u-flex-1">
<fenXiaoUserSelect v-model="userId" :maxWidth="200" @change="getList()" />
</view>
<!-- <view class="u-flex-1 filter-box" style="border-radius: 100rpx">
<up-icon name="search" size="18"></up-icon>
<input class="u-m-l-10 u-font-28" type="text" placeholder-class="color-999 u-font-28"
placeholder="分销员昵称/手机号" v-model="keyWord" @blur="keyWordBlur" />
</view> -->
<view class="u-flex-1 u-font-28 filter-box u-flex u-row-between" @click="showTimeArea = true">
<template v-if="userComponentQuery.startTime && userComponentQuery.endTime">
<text class="u-font-20">
{{ userComponentQuery.startTime }} -
{{ userComponentQuery.endTime }}
</text>
<view @click.stop="clearTime">
<up-icon name="close" size="14"></up-icon>
</view>
</template>
<template v-else>
<text class="color-999">请选择日期</text>
<up-icon name="arrow-right" size="12"></up-icon>
</template>
</view>
</view>
<view v-if="active == 2" class="u-flex u-p-l-32 u-p-r-32 u-m-t-32">
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
listRes.totalCount
}}</view>
<view class="u-font-24 color-666 u-m-t-16">支付开通人数</view>
</view>
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
<view class="u-font-24 color-666 u-m-t-16">支付开通人数</view>
</view>
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
listRes.totalAmount
}}</view>
<view class="u-font-24 color-666 u-m-t-16">已支付金额</view>
</view>
</view>
<view v-if="active == 3" class="u-flex u-m-t-32">
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
<view class="u-font-24 color-666 u-m-t-16">已支付金额</view>
</view>
</view>
<view v-if="active == 3" class="u-flex u-m-t-32">
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
listRes.successAmount
}}</view>
<view class="u-font-24 color-666 u-m-t-16">已入账金额</view>
</view>
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
<view class="u-font-24 color-666 u-m-t-16">已入账金额</view>
</view>
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
listRes.pendingAmount || 0
}}</view>
<view class="u-font-24 color-666 u-m-t-16">待入账金额</view>
</view>
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
<view class="u-font-24 color-666 u-m-t-16">待入账金额</view>
</view>
<view class="u-flex-1 u-text-center">
<view class="u-font-32 color-main font-bold">{{
listRes.balanceAmount
}}</view>
<view class="u-font-24 color-666 u-m-t-16">
<text>运营余额</text>
<text class="color-main" @click="go.to('PAGES_PAY')"
>充值{{ ">" }}</text
>
</view>
</view>
</view>
</view>
</up-sticky>
<configVue v-if="active == 0"></configVue>
<fenxiaoUserListVue
v-if="active == 1"
:list="list"
:isEnd="isEnd"
@refresh="refresh"
></fenxiaoUserListVue>
<view class="u-font-24 color-666 u-m-t-16">
<text>运营余额</text>
<text class="color-main" @click="go.to('PAGES_PAY')">充值{{ ">" }}</text>
</view>
</view>
</view>
</view>
</up-sticky>
<configVue v-if="active == 0"></configVue>
<fenxiaoUserListVue v-if="active == 1" :list="list" :isEnd="isEnd" @refresh="refresh"></fenxiaoUserListVue>
<openListVue
v-if="active == 2"
:list="list"
:isEnd="isEnd"
@refresh="refresh"
></openListVue>
<fenxiaoMingxiVue
v-if="active == 3"
:list="list"
:isEnd="isEnd"
@refresh="refresh"
></fenxiaoMingxiVue>
<!-- 选择门店 -->
<shopSelActionSheetVue
@choose="chooseShop"
v-model="showShopSelActionSheet"
title="选择门店"
>
</shopSelActionSheetVue>
<openListVue v-if="active == 2" :list="list" :isEnd="isEnd" @refresh="refresh"></openListVue>
<fenxiaoMingxiVue v-if="active == 3" :list="list" :isEnd="isEnd" @refresh="refresh"></fenxiaoMingxiVue>
<!-- 选择门店 -->
<shopSelActionSheetVue @choose="chooseShop" v-model="showShopSelActionSheet" title="选择门店">
</shopSelActionSheetVue>
<dateAreaSel
:show="showTimeArea"
:minYear="2022"
@close="showTimeArea = false"
@confirm="confirmTimeArea"
></dateAreaSel>
<dateAreaSel :show="showTimeArea" :minYear="2022" @close="showTimeArea = false" @confirm="confirmTimeArea">
</dateAreaSel>
<!-- 分销明细状态 -->
<up-action-sheet
:show="showActions"
:actions="actions"
@select="handleSelect"
@close="showActions = false"
cancel-text="取消"
></up-action-sheet>
</view>
<!-- 分销明细状态 -->
<up-action-sheet :show="showActions" :actions="actions" @select="handleSelect" @close="showActions = false"
cancel-text="取消"></up-action-sheet>
</view>
</template>
<script setup>
import {
onLoad,
onReady,
onShow,
onPageScroll,
onReachBottom,
onBackPress,
} from "@dcloudio/uni-app";
import go from "@/commons/utils/go.js";
import { ref, onMounted, watch, provide } from "vue";
import * as consumeCashbackApi from "@/http/api/market/consumeCashback.js";
import * as distributionApi from "@/http/api/market/distribution.js";
import configVue from "./components/config.vue";
import shopSelActionSheetVue from "@/pageMarket/components/shop-sel-action-sheet.vue";
import dateAreaSel from "@/components/date-range-picker/date-range-picker.vue";
import fenxiaoUserListVue from "./components/fenxiao-user-list.vue";
import openListVue from "./components/open-list.vue";
import fenxiaoMingxiVue from "./components/fenxiao-mingxi.vue";
import {
onLoad,
onReady,
onShow,
onPageScroll,
onReachBottom,
onBackPress,
} from "@dcloudio/uni-app";
import go from "@/commons/utils/go.js";
import {
ref,
onMounted,
watch,
provide
} from "vue";
import * as consumeCashbackApi from "@/http/api/market/consumeCashback.js";
import * as distributionApi from "@/http/api/market/distribution.js";
import fenXiaoUserSelect from './components/fenxiao-user-select.vue'
import configVue from "./components/config.vue";
import shopSelActionSheetVue from "@/pageMarket/components/shop-sel-action-sheet.vue";
import dateAreaSel from "@/components/date-range-picker/date-range-picker.vue";
import fenxiaoUserListVue from "./components/fenxiao-user-list.vue";
import openListVue from "./components/open-list.vue";
import fenxiaoMingxiVue from "./components/fenxiao-mingxi.vue";
import { useDistributionStore } from "@/store/market.js";
import { reactive } from "vue";
import {
useDistributionStore
} from "@/store/market.js";
import {
reactive
} from "vue";
const actions = [
{
name: "全部",
value: "",
},
{
name: "已入账",
value: "success",
},
{
name: "待入账",
value: "pending",
},
{
name: "已退款",
value: "refund",
},
];
function clearKeyWord() {
keyWord.value = "";
userComponentQuery.user = "";
}
function clearTime() {
userComponentQuery.startTime = "";
userComponentQuery.endTime = "";
}
const selActions = ref("");
const showActions = ref(false);
function handleSelect(e) {
selActions.value = e;
}
const actions = [{
name: "全部",
value: "",
},
{
name: "已入账",
value: "success",
},
{
name: "待入账",
value: "pending",
},
{
name: "已退款",
value: "refund",
},
];
const distributionStore = useDistributionStore();
distributionStore.getConfig();
provide("distributionStore", distributionStore);
provide("distributionApi", distributionApi);
const showTimeArea = ref(false);
function confirmTimeArea(e) {
console.log(e);
userComponentQuery.startTime = e[0];
userComponentQuery.endTime = e[1];
}
const tabs = [
{
label: "基础设置",
value: "basic",
},
{
label: "分销员",
value: "user",
},
{
label: "开通记录",
value: "recoders",
},
{
label: "分销明细",
value: "details",
},
];
const keyWord = ref("");
function keyWordBlur() {
userComponentQuery.user = keyWord.value;
}
const userComponentQuery = reactive({
user: "",
startTime: "",
endTime: "",
});
const userId = ref('')
const form = ref({
isEnable: 0,
});
function clearKeyWord() {
keyWord.value = "";
userComponentQuery.user = "";
}
const list = ref([]);
const pageNum = ref(1);
const isEnd = ref(false);
const selShop = ref({
shopId: "",
shopName: "",
});
const searchText = ref("");
function clearTime() {
userComponentQuery.startTime = "";
userComponentQuery.endTime = "";
}
const selActions = ref("");
const showActions = ref(false);
function search() {
pageNum.value = 1;
getList();
}
function handleSelect(e) {
selActions.value = e;
}
function chooseShop(e) {
selShop.value = e;
}
const distributionStore = useDistributionStore();
distributionStore.getConfig();
provide("distributionStore", distributionStore);
provide("distributionApi", distributionApi);
const showTimeArea = ref(false);
watch(
() => selShop.value.shopId,
(newval) => {
pageNum.value = 1;
getList();
}
);
function confirmTimeArea(e) {
console.log(e);
userComponentQuery.startTime = e[0];
userComponentQuery.endTime = e[1];
}
const tabs = [{
label: "基础设置",
value: "basic",
},
{
label: "分销员",
value: "user",
},
{
label: "开通记录",
value: "recoders",
},
{
label: "分销明细",
value: "details",
},
];
watch(
() => userComponentQuery,
(newval) => {
isEnd.value = false;
pageNum.value = 1;
const keyWord = ref("");
getList();
},
{
deep: true,
}
);
function keyWordBlur() {
userComponentQuery.user = keyWord.value;
}
const userComponentQuery = reactive({
user: "",
startTime: "",
endTime: "",
});
function refresh() {
isEnd.value = false;
pageNum.value = 1;
getList();
}
const form = ref({
isEnable: 0,
});
const listRes = ref({});
async function getList() {
let res = null;
if (active.value == 1) {
//分销员列表
res = await distributionApi.distributionUser({
page: pageNum.value,
size: 10,
user: userComponentQuery.user,
startTime: userComponentQuery.startTime
? userComponentQuery.startTime + " 00:00:00"
: "",
endTime: userComponentQuery.endTime
? userComponentQuery.endTime + " 23:59:59"
: "",
});
}
if (active.value == 2) {
//开通记录
res = await distributionApi.openFlow({
page: pageNum.value,
size: 10,
key: userComponentQuery.user,
startTime: userComponentQuery.startTime
? userComponentQuery.startTime + " 00:00:00"
: "",
endTime: userComponentQuery.endTime
? userComponentQuery.endTime + " 23:59:59"
: "",
});
}
if (active.value == 3) {
//分销明细
res = await distributionApi.distributionFlow({
page: pageNum.value,
size: 10,
key: userComponentQuery.user,
status: selActions.value?.value || "",
startTime: userComponentQuery.startTime
? userComponentQuery.startTime + " 00:00:00"
: "",
endTime: userComponentQuery.endTime
? userComponentQuery.endTime + " 23:59:59"
: "",
});
}
if (res) {
listRes.value = res;
if (pageNum.value == 1) {
list.value = res.records || [];
} else {
list.value = [...list.value, ...(res.records || [])];
}
isEnd.value = pageNum.value >= res.totalPage * 1 ? true : false;
console.log(isEnd.value);
}
}
const list = ref([]);
const pageNum = ref(1);
const isEnd = ref(false);
const selShop = ref({
shopId: "",
shopName: "",
});
const searchText = ref("");
// 显示选择门店弹窗
const showShopSelActionSheet = ref(false);
function search() {
pageNum.value = 1;
getList();
}
function showShopSelActionSheetFun() {
showShopSelActionSheet.value = true;
}
const active = ref(0);
watch(
() => active.value,
(newval) => {
userComponentQuery.startTime = "";
userComponentQuery.endTime = "";
keyWord.value = "";
console.log(newval);
pageNum.value = 1;
getList();
}
);
watch(
() => active.value,
(newval) => {
function chooseShop(e) {
selShop.value = e;
}
refresh();
}
);
watch(
() => selActions.value,
() => {
refresh();
}
);
onReachBottom(() => {
if (!isEnd.value) {
pageNum.value++;
getList();
}
});
watch(
() => distributionStore.config.isEnable,
() => {
distributionStore.editConfig();
}
);
onShow(() => {
pageNum.value = 1;
getList();
});
watch(
() => selShop.value.shopId,
(newval) => {
pageNum.value = 1;
getList();
}
);
watch(
() => userComponentQuery,
(newval) => {
isEnd.value = false;
pageNum.value = 1;
getList();
}, {
deep: true,
}
);
function refresh() {
isEnd.value = false;
pageNum.value = 1;
getList();
}
const listRes = ref({});
async function getList() {
let res = null;
if (active.value == 1) {
//分销员列表
res = await distributionApi.distributionUser({
page: pageNum.value,
size: 10,
user: userComponentQuery.user,
id:userId.value,
startTime: userComponentQuery.startTime ?
userComponentQuery.startTime + " 00:00:00" : "",
endTime: userComponentQuery.endTime ?
userComponentQuery.endTime + " 23:59:59" : "",
});
}
if (active.value == 2) {
//开通记录
res = await distributionApi.openFlow({
page: pageNum.value,
size: 10,
id:userId.value,
key: userComponentQuery.user,
startTime: userComponentQuery.startTime ?
userComponentQuery.startTime + " 00:00:00" : "",
endTime: userComponentQuery.endTime ?
userComponentQuery.endTime + " 23:59:59" : "",
});
}
if (active.value == 3) {
//分销明细
res = await distributionApi.distributionFlow({
page: pageNum.value,
size: 10,
id:userId.value,
key: userComponentQuery.user,
status: selActions.value?.value || "",
startTime: userComponentQuery.startTime ?
userComponentQuery.startTime + " 00:00:00" : "",
endTime: userComponentQuery.endTime ?
userComponentQuery.endTime + " 23:59:59" : "",
});
}
if (res) {
listRes.value = res;
if (pageNum.value == 1) {
list.value = res.records || [];
} else {
list.value = [...list.value, ...(res.records || [])];
}
isEnd.value = pageNum.value >= res.totalPage * 1 ? true : false;
console.log(isEnd.value);
}
}
// 显示选择门店弹窗
const showShopSelActionSheet = ref(false);
function showShopSelActionSheetFun() {
showShopSelActionSheet.value = true;
}
const active = ref(0);
watch(
() => active.value,
(newval) => {
userComponentQuery.startTime = "";
userComponentQuery.endTime = "";
keyWord.value = "";
userId.value="";
console.log(newval);
pageNum.value = 1;
getList();
}
);
watch(
() => active.value,
(newval) => {
refresh();
}
);
watch(
() => selActions.value,
() => {
refresh();
}
);
onReachBottom(() => {
if (!isEnd.value) {
pageNum.value++;
getList();
}
});
watch(
() => distributionStore.config.isEnable,
() => {
distributionStore.editConfig();
}
);
onShow(() => {
pageNum.value = 1;
getList();
});
</script>
<style lang="scss" scoped>
.min-page {
background: #f7f7f7;
}
.min-page {
background: #f7f7f7;
}
.box {
}
.box {}
.top {
padding: 32rpx 24rpx;
}
.top {
padding: 32rpx 24rpx;
}
.list {
padding: 0 30rpx;
.list {
padding: 0 30rpx;
.item {
padding: 32rpx 24rpx;
border-radius: 14rpx;
background-color: #fff;
overflow: hidden;
}
}
.item {
padding: 32rpx 24rpx;
border-radius: 14rpx;
background-color: #fff;
overflow: hidden;
}
}
.tag {
border-radius: 12rpx;
padding: 8rpx 22rpx;
font-size: 28rpx;
.tag {
border-radius: 12rpx;
padding: 8rpx 22rpx;
font-size: 28rpx;
&.success {
background-color: #edfff0;
color: #5bbc6d;
}
&.success {
background-color: #edfff0;
color: #5bbc6d;
}
&.end {
background-color: #f7f7f7;
color: #999;
}
}
&.end {
background-color: #f7f7f7;
color: #999;
}
}
.my-btn {
font-size: 28rpx;
line-height: 36rpx;
padding: 8rpx 32rpx;
border-radius: 12rpx;
margin: 0;
}
.my-btn {
font-size: 28rpx;
line-height: 36rpx;
padding: 8rpx 32rpx;
border-radius: 12rpx;
margin: 0;
}
.edit-btn {
background: #e6f0ff;
color: $my-main-color;
}
.edit-btn {
background: #e6f0ff;
color: $my-main-color;
}
.delete-btn {
background: #ffe7e6;
color: #ff1c1c;
}
.filter-box {
display: flex;
padding: 8rpx 24rpx;
align-items: center;
border-radius: 8rpx;
border: 2rpx solid #d9d9d9;
background: #f7f7f7;
min-height: 62rpx;
box-sizing: border-box;
}
</style>
.delete-btn {
background: #ffe7e6;
color: #ff1c1c;
}
.filter-box {
display: flex;
padding: 8rpx 24rpx;
align-items: center;
border-radius: 8rpx;
border: 2rpx solid #d9d9d9;
background: #f7f7f7;
min-height: 62rpx;
box-sizing: border-box;
}
</style>

View File

@@ -213,7 +213,7 @@ function toMiniApp() {
uni.navigateToMiniProgram({
appId: 'wxd88fffa983758a30',
path: `/groupBuying/goodsDetail/goodsDetail?wareId=${query.id}&shopId=${query.shopId}`,
envVersion: 'trial', // 环境版本release(正式版)、trial(体验版)、develop(开发版)
envVersion: 'release', // 环境版本release(正式版)、trial(体验版)、develop(开发版)
success: () => {},
fail: () => {}
});

View File

@@ -210,7 +210,7 @@ function toMiniApp() {
uni.navigateToMiniProgram({
appId: 'wxd88fffa983758a30',
path: `/userPackage/goodsDetail/goodsDetail?id=${query.id}&shopId=${query.shopId}`,
envVersion: 'trial', // 环境版本release(正式版)、trial(体验版)、develop(开发版)
envVersion: 'release', // 环境版本release(正式版)、trial(体验版)、develop(开发版)
success: () => {},
fail: () => {}
});

View File

@@ -14,10 +14,13 @@
</view>
<my-tabs v-model="active" :list="tabs" textKey="label"></my-tabs>
<view v-if="active == 2" class="u-flex u-row-between u-m-t-32" style="gap: 58rpx">
<view class="u-flex-1 filter-box" style="border-radius: 100rpx">
<!-- <view class="u-flex-1 filter-box" style="border-radius: 100rpx">
<up-icon name="search" size="18"></up-icon>
<input class="u-m-l-10 u-font-28" type="text" placeholder-class="color-999 u-font-28" placeholder="搜索关键词" v-model="keyWord" @blur="keyWordBlur" />
<up-icon v-if="keyWord" name="close" size="14" @click="clearKeyWord"></up-icon>
</view> -->
<view class="u-flex-1">
<my-user-select v-model="userId" @change="search()" ></my-user-select>
</view>
<view class="u-flex-1 u-font-28 filter-box u-flex u-row-between" @click="showTimeArea = true">
<template v-if="userComponentQuery.startTime && userComponentQuery.endTime">
@@ -215,6 +218,7 @@ function refresh() {
}
const listRes = ref({});
const userId=ref('')
async function getList() {
let res = null;
if (active.value == 1) {
@@ -232,7 +236,8 @@ async function getList() {
res = await memberApi.orderList({
page: pageNum.value,
size: 10,
key: userComponentQuery.user,
// key: userComponentQuery.user,
key: userId.value,
startTime: userComponentQuery.startTime ? userComponentQuery.startTime + ' 00:00:00' : '',
endTime: userComponentQuery.endTime ? userComponentQuery.endTime + ' 23:59:59' : ''
});
@@ -264,6 +269,7 @@ watch(
userComponentQuery.startTime = '';
userComponentQuery.endTime = '';
keyWord.value = '';
userId.value=''
refresh();
}
);

View File

@@ -911,6 +911,24 @@
"navigationBarTitleText": "分销"
}
}]
},
{
"root": "entryManager",
"pages": [
{
"path": "index/index",
"style": {
"navigationBarTitleText": "进件管理"
}
},
{
"path": "add/add",
"style": {
"navigationBarTitleText": "进件"
}
}
]
}
// ,
// {

View File

@@ -23,11 +23,11 @@
<view class="u-flex-1 u-p-l-30 u-text-left">
<view class="u-flex">
<view class="u-line-1 u-font-36 font-bold" style="width: 80%;">{{shopInfo.shopName}}</view>
<view class="u-font-28 color-fff change-shop u-flex u-row-center" @click="changeShop">
<!-- <view class="u-font-28 color-fff change-shop u-flex u-row-center" @click="changeShop">
<image src="/static/change.png" class="u-m-t-2" style="width: 20rpx;height: 20rpx;"
mode=""></image>
<text class="u-m-l-6">切换店铺</text>
</view>
</view> -->
</view>
<view class="u-m-t-16">
<view style="color: rgba(255,255,255,0.83);" v-if="shopStaff">

View File

@@ -74,7 +74,7 @@ export default {
},
async init() {
const res = await menusStore.getMenus()
const arr=res.filter(v => v.type == 0 && v.children.length && !v.hidden).map(v => {
const arr=res.filter(v => v.type == 0 && v.children.length && !v.hidden && v.menuId!=1).map(v => {
return {
...v,
children: v.children.filter(child => child.type == 0 && !child.hidden)

View File

@@ -239,6 +239,7 @@
discount_sale_amount: form.price, //数量
pack_number: 0, //数量
is_gift: 0,
remark:form.note,
is_temporary: 1, //是否是临时菜
}
websocketUtil.send(JSON.stringify(params))
@@ -247,6 +248,7 @@
name: form.name,
lowPrice: form.price,
number: form.num,
remark:form.note,
is_temporary: 1, //是否是临时菜
})
clearInterval(timer)

View File

@@ -37,7 +37,7 @@
</view>
<view class="u-m-l-30 u-flex">
<text class="">积分</text>
<text class="color-main">{{item.accountPoints}}</text>
<text class="color-main">{{item.pointBalance||0}}</text>
</view>
</view>
</view>
@@ -113,7 +113,7 @@
headImg: '',
telephone: '',
amount: '0.00',
accountPoints: '0.00'
pointBalance: '0.00'
})
} else {
list[index].checked = true

View File

@@ -32,7 +32,7 @@
<view class="u-font-24 u-m-l-30 u-text-center">
<text>积分</text>
<text class="color-main">{{
pageData.user.accountPoints
pageData.user.pointBalance||0
}}</text>
</view>
</view>
@@ -88,7 +88,7 @@
<view class="u-flex">
<view>积分</view>
<view class="color-333 u-m-l-10">{{
pageData.user.accountPoints
pageData.user.pointBalance||0
}}</view>
</view>
<view class="u-flex">
@@ -935,14 +935,15 @@ function watchChooseuser() {
if (pageData.user.id == data.id) {
return;
}
if (data.id) {
const res = await shopUserDetail({
userId: data.userId,
});
pageData.user = res;
} else {
pageData.user = data;
}
pageData.user = data;
// if (data.id) {
// const res = await shopUserDetail({
// userId: data.userId,
// });
// pageData.user = res;
// } else {
// pageData.user = data;
// }
// 更新购物车和历史订单数据
uodateCartAndHistory();
});

View File

@@ -436,6 +436,7 @@
websocketUtil.offMessage();
websocketUtil.onMessage(async (res) => {
let msg = JSON.parse(res);
console.log('收到消息',msg)
let cartItem;
let cartArr = [];
// console.log("onMessage===",msg)
@@ -1349,13 +1350,13 @@
if (fileRes) {
uni.setStorageSync('stickData',{
table:data.table,
orderInfo:data.orderInfo
orderInfo:data.orderInfo,
limitTimeDiscount:data.limitTimeDiscount
})
go.to("PAGES_CREATE_ORDER_STICK", {
tableCode: data.table.tableCode,
number:fileRes,
isCreateOrderToDetail:isCreateOrderToDetail.value,
limitTimeDiscount:data.limitTimeDiscount
});

View File

@@ -2,23 +2,23 @@
<view class="default-box-padding bg-fff border-r-12 u-m-t-24">
<view class="u-flex u-row-between">
<view>订单状态</view>
<view>{{$dict.getDiceName(data.status,'orderStatus')}}</view>
<view>{{ $dict.getDiceName(data.status, 'orderStatus') }}</view>
</view>
<view class="u-flex u-row-between u-m-t-24">
<view>订单类型</view>
<view>{{$dict.getDiceName(data.dineMode,'dineMode')}}</view>
<view>{{ $dict.getDiceName(data.dineMode, 'dineMode') }}</view>
</view>
<view class="u-flex u-row-between u-m-t-24">
<view>桌位号</view>
<view>{{table.name||data.tableName}}</view>
<view>{{ table.name || data.tableName }}</view>
</view>
<view class="u-flex u-row-between u-m-t-24" v-if="seatFee.number">
<view>就餐人数</view>
<view>{{seatFee.number||''}}</view>
<view>{{ seatFee.number || '' }}</view>
</view>
<view class="u-flex u-row-between u-m-t-24">
<view>支付方式</view>
<view>{{$dict.getDiceName(data.payType,'payType')||''}}</view>
<view>{{ $dict.getDiceName(data.payType, 'payType') || '' }}</view>
</view>
<view class="u-flex u-row-between u-m-t-24">
<view>下单时间</view>
@@ -27,9 +27,9 @@
<view class="u-flex u-row-between u-m-t-24">
<view>订单编号</view>
<view class="u-flex">
<view>{{data.orderNo}}</view>
<view>{{ data.orderNo }}</view>
<view v-if="data.orderNo" class="u-m-l-6">
<up-copy :content="data.orderNo" >
<up-copy :content="data.orderNo">
<up-icon name="/static/copy.svg" :size="16"></up-icon>
</up-copy>
</view>
@@ -37,34 +37,44 @@
</view>
<view class="u-flex u-row-between u-m-t-24 u-col-top">
<view class="no-wrap">商家备注</view>
<view class="u-p-l-32 " style="max-width: 522rpx; word-wrap: break-word;">
{{data.remark}}
<view class="u-p-l-32" style="max-width: 522rpx; word-wrap: break-word">
{{ data.remark }}
</view>
</view>
<view class="u-flex u-row-between u-m-t-24 u-col-top">
<view class="no-wrap">打印状态</view>
<view class="u-p-l-32" style="max-width: 522rpx; word-wrap: break-word; color: red">
<template v-if="JSON.parse(data.printStatus).length > 0">
打印失败{{
JSON.parse(data.printStatus)
.map((item) => item.name)
.join('、')
}}
</template>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { ref } from 'vue';
const props = defineProps({
data: {
type: Object,
default: () => {}
},
table: {
type: Object,
default: () => {}
},
seatFee: {
type: Object,
default: () => {
totalNumber: 0
}
const props = defineProps({
data: {
type: Object,
default: () => {}
},
table: {
type: Object,
default: () => {}
},
seatFee: {
type: Object,
default: () => {
totalNumber: 0;
}
})
}
});
</script>
<style lang="scss" scoped>
</style>
<style lang="scss" scoped></style>

View File

@@ -2,20 +2,18 @@
<my-model ref="model" :title="title" iconColor="#000" @close="resetForm">
<template #desc>
<view class="u-text-left u-p-30 color-666 u-font-28">
<view class="u-m-t-32 u-flex ">
<view class="" v-if="accountPoints.calcRes.usable">
<view class="u-m-t-32 u-flex">
<view class="" v-if="pointDeductionRule.enableRewards">
<text class="color-red">*</text>
<text class=""
v-if="accountPoints.calcRes.equivalentPoints">100积分等于{{to2(accountPoints.calcRes.equivalentPoints*100)}},</text>
v-if="pointDeductionRule.equivalentPoints">{{ pointDeductionRule.equivalentPoints }}积分等于1,</text>
<text>
最大抵扣积分{{accountPoints.calcRes.maxUsablePoints}}
</text>
<text>,
最小抵扣积分0
最大抵扣积分{{ maxCanUsePoints }}
</text>
<text>, 最小抵扣积分0 </text>
</view>
</view>
<view class="u-m-t-40 u-flex ">
<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="pointsInput" @change="pointsChange" paddingNone
@@ -38,93 +36,103 @@
</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 myTabs from '@/components/my-components/my-tabs.vue'
import infoBox from '@/commons/utils/infoBox.js'
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 myTabs from "@/components/my-components/my-tabs.vue";
import infoBox from "@/commons/utils/infoBox.js";
const props = defineProps({
title: {
type: String,
default: '积分抵扣'
default: "积分抵扣",
},
accountPoints:{
type:Object,
default:()=>{
maxCanUsePoints: {
type: Number,
default: 0
},
pointDeductionRule: {
type: Object,
default: () => {
return {
calcRes:{
usable: false,
unusableReason: '',
minDeductionPoints: 0,
maxUsablePoints: 0
}
}
}
enableRewards: false,
unusableReason: "",
minDeductionPoints: 0,
maxUsablePoints: 0,
};
},
},
price: {
type: [Number, String],
default: 0
}
})
default: 0,
},
});
function to2(n) {
if (!n) {
return ''
}
return n.toFixed(2)
}
function pointsInput(e){
setTimeout(()=>{
form.points=Math.floor(e)
},100)
}
function pointsChange(newval) {
form.points=Math.floor(newval)
if (newval < 0) {
form.points = 0
return infoBox.showToast('积分抵扣不能小于0')
}
if (newval > props.accountPoints.calcRes.maxUsablePoints) {
form.points = props.price
return infoBox.showToast('积分抵扣不能大于'+props.accountPoints.calcRes.maxUsablePoints)
return "";
}
return n.toFixed(2);
}
function pointsInput(e) {
setTimeout(() => {
form.points = Math.floor(e);
}, 100);
}
function pointsChange(newval) {
form.points = Math.floor(newval);
if (newval < 0) {
form.points = 0;
return infoBox.showToast("积分抵扣不能小于0");
}
if (newval > props.maxCanUsePoints) {
form.points = props.price;
return infoBox.showToast(
"积分抵扣不能大于" + props.maxCanUsePoints
);
}
}
const form = reactive({
points: props.price,
})
watch(() => props.price, (newval) => {
form.points = newval
})
});
watch(
() => props.price,
(newval) => {
form.points = newval;
}
);
function resetForm() {
form.points=0
form.points = 0;
}
const model = ref(null)
const model = ref(null);
function open() {
model.value.open()
form.points = props.price
model.value.open();
form.points = props.price;
}
function close() {
model.value.close()
model.value.close();
}
const emits = defineEmits(['confirm'])
const emits = defineEmits(["confirm"]);
function confirm() {
emits('confirm',Math.floor(form.points) )
close()
emits("confirm", Math.floor(form.points));
close();
}
defineExpose({
open,
close
})
close,
});
</script>
<style lang="scss" scoped>
@@ -140,7 +148,7 @@
.tag {
background-color: #fff;
border: 1px solid #E5E5E5;
border: 1px solid #e5e5e5;
line-height: inherit;
font-size: 24rpx;
color: #666666;
@@ -148,13 +156,13 @@
border-radius: 8rpx;
&.active {
border-color: #E6F0FF;
border-color: #e6f0ff;
color: $my-main-color;
}
}
.hover-class {
background-color: #E5E5E5;
background-color: #e5e5e5;
}
.discount {
@@ -166,7 +174,7 @@
}
.bg1 {
background: #F7F7FA;
background: #f7f7fa;
}
.tab {
@@ -174,7 +182,7 @@
}
.border {
border: 1px solid #E5E5E5;
border: 1px solid #e5e5e5;
border-radius: 4rpx;
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,17 +7,25 @@ export default defineConfig({
],
server: {
proxy: {
'/javaapi': {
// target: 'https://cashier.sxczgkj.com', // 目标服务器地址
'/prodJavaApi': {
target: 'https://cashier.sxczgkj.com', // 目标服务器地址
changeOrigin: true, // 是否更改请求源
rewrite: path => path.replace(/^\/prodJavaApi/, '')
},
'/testJavaApi': {
target: 'http://192.168.1.42/', // 目标服务器地址
changeOrigin: true, // 是否更改请求源
rewrite: path => path.replace(/^\/javaapi/, '')
rewrite: path => path.replace(/^\/testJavaApi/, '')
},
'/phpapi': {
// target: 'https://cashier.sxczgkj.com', // 目标服务器地址
'/prodPhpApi': {
target: 'https://cashier.sxczgkj.com', // 目标服务器地址
changeOrigin: true, // 是否更改请求源
rewrite: path => path.replace(/^\/prodPhpApi/, '')
},
'/testPhpApi': {
target: 'http://192.168.1.42:8787/', // 目标服务器地址
changeOrigin: true, // 是否更改请求源
rewrite: path => path.replace(/^\/phpapi/, '')
rewrite: path => path.replace(/^\/testPhpApi/, '')
}
}
}