13 Commits

69 changed files with 11713 additions and 2167 deletions

View File

@@ -0,0 +1,59 @@
<template>
<view>
<view class="u-flex date-filter-box " @click="show">
<view>
<text class="color-999" v-if="!startDate">开始日期</text>
<text class="" v-else>
{{ showDate(startDate)}}
</text>
</view>
<text></text>
<view>
<text class="color-999" v-if="!endDate">结束日期</text>
<text class="" v-else>
{{ showDate(endDate)}}
</text>
</view>
</view>
<my-date-pickerview mode="date" @confirm="datePickerConfirm" ref="datePicker"
style="z-index: 999"></my-date-pickerview>
</view>
</template>
<script setup>
import {
ref
} from 'vue';
import dayjs from 'dayjs';
const startDate = defineModel('start')
const endDate = defineModel('end')
const datePicker = ref(null)
function show() {
datePicker.value.open()
}
function datePickerConfirm(e) {
console.log(e);
startDate.value = e.start
endDate.value = e.end
}
function showDate(str){
if(str){
return str.split(' ')[0]
}
return ''
}
</script>
<style lang="scss">
.date-filter-box {
border: 1px solid #eee;
padding: 10rpx 32rpx;
justify-content: space-between;
border-radius: 12rpx;
}
</style>

28
activationCode/data.js Normal file
View File

@@ -0,0 +1,28 @@
export const shopTypes = {
'only': '单店',
'chain': '连锁店',
'join': '加盟店',
}
export const registerTypes = {
'before': '快餐版',
'after': '餐饮版',
}
export const profiless = {
probation: '试用',
release: '正式',
}
export const statuss = {
1: '开启',
0: '关闭'
}
export const tubeTypes = {
1: '直接管理',
0: '不可直接管理'
}
export const channels = {
'poly': '聚合支付',
'native': '支付进件',
}

View File

@@ -0,0 +1,395 @@
<template>
<view class="min-page bg-f7 u-font-28 color-333">
<up-sticky>
<view class="top u-flex gap-20">
<!-- <up-select :options="types" @select="typesSelect">
<template #text>
<text v-if="query.type!==''">{{returnTypesLabel(query.type)}}</text>
<text v-else>类型</text>
</template>
</up-select> -->
<up-select :options="statusList" @select="statusListSelect">
<template #text>
<text v-if="query.state!==''">{{returnStatusLabel(query.state)}}</text>
<text v-else>状态</text>
</template>
</up-select>
<view class="u-flex-1">
<datePickerSelect v-model:start="query.startTime" v-model:end="query.endTime">
</datePickerSelect>
</view>
</view>
</up-sticky>
<view class="box">
<view class="container" v-for="(item,index) in list" :key="index">
<view class="u-flex u-row-between">
<view>
<text class="color-666">创建时间</text>
<text class="font-bold">{{item.createTime}}</text>
</view>
<view class="status" :class="returnStatusClass(item.status)">{{item.status?'已使用':'未使用'}}</view>
</view>
<view class="u-flex u-row-between u-m-t-24">
<view>
<text class="color-666">激活时长()</text>
<text class="font-bold">{{item.periodMonth}}个月</text>
</view>
</view>
<view class="u-flex u-m-t-32" v-if="item.shopName">
<view>
<text class="color-666">使用店铺名称</text>
<text class="font-bold">{{item.shopName}}</text>
<text class="u-m-l-10">({{item.registerType=='before'?'快餐版':'餐饮版'}})</text>
</view>
</view>
<view class="u-m-t-32">
<view class="u-m-t-24 u-flex u-col-center">
<text class="color-666">激活码</text>
<view class="status success u-font-24">{{item.registerCode}}</view>
</view>
<view class="u-flex u-m-t-32 u-row-right gap-20">
<view style="min-width: 160rpx;">
<my-button @click="oncopy(item.registerCode)">复制激活码</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="toAdd()">添加激活码</my-button>
</view>
<up-popup :show="addShow" mode="center"
@close="addShow=false" round="16rpx" :close-on-click-overlay="true">
<view class="add-box">
<view class="font-bold u-font-32 text-center">生成激活码</view>
<view class="u-p-l-20 u-p-r-20 u-m-r-32">
<up-form label-width="auto">
<up-form-item label="激活时长" required>
<up-number-box input-width="200rpx" v-model="form.periodMonth" :integer="true" :step="1" ></up-number-box>
</up-form-item>
</up-form>
<up-form label-width="auto">
<up-form-item label="生产数量" required>
<up-number-box input-width="200rpx" v-model="form.num" :integer="true" :step="1" ></up-number-box>
</up-form-item>
</up-form>
</view>
<view class="u-flex gap-20 u-m-t-42">
<view class="u-flex-1">
<my-button type="default" @click="addShow=false">取消</my-button>
</view>
<view class="u-flex-1">
<my-button @click="submit">生产激活码</my-button>
</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import {
reactive,
ref,
watch
} from 'vue';
import {
onReachBottom,
onShow
} from '@dcloudio/uni-app'
import * as Api from '@/http/api/account/merchantRegister.js'
import datePickerSelect from '../components/date-picker-select.vue'
const code = ref('')
const addShow = ref(false)
const form =reactive({
periodMonth:1,
num:1,
})
watch(()=>addShow.value,(newval)=>{
if(!newval){
form.periodMonth=1;
form.num=1;
}
})
const codeType = ref('')
const datePicker = ref(null)
function datePickerConfirm(e) {
}
function submit(){
Api.addMerchantRegister(form).then(res=>{
if(res){
uni.$u.toast('添加成功')
addShow.value=false
search()
}
})
}
const statusList = [{
value: 1,
name: '已激活',
},
{
value: 0,
name: '待激活',
},
]
const types = [{
value: 'before',
name: '快餐版',
},
{
value: 'after',
name: '餐饮版',
},
]
function oncopy(code) {
uni.setClipboardData({
data: code
})
}
function statusListSelect(e) {
query.state = e.value
}
function typesSelect(e) {
query.type = 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 returnTypesLabel(state) {
const item = types.find(v => v.value == state)
if (item) {
return item.name
}
return ''
}
function returnStatusClass(state) {
if (state == 1) {
return 'success'
}
return 'gray'
}
function showCode(item, type) {
if (type == 'wx') {
code.value = item.wechatSignUrl
}
if (type == 'aliPay') {
code.value = item.alipaySignUrl
}
codeType.value = type
addShow.value = true
}
const showShopSelect = ref(false)
const query = reactive({
page: 1,
size: 10,
state: '',
type: '',
startTime:'',
endTime:'',
})
watch(() => query.state, (newval) => {
search()
})
watch(() => query.startTime, (newval) => {
search()
})
watch(() => query.endTime, (newval) => {
search()
})
watch(() => query.type, (newval) => {
search()
})
const isEnd = ref(false)
const list = ref([])
function search() {
isEnd.value = false
query.page = 1
getData()
}
function toAdd(shop) {
addShow.value = true
}
function toEdit(shop) {
console.log(shop)
uni.navigateTo({
url: '/pagesShops/add/add?id=' + shop.id
})
}
let isAjaxIng=ref(false)
function getData() {
if(isAjaxIng.value){
return
}
isAjaxIng.value=true
Api.getMerchantRegister({
...query,
startTime:'',
endTime:'',
createdAt:query.startTime&&query.endTime?( [query.startTime,query.endTime]):''
}).then(res => {
isAjaxIng.value=false
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++
getData()
}
})
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 {
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;
}
.add-box {
background-color: #fff;
width: 690rpx;
background-color: #fff;
padding: 32rpx 28rpx;
border-radius: 16rpx;
}
</style>

View File

@@ -0,0 +1,73 @@
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': '对公借记卡',
}
// WAIT 待提交
// INIT 待处理
// AUDIT 待审核
// SIGN 待签约
// FINISH 已完成
// REJECTED 失败
export 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'
},
]

View File

@@ -0,0 +1,254 @@
<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'
/**
* 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: 'my-address-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: [],
isChooseA: false, //是否已经选择了区
area: 0, //区级下标
areas: [],
tabsIndex: 0,
list:[]
}
},
async mounted() {
await this.getRegon()
this.init();
},
computed: {
isChange() {
return this.tabsIndex > 1;
},
genTabsList() {
let tabsList = [{
name: "请选择"
}];
if (this.isChooseP) {
console.log(this.province)
tabsList[0]['name'] = this.provinces[this.province]['regionName'];
tabsList[1] = {
name: "请选择"
};
}
if (this.isChooseC) {
tabsList[1]['name'] = this.citys[this.city]['regionName'];
tabsList[2] = {
name: "请选择"
};
}
if (this.isChooseA) {
tabsList[2]['name'] = this.areas[this.area]['regionName'];
}
return tabsList;
},
uZIndex() {
// 如果用户有传递z-index值优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
}
},
emits: ['city-change'],
methods: {
async getRegon(){
const res=await region()
this.provinces=res||[]
},
init() {
if (this.areaCode.length == 3) {
this.setProvince("", this.areaCode[0]);
this.setCity("", this.areaCode[1]);
this.setArea("", this.areaCode[2]);
} else if (this.defaultRegion.length == 3) {
this.setProvince(this.defaultRegion[0], "");
this.setCity(this.defaultRegion[1], "");
this.setArea(this.defaultRegion[2], "");
};
},
setProvince(regionName = "", value = "") {
this.provinces.map((v, k) => {
if (value ? v.value == value : v.regionName == regionName) {
this.provinceChange(k);
}
})
},
setCity(regionName = "", value = "") {
this.citys.map((v, k) => {
if (value ? v.value == value : v.regionName == regionName) {
this.cityChange(k);
}
})
},
setArea(regionName = "", value = "") {
this.areas.map((v, k) => {
if (value ? v.value == value : v.regionName == regionName) {
this.isChooseA = true;
this.area = k;
}
})
},
close() {
this.$emit('update:modelValue', false);
},
tabsChange(index) {
this.tabsIndex = index;
},
provinceChange(index) {
this.isChooseP = true;
this.isChooseC = false;
this.isChooseA = false;
this.province = index;
this.citys =this.provinces[index].children
this.tabsIndex = 1;
},
cityChange(index) {
this.isChooseC = true;
this.isChooseA = false;
this.city = index;
this.areas =this.provinces[this.province].children[index].children
this.tabsIndex = 2;
},
areaChange(index) {
this.isChooseA = true;
this.area = index;
let result = {};
result.province = this.provinces[this.province];
result.city = this.citys[this.city];
result.area = this.areas[this.area];
this.$emit('city-change', result);
this.close();
}
}
}
</script>
<style lang="scss">
.area-box {
width: 100%;
overflow: hidden;
height: 800rpx;
>view {
width: 150%;
transition: transform 0.3s ease-in-out 0s;
transform: translateX(0);
&.change {
transform: translateX(-33.3333333%);
}
}
.area-item {
width: 33.3333333%;
height: 800rpx;
}
}
</style>

View File

@@ -6,16 +6,17 @@
class="fixed-bottom u-flex gap-20" class="fixed-bottom u-flex gap-20"
:class="[direction == 'column' ? 'u-flex-column' : '']" :class="[direction == 'column' ? 'u-flex-column' : '']"
> >
<view class="u-flex-1">
<my-button type="primary" @click="save" shape="circle">
保存
</my-button>
</view>
<view class="u-flex-1"> <view class="u-flex-1">
<my-button bgColor="#fff" type="default" @click="cancel" shape="circle"> <my-button bgColor="#fff" type="default" @click="cancel" shape="circle">
取消 取消
</my-button> </my-button>
</view> </view>
<view class="u-flex-1">
<my-button type="primary" @click="save" shape="circle">
保存
</my-button>
</view>
</view> </view>
</view> </view>
</template> </template>

View File

@@ -0,0 +1,35 @@
<template>
<view class="min-page u-flex u-col-center u-row-center">
<view class="loader">
</view>
</view>
</template>
<script>
</script>
<style lang="scss">
/* HTML: <div class="loader"></div> */
.loader {
width: 50px;
--b: 8px;
aspect-ratio: 1;
border-radius: 50%;
padding: 1px;
background: conic-gradient(#0000 10%, #f03355) content-box;
-webkit-mask:
repeating-conic-gradient(#0000 0deg, #000 1deg 20deg, #0000 21deg 36deg),
radial-gradient(farthest-side, #0000 calc(100% - var(--b) - 1px), #000 calc(100% - var(--b)));
-webkit-mask-composite: destination-in;
mask-composite: intersect;
animation: l4 1s infinite steps(10);
}
@keyframes l4 {
to {
transform: rotate(1turn)
}
}
</style>

View File

@@ -1,8 +1,8 @@
<template> <template>
<view class="upload-file" @click="chooseImage"> <view class="upload-file" @click="chooseImage" :style="returnStyle('box')">
<slot v-if="$slots.default"></slot> <slot v-if="$slots.default"></slot>
<view class="icon" v-if="!modelValue">+</view> <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"> <view class="close" @click.stop="() => {}" v-if="modelValue">
<up-icon name="close-circle" color="#333" size="14" @click="clearImg"></up-icon> <up-icon name="close-circle" color="#333" size="14" @click="clearImg"></up-icon>
@@ -11,79 +11,152 @@
</template> </template>
<script setup> <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 props = defineProps({
size: {
const modelValue = defineModel({ default: ''
type: String, },
default: '' // 图片最大上传大小单位M默认undefined不限制大小
}); maxSize: {
type: [Number, String], // 支持数字如2或字符串如"2")传参
function chooseImage() { default: undefined,
uni.chooseImage({ validator: (value) => {
count: 1, //默认9 // 校验传参必须是大于0的数字转换后否则视为不限制
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有 const numValue = Number(value);
sourceType: ['album', 'camera'], return value === undefined || (!isNaN(numValue) && numValue > 0);
success: async function (res) {
uni.showLoading({
title: '上传中'
});
console.log(res);
const fileRes = await uploadFile(res.tempFiles[0]);
uni.hideLoading();
if (fileRes) {
modelValue.value = fileRes;
} else {
uni.showToast({
title: '上传失败',
icon: 'none'
});
} }
} }
}); })
}
function clearImg() { function returnStyle(type) {
modelValue.value = ''; 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> </script>
<style lang="scss"> <style lang="scss">
.upload-file { .upload-file {
$size: 128rpx; $size: 128rpx;
width: $size;
height: $size;
display: flex;
justify-content: center;
align-items: center;
border: 1px dashed #d9d9d9;
border-radius: 8rpx;
position: relative;
.close {
position: absolute;
right: -10rpx;
top: -10rpx;
}
.img {
width: $size; width: $size;
height: $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; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
line-height: 1; border: 1px dashed #d9d9d9;
padding: 0; border-radius: 8rpx;
margin: 0; position: relative;
text-align: center;
.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 = { const appConfig = {
// 项目名称 // 项目名称
appName: '银收客', appName: '银收客',
// token取值key // token取值key
tokenKey: 'iToken', tokenKey: 'iToken',
// tokenKey: 'satoken', // tokenKey: 'satoken',
// 环境变量相关 // 环境变量相关
env: {}, env: {},
// wss: "wss://sockets.sxczgkj.com/wss", //测试环境 // wss: "wss://sockets.sxczgkj.com/wss", //测试环境
wss: "wss://czgeatws.sxczgkj.com/wss", //正式环境 wss: "wss://czgeatws.sxczgkj.com/wss", //正式环境
// 环境变量常量 // 环境变量常量
ENV_ENUM: { ENV_ENUM: {
DEVELOPMENT: 'development', // 本地调试地址 DEVELOPMENT: 'development', // 本地调试地址
TEST: 'test', // 测试地址 TEST: 'test', // 测试地址
DEMO: 'demo', // 演示环境 DEMO: 'demo', // 演示环境
PRODUCTION: 'production' // 生产环境 PRODUCTION: 'production' // 生产环境
}, },
returnBaseUrl: returnBaseUrl,
storeEnvEnumKey: 'currentEnvEnum', // 本地存储的envkey的值 storeEnvEnumKey: 'currentEnvEnum', // 本地存储的envkey的值
encryptKey: '1234567890123456' // http数据加解密的key encryptKey: '1234567890123456', // http数据加解密的key
baseUrl: "",
} }
export default appConfig; 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,250 @@
<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 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
}
}
})
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,291 @@
<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.bank_branch_id"
@click="itemClick(item)" :id="'shop_'+item.bank_branch_id"
:class="{active:selItem&&selItem.bank_branch_id==item.bank_branch_id}">
<view class="checkbox">
<up-icon name="checkbox-mark" color="#fff"></up-icon>
</view>
<view class="u-flex-1">{{item.bank_branch_name}}</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 bankbank_branch_name = defineModel('bankbank_branch_name', {
default: '',
});
const selid = ref('')
function returnLabel() {
const findShop = list.value.find(v => v.bank_branch_id == modelValue.value)
return findShop ? findShop.bank_branch_name : ''
}
const selItem = ref(null)
function itemClick(data) {
selItem.value = data
}
function returnbank_branch_name(bank_branch_id) {
const item = list.value.find((v) => v.bank_branch_id == bank_branch_id);
return item?.bank_branch_name || '';
}
function close() {
show.value = false;
}
function submit() {
modelValue.value = selItem.value.bank_branch_id
bankbank_branch_name.value = selItem.value.bank_branch_name
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([]);
async function openPopup() {
await init()
selid.value = 'shop_' + modelValue.value
console.log('list.value',list.value);
const findShop = list.value.find(v => v.bank_branch_id == 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: () => ({
bankAliceCode: '',
cityCode: ''
})
}
})
let allList = []
async function init() {
const res = await bankBranchList(props.query);
list.value = res.data
allList = res.data
}
</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" v-if="form.userType=='1'">
<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,378 @@
<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: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" @init="getRegionData"></citySelect>
</view>
</template>
<script setup>
import {
computed,
reactive,
watch,
ref,
watchEffect
} 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'
import list from 'uview-plus/components/u-list/list';
const regions = ref([])
function getRegionData(arr = []) {
regions.value = arr
}
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;
wxProvinceCode.value = e.city.wxProvinceCode;
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 {
bankAliceCode: form.bankType,
cityCode: wxProvinceCode.value
}
})
const props = defineProps({
data: {
type: Object,
default: () => {
}
}
})
watchEffect(() => {
if (regions.value.length && form.openAccCityId) {
if (!wxProvinceCode.value) {
const findPItem= regions.value.find(v=>v.regionId==form.openAccProvinceId)
const cityIyem=findPItem?(findPItem.children.find(v=>v.regionId==form.openAccCityId)):null
if(cityIyem){
wxProvinceCode.value =cityIyem.wxProvinceCode
}
}
}
})
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,258 @@
<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','init'],
methods: {
async getRegon(){
const res=await region()
this.provinces=res||[]
this.emits('init',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>

73
entryManager/data.js Normal file
View File

@@ -0,0 +1,73 @@
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': '对公借记卡',
}
// WAIT 待提交
// INIT 待处理
// AUDIT 待审核
// SIGN 待签约
// FINISH 已完成
// REJECTED 失败
export 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'
},
]

View File

@@ -0,0 +1,322 @@
<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,statusList
} 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'
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++
getData()
}
})
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

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

View File

@@ -0,0 +1,50 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "account";
export function allShopList(data) {
return request({
url: `${urlType}/admin/shopInfo`,
method: "GET",
data: {
...data,
},
});
}
export function addShop(data) {
return request({
url: `${urlType}/admin/shopInfo`,
method: "post",
data: {
...data,
},
});
}
export function editShop(data) {
return request({
url: `${urlType}/admin/shopInfo`,
method: "put",
data: {
...data,
},
});
}
export function getShopDetail(data) {
return request({
url: `${urlType}/admin/shopInfo/detail`,
method: "get",
data: {
...data,
},
});
}
export function delShop(data) {
return request({
url: `${urlType}/admin/shopInfo`,
method: "DELETE",
data: {
...data,
},
});
}

View File

@@ -188,4 +188,13 @@ export function shopUserFlow(data) {
method: 'get', method: 'get',
data 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

@@ -0,0 +1,33 @@
import http from "@/http/http.js";
const request = http.request;
const urlType = "order";
export function shopMerchant(data) {
return request({
url: `${urlType}/admin/shopMerchant`,
method: "GET",
data: {
...data,
},
});
}
export function editShopMerchant(data) {
return request({
url: `${urlType}/admin/shopMerchant`,
method: "PUT",
data: {
...data,
},
});
}
//获取当前店铺的主店进件信息
export function getMainMerchant(data) {
return request({
url: `${urlType}/admin/shopMerchant/getMainMerchant`,
method: "GET",
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 * @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"; import { reject } from "lodash";
// 设置node环境 // 设置node环境
// envConfig.changeEnv(storageManage.env('production')) //正式 // envConfig.changeEnv(storageManage.env('production')) //正式
envConfig.changeEnv(storageManage.env("development")); //测试 // 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
let baseUrl = appConfig.returnBaseUrl({apiType:'java'});
const loadingShowTime = 200; const loadingShowTime = 200;
function getHeader() { function getHeader() {

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,10 +14,13 @@
</view> </view>
<my-tabs v-model="active" :list="tabs" textKey="label"></my-tabs> <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 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> <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" /> <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> <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>
<view class="u-flex-1 u-font-28 filter-box u-flex u-row-between" @click="showTimeArea = true"> <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"> <template v-if="userComponentQuery.startTime && userComponentQuery.endTime">
@@ -215,6 +218,7 @@ function refresh() {
} }
const listRes = ref({}); const listRes = ref({});
const userId=ref('')
async function getList() { async function getList() {
let res = null; let res = null;
if (active.value == 1) { if (active.value == 1) {
@@ -232,7 +236,8 @@ async function getList() {
res = await memberApi.orderList({ res = await memberApi.orderList({
page: pageNum.value, page: pageNum.value,
size: 10, size: 10,
key: userComponentQuery.user, // key: userComponentQuery.user,
key: userId.value,
startTime: userComponentQuery.startTime ? userComponentQuery.startTime + ' 00:00:00' : '', startTime: userComponentQuery.startTime ? userComponentQuery.startTime + ' 00:00:00' : '',
endTime: userComponentQuery.endTime ? userComponentQuery.endTime + ' 23:59:59' : '' endTime: userComponentQuery.endTime ? userComponentQuery.endTime + ' 23:59:59' : ''
}); });
@@ -264,6 +269,7 @@ watch(
userComponentQuery.startTime = ''; userComponentQuery.startTime = '';
userComponentQuery.endTime = ''; userComponentQuery.endTime = '';
keyWord.value = ''; keyWord.value = '';
userId.value=''
refresh(); refresh();
} }
); );

View File

@@ -911,6 +911,59 @@
"navigationBarTitleText": "分销" "navigationBarTitleText": "分销"
} }
}] }]
},
{
"root": "entryManager",
"pages": [
{
"path": "index/index",
"style": {
"navigationBarTitleText": "进件管理"
}
},
{
"path": "add/add",
"style": {
"navigationBarTitleText": "进件"
}
}
]
},
{
"root": "pagesShops",
"pages": [
{
"path": "index/index",
"style": {
"navigationBarTitleText": "店铺管理"
}
},
{
"path": "add/add",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pay-config/pay-config",
"style": {
"navigationBarTitleText": "支付配置"
}
}
]
},
{
"root": "activationCode",
"pages": [
{
"path": "index/index",
"style": {
"navigationBarTitleText": "激活码列表"
}
}
]
} }
// , // ,
// { // {

View File

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

View File

@@ -74,7 +74,7 @@ export default {
}, },
async init() { async init() {
const res = await menusStore.getMenus() 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 { return {
...v, ...v,
children: v.children.filter(child => child.type == 0 && !child.hidden) children: v.children.filter(child => child.type == 0 && !child.hidden)

View File

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

View File

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

View File

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

View File

@@ -436,6 +436,7 @@
websocketUtil.offMessage(); websocketUtil.offMessage();
websocketUtil.onMessage(async (res) => { websocketUtil.onMessage(async (res) => {
let msg = JSON.parse(res); let msg = JSON.parse(res);
console.log('收到消息',msg)
let cartItem; let cartItem;
let cartArr = []; let cartArr = [];
// console.log("onMessage===",msg) // console.log("onMessage===",msg)
@@ -1349,13 +1350,13 @@
if (fileRes) { if (fileRes) {
uni.setStorageSync('stickData',{ uni.setStorageSync('stickData',{
table:data.table, table:data.table,
orderInfo:data.orderInfo orderInfo:data.orderInfo,
limitTimeDiscount:data.limitTimeDiscount
}) })
go.to("PAGES_CREATE_ORDER_STICK", { go.to("PAGES_CREATE_ORDER_STICK", {
tableCode: data.table.tableCode, tableCode: data.table.tableCode,
number:fileRes, number:fileRes,
isCreateOrderToDetail:isCreateOrderToDetail.value, 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="default-box-padding bg-fff border-r-12 u-m-t-24">
<view class="u-flex u-row-between"> <view class="u-flex u-row-between">
<view>订单状态</view> <view>订单状态</view>
<view>{{$dict.getDiceName(data.status,'orderStatus')}}</view> <view>{{ $dict.getDiceName(data.status, 'orderStatus') }}</view>
</view> </view>
<view class="u-flex u-row-between u-m-t-24"> <view class="u-flex u-row-between u-m-t-24">
<view>订单类型</view> <view>订单类型</view>
<view>{{$dict.getDiceName(data.dineMode,'dineMode')}}</view> <view>{{ $dict.getDiceName(data.dineMode, 'dineMode') }}</view>
</view> </view>
<view class="u-flex u-row-between u-m-t-24"> <view class="u-flex u-row-between u-m-t-24">
<view>桌位号</view> <view>桌位号</view>
<view>{{table.name||data.tableName}}</view> <view>{{ table.name || data.tableName }}</view>
</view> </view>
<view class="u-flex u-row-between u-m-t-24" v-if="seatFee.number"> <view class="u-flex u-row-between u-m-t-24" v-if="seatFee.number">
<view>就餐人数</view> <view>就餐人数</view>
<view>{{seatFee.number||''}}</view> <view>{{ seatFee.number || '' }}</view>
</view> </view>
<view class="u-flex u-row-between u-m-t-24"> <view class="u-flex u-row-between u-m-t-24">
<view>支付方式</view> <view>支付方式</view>
<view>{{$dict.getDiceName(data.payType,'payType')||''}}</view> <view>{{ $dict.getDiceName(data.payType, 'payType') || '' }}</view>
</view> </view>
<view class="u-flex u-row-between u-m-t-24"> <view class="u-flex u-row-between u-m-t-24">
<view>下单时间</view> <view>下单时间</view>
@@ -27,9 +27,9 @@
<view class="u-flex u-row-between u-m-t-24"> <view class="u-flex u-row-between u-m-t-24">
<view>订单编号</view> <view>订单编号</view>
<view class="u-flex"> <view class="u-flex">
<view>{{data.orderNo}}</view> <view>{{ data.orderNo }}</view>
<view v-if="data.orderNo" class="u-m-l-6"> <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-icon name="/static/copy.svg" :size="16"></up-icon>
</up-copy> </up-copy>
</view> </view>
@@ -37,34 +37,44 @@
</view> </view>
<view class="u-flex u-row-between u-m-t-24 u-col-top"> <view class="u-flex u-row-between u-m-t-24 u-col-top">
<view class="no-wrap">商家备注</view> <view class="no-wrap">商家备注</view>
<view class="u-p-l-32 " style="max-width: 522rpx; word-wrap: break-word;"> <view class="u-p-l-32" style="max-width: 522rpx; word-wrap: break-word">
{{data.remark}} {{ 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> </view>
</view> </view>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
const props = defineProps({ const props = defineProps({
data: { data: {
type: Object, type: Object,
default: () => {} default: () => {}
}, },
table: { table: {
type: Object, type: Object,
default: () => {} default: () => {}
}, },
seatFee: { seatFee: {
type: Object, type: Object,
default: () => { default: () => {
totalNumber: 0 totalNumber: 0;
}
} }
}) }
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped></style>
</style>

View File

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

File diff suppressed because it is too large Load Diff

351
pagesShops/add/add.vue Normal file
View File

@@ -0,0 +1,351 @@
<template>
<view class="min-page u-font-28 color-333 bg-f7">
<view class="container">
<up-form labelPosition="top" :model="form" :rules="rules" ref="refForm" label-width="700rpx"
errorType="toast" :label-style="labelStyle">
<up-form-item prop="shopName" label="店铺名称" :required="returnIsRequired('shopName')">
<up-input v-model="form.shopName" placeholder="请输入店铺名称"></up-input>
</up-form-item>
<up-form-item prop="shopType" label="店铺类型" :required="returnIsRequired('shopType')">
<up-radio-group v-model="form.shopType" :disabled="isDisabledEdit()">
<up-radio v-for="(value,key) in shopTypes" :label="value" :name="key"></up-radio>
</up-radio-group>
</up-form-item>
<up-form-item prop="isHeadShop" label="是否为主店" :required="returnIsRequired('isHeadShop')"
v-if="form.shopType != 'only'">
<up-switch v-model="form.isHeadShop" :size="20" :disabled="isDisabledEdit()" :inactive-value="0"
:active-value="1"></up-switch>
</up-form-item>
<template v-if="form.isHeadShop == 0 && form.shopType != 'only'">
<up-form-item label="选择主店" prop="mainId" :required="returnIsRequired('mainId')">
<headShopSelect v-model="form.mainId" :disabled="isDisabledEdit('mainId')"></headShopSelect>
</up-form-item>
</template>
<up-form-item prop="chainName" label="连锁店扩展店名" :required="returnIsRequired('chainName')">
<up-input v-model="form.chainName" placeholder="连锁店扩展店名"></up-input>
</up-form-item>
<up-form-item prop="logo" label="门店logo" :required="returnIsRequired('logo')">
<my-upload-img v-model="form.logo" :size="200"></my-upload-img>
</up-form-item>
<up-form-item prop="frontImg" label="门店照片" :required="returnIsRequired('frontImg')">
<my-upload-img v-model="form.frontImg" :size="200"></my-upload-img>
</up-form-item>
<up-form-item prop="registerType" label="经营模式" :required="returnIsRequired('registerType')">
<up-radio-group v-model="form.registerType">
<up-radio v-for="(value,key) in registerTypes" :label="value" :name="key"></up-radio>
</up-radio-group>
<up-alert description="请谨慎修改" type="warning" :show-icon="true"></up-alert>
</up-form-item>
<up-form-item prop="tubeType" label="管理方式" :required="returnIsRequired('tubeType')"
v-if="form.shopType != 'only'">
<up-radio-group v-model="form.tubeType">
<up-radio v-for="(value,key) in tubeTypes" :label="value" :name="key*1"></up-radio>
</up-radio-group>
<up-alert description="请谨慎修改" type="warning" :show-icon="true"></up-alert>
</up-form-item>
<up-form-item prop="profiles" label="试用/正式" :required="returnIsRequired('profiles')">
<up-radio-group v-model="form.profiles">
<up-radio v-for="(value,key) in profiless" :label="value" :name="key"></up-radio>
</up-radio-group>
</up-form-item>
<up-form-item prop="activateCode" label="激活码" :required="returnIsRequired('activateCode')">
<view>
<up-input v-model="form.activateCode" placeholder="激活码"></up-input>
<view class="u-font-24 u-m-t-14 color-666">
输入有效激活码表示添加的同时直接激活该店铺
</view>
</view>
</up-form-item>
<up-form-item prop="accountName" label="登录账号" :required="returnIsRequired('accountName')">
<up-input v-model="form.accountName" placeholder="登录账号"></up-input>
</up-form-item>
<up-form-item prop="accountPwd" label="登录密码" :required="returnIsRequired('accountPwd')">
<up-input v-model="form.accountPwd" placeholder="登录密码"></up-input>
</up-form-item>
<up-form-item prop="phone" label="联系电话" :required="returnIsRequired('phone')">
<up-input v-model="form.phone" placeholder="联系电话"></up-input>
</up-form-item>
<up-form-item prop="supportDeviceNumber" label="设备数量"
:required="returnIsRequired('supportDeviceNumber')">
<up-number-box v-model="form.supportDeviceNumber" input-width="100"></up-number-box>
</up-form-item>
<up-form-item prop="districts" label="店铺地址" :required="returnIsRequired('districts')">
<view class="u-relative w-full">
<up-input :modelValue="address" placeholder="店铺地址"></up-input>
<view class="u-absolute position-all" style="z-index: 10;" @click="showAddressSelect=true">
</view>
</view>
</up-form-item>
<up-form-item prop="lng" label="店铺经度" :required="returnIsRequired('lng')">
<view class="u-flex u-col-center gap-20">
<up-input v-model="form.lng" placeholder="经度" disabled></up-input>
<up-input v-model="form.lat" placeholder="维度" disabled></up-input>
<my-button @click="chooseLng">选择坐标</my-button>
</view>
</up-form-item>
<up-form-item prop="address" label="店铺详细地址" :required="returnIsRequired('address')">
<up-input v-model="form.address" placeholder="店铺详细地址"></up-input>
</up-form-item>
<up-form-item prop="detail" label="店铺简介" :required="returnIsRequired('detail')">
<up-textarea v-model="form.detail" placeholder="店铺简介"></up-textarea>
</up-form-item>
<up-form-item prop="status" label="状态" :required="returnIsRequired('status')">
<up-radio-group v-model="form.status">
<up-radio v-for="(value,key) in statuss" :label="value" :name="key*1"></up-radio>
</up-radio-group>
</up-form-item>
</up-form>
</view>
<my-address-select v-model="showAddressSelect" @city-change="cityChange"></my-address-select>
<my-bottom-btn-group @cancel="uni.navigateBack()" @save="submit"></my-bottom-btn-group>
</view>
</template>
<script setup>
import {
onLoad
} from '@dcloudio/uni-app'
import {
computed,
reactive,
ref,
watch,
watchEffect
} from 'vue';
import {
statuss,
shopTypes,
profiless,
registerTypes,
tubeTypes
} from '../data.js'
import headShopSelect from './components/head-shop-select.vue'
import * as shopInfoApi from '@/http/api/account/shopInfo.js'
function chooseLng() {
uni.chooseLocation({
keyword: address.value,
success(res) {
console.log('res', res);
form.lat = '' + res.latitude
form.lng = '' + res.longitude
}
})
}
function cityChange(e) {
console.log(e);
form.provinces = e.province.regionName
form.cities = e.city.regionName
form.districts = e.area.regionName
}
const showAddressSelect = ref(false)
const labelStyle = {
fontWeight: "700"
}
const refForm = ref(null)
const form = reactive({
id: null,
shopName: "",
mainId: "",
shopType: "only",
tubeType: 0,
registerType: "before",
profiles: "release",
activateCode: "",
accountName: "",
accountPwd: "",
phone: "",
supportDeviceNumber: 1,
lat: "",
lng: "",
address: "",
detail: "",
status: 1,
logo: "",
frontImg: "",
provinces: "",
cities: "",
districts: "",
chainName: "",
isHeadShop: 0,
})
watch(() => form.isHeadShop, (newval) => {
form.mainId = ''
})
function isDisabledEdit(key) {
if(options.type==='addBranch'){
return true
}
if (form.id) {
return true
} else {
false
}
}
const address = computed(() => {
if (form.provinces && form.cities && form.districts) {
return form.provinces + '-' + form.cities + '-' + form.districts
}
})
const rules = reactive({
'shopName': {
type: 'string',
max: 30,
required: true,
message: '请输入店铺名称',
trigger: ['blur', 'change'],
},
'logo': {
type: 'string',
required: true,
message: '请上传门店logo',
trigger: ['blur', 'change'],
},
accountName: {
max: 30,
required: true,
message: '请输入登录账号',
trigger: ['blur', 'change'],
},
phone: {
max: 30,
required: true,
message: '请输入联系电话',
trigger: ['blur', 'change'],
},
districts: {
max: 30,
required: true,
message: '请选择店铺地址',
trigger: ['blur', 'change'],
},
lng: {
max: 30,
required: true,
message: '请选择店铺坐标',
trigger: ['blur', 'change'],
// 自定义验证函数,见上说明
// validator: (rule, value, callback) => {
// return !form.lng?false:true;
// },
},
tubeType: {
type: 'number',
max: 30,
required: true,
message: '请选择管理方式',
trigger: ['blur', 'change'],
},
mainId: {
max: 30,
required: true,
message: '请选择主店铺',
trigger: ['blur', 'change'],
}
})
function returnIsRequired(key) {
if (rules[key] && rules[key].required) {
return true
}
return false
}
function submit() {
refForm.value.validate().then(valid => {
console.log('valid', valid);
if (valid) {
// uni.$u.toast('校验通过')
save()
} else {}
}).catch(() => {
// 处理验证错误
});
}
async function save() {
const res = (form.id) ? await shopInfoApi.editShop(form) : await shopInfoApi.addShop(form)
if (res) {
uni.$u.toast(form.id ? '修改成功' : '添加成功')
setTimeout(() => {
uni.navigateBack()
}, 1000)
}
}
const options=reactive({})
onLoad(async (opt) => {
Object.assign(options,opt)
if(opt.type=='addBranch'){
// 添加分店
const res = await await shopInfoApi.getShopDetail({
id: opt.id
})
form.isHeadShop=0
form.mainId=res.id
form.shopType=res.shopType
uni.setNavigationBarTitle({
title: '添加店铺'
})
return
}else{
if (opt.id) {
rules.accountName.required = false
const res = await await shopInfoApi.getShopDetail({
id: opt.id
})
for (let key in form) {
form[key] = res[key]
}
uni.setNavigationBarTitle({
title: '编辑店铺'
})
} else {
uni.setNavigationBarTitle({
title: '添加店铺'
})
}
}
})
watchEffect(()=>{
console.log(form.shopType)
if(form.shopType!='only'&&!form.isHeadShop){
rules.mainId.required=true
}
})
</script>
<style lang="scss" scoped>
.min-page {
padding: 32rpx 28rpx;
}
.container {
background-color: #fff;
padding: 12rpx 28rpx;
border-radius: 16rpx;
}
:deep(.input-placeholder) {
font-size: 28rpx;
}
</style>

View File

@@ -0,0 +1,73 @@
<template>
<view class="w-full">
<view class="box u-flex u-row-between" @click="showPop" :class="{disabled:disabled}">
<text class="color-999 " v-if="!modelValue">请选择</text>
<text v-else class="u-m-r-30">{{shopName}}</text>
<up-icon name="arrow-down"></up-icon>
</view>
<popupHeadShop v-model="show" @confirm="confirm"></popupHeadShop>
</view>
</template>
<script setup>
import {
computed,
onMounted,
ref
} from 'vue';
import popupHeadShop from './popup-head-shop.vue'
import * as shopInfoApi from '@/http/api/account/shopInfo.js'
const props = defineProps({
disabled: {
type: Boolean,
default: false
}
})
const show = ref(false)
function showPop() {
if (props.disabled) {
return
}
show.value = true
}
const modelValue = defineModel()
function confirm(e) {
console.log(e);
modelValue.value = e.shopId
selShopInfo.value = e
}
const selShopInfo = ref(null)
const shopName = computed(() => {
if (selShopInfo.value) {
return selShopInfo.value.shopName
}
return ''
})
onMounted(async () => {
if (modelValue.value) {
const res = await shopInfoApi.getShopDetail({
id: modelValue.value
})
if (res) {
selShopInfo.value = res
}
}
})
</script>
<style lang="scss" scoped>
.box {
padding: 9px 9px;
border: 1px solid #dadbde;
display: flex;
border-radius: 4px;
&.disabled {
background-color: rgb(245, 247, 250);
}
}
</style>

View File

@@ -0,0 +1,209 @@
<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: '',
});
watch(() => show.value, (newval) => {
if (newval) {
init()
}
})
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: '',
type: 'only',
isHeadShop: 1
})
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)
}
}
}
</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,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>

28
pagesShops/data.js Normal file
View File

@@ -0,0 +1,28 @@
export const shopTypes = {
'only': '单店',
'chain': '连锁店',
'join': '加盟店',
}
export const registerTypes = {
'before': '快餐版',
'after': '餐饮版',
}
export const profiless = {
probation: '试用',
release: '正式',
}
export const statuss = {
1: '开启',
0: '关闭'
}
export const tubeTypes = {
1: '直接管理',
0: '不可直接管理'
}
export const channels = {
'poly': '聚合支付',
'native': '支付进件',
}

415
pagesShops/index/index.vue Normal file
View File

@@ -0,0 +1,415 @@
<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 class="u-flex u-row-between">
<view>
<text class="color-666">到期时间</text>
<text class="font-bold">{{item.expireTime}}</text>
</view>
<view class="status" :class="returnStatusClass(item.status)">{{item.status?'正式':'试用'}}</view>
</view>
<view class="u-flex u-m-t-10">
<up-avatar :src="item.logo" size="120rpx" shape="square"></up-avatar>
<view class="u-flex-1 u-p-l-32">
<view class="font-bold u-line-1">{{item.shopName}}</view>
<view class="u-m-t-10">{{item.provinces}}-{{item.cities}}-{{item.districts}} {{item.address}}
</view>
</view>
</view>
<view class="u-m-t-32">
<view class="">
<text class="color-666">经营模式</text>
<text class="font-bold">{{returnRegisterType(item.registerType) }}</text>
</view>
<view class="u-m-t-24">
<text class="color-666">店铺类型</text>
<text class="font-bold">{{returnShopType(item.shopType) }}</text>
<text class="font-bold"
v-if="item.shopType!='only'&&item.isHeadShop==0">(主店{{item.headShopName}})</text>
</view>
<view class="u-m-t-24 u-flex">
<text class="color-666">店铺状态</text>
<up-switch disabled v-model="item.status" :active-value="1" :inactive-value="0"
size="20"></up-switch>
</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;">
<my-button @click="toEdit(item)">编辑</my-button>
</view>
<view style="min-width: 160rpx;">
<my-button @click="showaAtivationCode(item)">激活</my-button>
</view>
<view style="min-width: 160rpx;">
<my-button @click="showMore(item)">
<text class="u-m-r-10">更多</text>
<up-icon name="arrow-down" color="#fff"></up-icon>
</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="toAdd()">添加店铺</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>
<up-popup :show="activationCode.show" mode="center" @close="activationCode.show=false" round="16rpx"
:close-on-click-overlay="true">
<view class="popup-box">
<view class="font-bold u-font-32 text-center">生成激活码</view>
<view class="u-p-l-20 u-p-r-20 u-m-r-32">
<up-form label-width="auto">
<up-form-item label="激活码" required>
<up-input v-model="activationCode.form.activateCode"></up-input>
</up-form-item>
</up-form>
</view>
<view class="u-flex gap-20 u-m-t-42">
<view class="u-flex-1">
<my-button type="default" @click="activationCode.show=false">取消</my-button>
</view>
<view class="u-flex-1">
<my-button @click="activationCodeSubmit">确认</my-button>
</view>
</view>
</view>
</up-popup>
<up-action-sheet :actions="moreMenus.list" round="16rpx" @select="moreMenusSelect" @close="moreMenus.show=false"
cancelText="取消" :show="moreMenus.show"></up-action-sheet>
</view>
</template>
<script setup>
import {
shopTypes,
registerTypes
} from '../data.js'
const code = ref('')
const codeShow = ref(false)
const codeType = ref('')
import {
reactive,
ref,
watch
} from 'vue';
import shopSelect from '../components/shop-select.vue'
import {
onReachBottom,
onShow
} from '@dcloudio/uni-app'
import * as shopInfoApi from '@/http/api/account/shopInfo.js'
const activationCode = reactive({
show: false,
form: {
activateCode: '',
id: '',
},
})
async function activationCodeSubmit(){
if (!activationCode.form.activateCode) {
return uni.$u.toast('请填写激活码')
}
const res = await shopInfoApi.editShop(activationCode.form)
if (res) {
uni.$u.toast('激活成功')
search()
}
}
function showaAtivationCode(item) {
activationCode.form.id = item.id
activationCode.show=true
}
const statusList = [{
value: 1,
name: '开启',
},
{
value: 0,
name: '关闭',
},
]
const moreMenusList = [{
name: '添加分店',
},
{
name: '支付配置',
},
// {
// name: '续费记录',
// },
// {
// name: '重置密码',
// },
// {
// name: '删除',
// color: '#FF1C1C'
// },
]
const moreMenus = reactive({
list: moreMenusList,
selItem: null,
show: false
})
function moreMenusSelect(e) {
console.log(e);
if (e.name == '支付配置') {
uni.navigateTo({
url: '/pagesShops/pay-config/pay-config?shopId=' + moreMenus.selItem.id + '&licenceNo=' + moreMenus
.selItem.licenceNo
})
}
if (e.name == '添加分店') {
uni.navigateTo({
url: '/pagesShops/add/add?id=' + moreMenus.selItem.id + '&type=addBranch'
})
}
}
function showMore(item) {
if (item.shopType != 'only' && item.isHeadShop) {
moreMenus.list = moreMenusList
} else {
moreMenus.list = moreMenusList.filter(v => v.name != '添加分店')
}
moreMenus.selItem = item
moreMenus.show = true
}
function returnRegisterType(registerTyp) {
return registerTypes[registerTyp] ? registerTypes[registerTyp] : ''
}
function returnShopType(shopType) {
return shopTypes[shopType] ? shopTypes[shopType] : ''
}
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) {
if (state == 1) {
return 'success'
}
return 'warning'
}
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: '/pagesShops/add/add'
})
}
function toEdit(shop) {
console.log(shop)
uni.navigateTo({
url: '/pagesShops/add/add?id=' + shop.id
})
}
function getData() {
shopInfoApi.allShopList(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++
getData()
}
})
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 {
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;
}
.popup-box {
background-color: #fff;
width: 690rpx;
background-color: #fff;
padding: 32rpx 28rpx;
border-radius: 16rpx;
}
</style>

View File

@@ -0,0 +1,615 @@
<template>
<view class="min-page bg-f7 u-font-28">
<template v-if="loadingFinish">
<up-form :model="form" :rules="rules" ref="refForm" label-width="auto" label-position="top"
errorType="toast" :label-style="labelStyle">
<view class="container">
<up-form-item label="当前模式">
<up-radio-group v-model="form.channel">
<up-radio v-for="(value,key) in channels" :key="key" :name="key" :label="value"></up-radio>
</up-radio-group>
</up-form-item>
<template v-if="form.channel=='native'">
<up-form-item label="使用信息"
v-if="shopInfo.shopType!='only'&& !shopInfo.isHeadShop && mainEntryManagerRes">
<up-radio-group v-model="useInfo">
<up-radio v-for="(value,key) in useInfos" :key="key" :name="key*1"
:label="value"></up-radio>
</up-radio-group>
</up-form-item>
</template>
</view>
<template v-if="form.channel=='poly'">
<view class="container">
<up-form-item label="店铺id" prop="polyMerchantDTO.storeId"
:required="returnIsRequired('polyMerchantDTO.storeId')">
<up-input v-model="form.polyMerchantDTO.storeId"></up-input>
</up-form-item>
<up-form-item label="商户名称" prop="polyMerchantDTO.merchantName"
:required="returnIsRequired('polyMerchantDTO.merchantName')">
<up-input v-model="form.polyMerchantDTO.merchantName"></up-input>
</up-form-item>
<up-form-item label="商户应用id" prop="polyMerchantDTO.appId"
:required="returnIsRequired('polyMerchantDTO.appId')">
<up-input v-model="form.polyMerchantDTO.appId"></up-input>
</up-form-item>
<up-form-item label="商户密钥" prop="polyMerchantDTO.appSecret"
:required="returnIsRequired('polyMerchantDTO.appSecret')">
<up-input v-model="form.polyMerchantDTO.appSecret"></up-input>
</up-form-item>
<!-- <up-form-item label="支付密码" prop="polyMerchantDTO.payPassword"
:required="returnIsRequired('polyMerchantDTO.payPassword')">
<up-input v-model="form.polyMerchantDTO.payPassword"></up-input>
</up-form-item> -->
</view>
</template>
<template v-else>
<template v-if="useInfo==0">
<template v-if="entryManagerRes">
<view class="container container1">
<view class="u-flex u-row-between">
<text class="font-bold">商户号</text>
<text class="">{{entryManagerRes.merchantCode}}</text>
</view>
<view class="u-flex u-row-between u-m-t-24">
<text class="font-bold">商户类型</text>
<text class="">{{returnType(entryManagerRes.userType)}}</text>
</view>
<view class="status">
<view class="u-flex u-row-between ">
<text class="font-bold">支付宝进件状态</text>
<view class="state" :class="returnStatusClass(entryManagerRes.alipayStatus)">
{{returnStatusLabel(entryManagerRes.alipayStatus)}}
</view>
</view>
<view v-if="entryManagerRes.alipayStatus==='SIGN'" class="u-m-t-16">
<my-button @click="showCode(entryManagerRes,'aliPay')">查看签约码</my-button>
</view>
<view class="u-m-t-14" v-if="entryManagerRes.alipayErrorMsg">
<up-alert title="拒绝原因" type="error"
:description="entryManagerRes.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(entryManagerRes.wechatStatus)">
{{returnStatusLabel(entryManagerRes.wechatStatus)}}
</view>
</view>
<view v-if="entryManagerRes.wechatStatus==='SIGN'" class="u-m-t-16">
<my-button @click="showCode(entryManagerRes,'wx')">查看签约码</my-button>
</view>
</view>
<view class="u-m-t-14" v-if="entryManagerRes.wechatErrorMsg">
<up-alert title="拒绝原因" type="error"
:description="entryManagerRes.wechatErrorMsg"></up-alert>
</view>
</view>
<view class="u-flex u-row-between u-m-t-24">
<text class="font-bold">最后提交时间</text>
<text class="">{{entryManagerRes.updateTime}}</text>
</view>
<view class="u-flex u-row-between u-m-t-24">
<text class="font-bold">创建时间</text>
<text class="">{{entryManagerRes.createTime}}</text>
</view>
<view class="u-flex u-row-right gap-20 u-m-t-32">
<my-button @click="toEdit(entryManagerRes)">查看详情</my-button>
<my-button @click="toEdit(entryManagerRes)">修改</my-button>
</view>
</view>
</template>
<view class="container container1" v-if="!entryManagerRes">
<view class="color-red font-bold text-center">您还未申请进件</view>
<view class="u-m-t-32">
<my-button @click="toEdit()">去申请</my-button>
</view>
</view>
</template>
<template v-if="useInfo==1">
<view class="container container1">
<view class="u-flex u-row-between u-m-b-24">
<text class="font-bold">复用信息</text>
<up-tag type="primary" plain text="复用主店信息"></up-tag>
</view>
<view class="u-flex u-row-between">
<text class="font-bold">商户号</text>
<text class="">{{form.shopDirectMerchant.merchantCode}}</text>
</view>
<view class="u-flex u-row-between u-m-t-24">
<text class="font-bold">商户类型</text>
<text class="">{{returnType(form.shopDirectMerchant.userType)}}</text>
</view>
<view class="status">
<view class="u-flex u-row-between ">
<text class="font-bold">支付宝进件状态</text>
<view class="state"
:class="returnStatusClass(form.shopDirectMerchant.alipayStatus)">
{{returnStatusLabel(form.shopDirectMerchant.alipayStatus)}}
</view>
</view>
<view v-if="form.shopDirectMerchant.alipayStatus==='SIGN'" class="u-m-t-16">
<my-button @click="showCode(form.shopDirectMerchant,'aliPay')">查看签约码</my-button>
</view>
<view class="u-m-t-14" v-if="form.shopDirectMerchant.alipayErrorMsg">
<up-alert title="拒绝原因" type="error"
:description="form.shopDirectMerchant.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(form.shopDirectMerchant.wechatStatus)">
{{returnStatusLabel(form.shopDirectMerchant.wechatStatus)}}
</view>
</view>
<view v-if="form.shopDirectMerchant.wechatStatus==='SIGN'" class="u-m-t-16">
<my-button @click="showCode(form.shopDirectMerchant,'wx')">查看签约码</my-button>
</view>
</view>
<view class="u-m-t-14" v-if="form.shopDirectMerchant.wechatErrorMsg">
<up-alert title="拒绝原因" type="error"
:description="form.shopDirectMerchant.wechatErrorMsg"></up-alert>
</view>
</view>
<view class="u-flex u-row-between u-m-t-24">
<text class="font-bold">最后提交时间</text>
<text class="">{{form.shopDirectMerchant.updateTime}}</text>
</view>
<view class="u-flex u-row-between u-m-t-24">
<text class="font-bold">创建时间</text>
<text class="">{{form.shopDirectMerchant.createTime}}</text>
</view>
</view>
</template>
</template>
<!-- <up-form-item label="微信appid" prop="polyMerchantDTO.wechatSmallAppid"
:required="returnIsRequired('polyMerchantDTO.wechatSmallAppid')">
<up-input v-model="form.polyMerchantDTO.wechatSmallAppid"></up-input>
</up-form-item>
<up-form-item label="微信支付宝appid" prop="polyMerchantDTO.alipaySmallAppid"
:required="returnIsRequired('polyMerchantDTO.alipaySmallAppid')">
<up-input v-model="form.polyMerchantDTO.alipaySmallAppid"></up-input>
</up-form-item> -->
</up-form>
<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>
<my-bottom-btn-group @cancel="uni.navigateBack()" @save="submit"></my-bottom-btn-group>
</template>
<my-page-loading v-else></my-page-loading>
</view>
</template>
<script setup>
import {
reactive,
ref,
watch
} from 'vue'
import {
companyChildTypes,
userTypes,
statusList
} from '@/commons/data/entryManager.js'
import * as shopMerchantApi from '@/http/api/order/shopMerchant.js'
import {
getEntryManager
} from '@/http/api/order/entryManager.js'
import {
channels
} from '../data.js'
import {
onLoad
} from '@dcloudio/uni-app'
import * as shopInfoApi from '@/http/api/account/shopInfo.js'
const useInfo = ref(0)
const useInfos = {
0: '当前店铺',
1: '主店'
}
function toEdit(shop) {
console.log(shop)
if (shop) {
uni.navigateTo({
url: '/entryManager/add/add?shopId=' + shop.shopId + '&licenceNo=' + shop.businessLicenceInfo
.licenceNo
})
} else {
return uni.navigateTo({
url: '/entryManager/add/add?shopId=' + query.shopId
})
// if (mainEntryManagerRes.value) {
// uni.showModal({
// title: '提示',
// content: '是否复用主店信息',
// showCancel: true,
// cancelText: '复用主店信息',
// confirmText: '继续进件',
// success(res) {
// if (res.confirm) {
// uni.navigateTo({
// url: '/entryManager/add/add?shopId=' + query.shopId
// })
// } else {
// form.shopDirectMerchant = mainEntryManagerRes.value
// }
// }
// })
// return
// } else {
// uni.navigateTo({
// url: '/entryManager/add/add?shopId=' + query.shopId
// })
// }
}
}
const code = ref('')
const codeShow = ref(false)
const codeType = ref('')
function returnStatusLabel(state) {
const item = statusList.find(v => v.value == state)
if (item) {
return item.name
}
return ''
}
function returnIsRequired(key) {
if (rules[key] && rules[key].required) {
return true
}
return false
}
function returnType(type) {
if (userTypes[type]) {
return userTypes[type]
}
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
}
function returnStatusClass(state) {
const item = statusList.find(v => v.value == state)
if (item) {
return item.class
}
return ''
}
const labelStyle = {
fontWeight: "700"
}
const query = reactive({
shopId: ''
})
const rules = reactive({
"polyMerchantDTO.merchantName": {
type: 'string',
required: true,
message: '请输入商户名称',
trigger: ['blur', 'change'],
},
"polyMerchantDTO.storeId": {
type: 'string',
required: true,
message: '请输入店铺id',
trigger: ['blur', 'change'],
},
"polyMerchantDTO.appId": {
type: 'string',
required: true,
message: '请输入商户应用id',
trigger: ['blur', 'change'],
},
"polyMerchantDTO.appSecret": {
type: 'string',
required: true,
message: '请输入商户密钥',
trigger: ['blur', 'change'],
},
// "polyMerchantDTO.payPassword": {
// type: 'string',
// required: true,
// message: '请输入支付密钥',
// trigger: ['blur', 'change'],
// },
// "polyMerchantDTO.wechatSmallAppid": {
// type: 'string',
// required: true,
// message: '请输入微信小程序appid',
// trigger: ['blur', 'change'],
// },
// "polyMerchantDTO.alipaySmallAppid": {
// type: 'string',
// required: true,
// message: '请输入支付宝小程序appid',
// trigger: ['blur', 'change'],
// }
})
const form = reactive({
shopId: 0,
channel: "native",
relatedLicenceNo: "",
nativeMerchantDTO: {
wechatMerchantId: "",
alipayMerchantId: "",
alipayAuthInfo: {
user_id: "",
open_id: "",
auth_app_id: "",
app_auth_token: "",
expires_in: "",
app_refresh_token: "",
re_expires_in: "",
order_no: "",
},
},
polyMerchantDTO: {
storeId: "",
merchantName: "",
appId: "",
appSecret: "",
payPassword: "",
wechatSmallAppid: "",
alipaySmallAppid: "",
},
shopDirectMerchant: {
shopId: 0,
shopName: "",
licenceNo: "",
alipayAccount: "",
merchantCode: "",
userType: "",
shortName: "",
merchantBaseInfo: "",
legalPersonInfo: "",
businessLicenceInfo: "",
storeInfo: "",
settlementInfo: "",
createTime: "",
updateTime: "",
errorMsg: "",
wechatApplyId: "",
wechatStatus: "",
wechatErrorMsg: "",
wechatSignUrl: "",
alipayOrderId: "",
alipayStatus: "",
alipayErrorMsg: "",
alipaySignUrl: "",
alipayAuthInfo: "",
wechatMerchantId: "",
alipayMerchantId: "",
},
});
const refForm = ref(null)
const entryManagerRes = ref(null)
const mainEntryManagerRes = ref(null)
const shopInfo = ref(null)
const shopMerchant=ref(null)
async function init() {
const shopInfoRes = await shopInfoApi.getShopDetail({
id: query.shopId
})
shopInfo.value = shopInfoRes
//不是单店且不是主店
if (shopInfoRes && shopInfo.shopType != 'only' && !shopInfoRes.isHeadShop) {
const res = await shopMerchantApi.getMainMerchant({
shopId: query.shopId
})
mainEntryManagerRes.value = res
}
const res = await getEntryManager({
shopId: query.shopId
})
entryManagerRes.value = res
shopMerchantApi.shopMerchant({
shopId: query.shopId
}).then(res => {
if(res){
if (res.shopDirectMerchant) {
useInfo.value = 1
} else {
useInfo.value = 0
}
shopMerchant.value=res
Object.assign(form, res)
}
loadingFinish.value=true
})
}
const loadingFinish=ref(false)
onLoad((opt) => {
query.shopId = opt.shopId
form.shopId = opt.shopId
init()
})
async function save() {
let res = null
if (form.channel == 'poly') {
res = await shopMerchantApi.editShopMerchant({
shopId: form.shopId,
channel: form.channel,
polyMerchantDTO: {
storeId: form.polyMerchantDTO.storeId,
merchantName: form.polyMerchantDTO.merchantName,
appId: form.polyMerchantDTO.appId,
appSecret: form.polyMerchantDTO.appSecret,
}
})
} else {
res = await shopMerchantApi.editShopMerchant({
shopId: form.shopId,
channel: form.channel,
relatedLicenceNo: form.relatedLicenceNo,
nativeMerchantDTO: {
wechatMerchantId: form.nativeMerchantDTO.wechatMerchantId,
alipayMerchantId: form.nativeMerchantDTO.alipayMerchantId,
alipayAuthInfo: {
user_id: form.nativeMerchantDTO.alipayAuthInfo.user_id,
open_id: form.nativeMerchantDTO.alipayAuthInfo.open_id,
auth_app_id: form.nativeMerchantDTO.alipayAuthInfo.auth_app_id,
app_auth_token: form.nativeMerchantDTO.alipayAuthInfo.app_auth_token,
expires_in: form.nativeMerchantDTO.alipayAuthInfo.expires_in,
app_refresh_token: form.nativeMerchantDTO.alipayAuthInfo.app_refresh_token,
re_expires_in: form.nativeMerchantDTO.alipayAuthInfo.re_expires_in,
order_no: form.nativeMerchantDTO.alipayAuthInfo.order_no,
}
}
})
}
if (res) {
uni.$u.toast('保存成功')
setTimeout(() => {
uni.navigateBack()
}, 1000)
}
}
function submit() {
if (form.channel == 'native' && !entryManagerRes.value) {
return uni.$u.toast('您还未申请进件,清先申请进件')
}
refForm.value.validate().then(valid => {
console.log('valid', valid);
if (valid) {
// uni.$u.toast('校验通过')
save()
} else {}
}).catch(() => {
// 处理验证错误
});
}
watch(() => form.channel, (newval) => {
for (let key in rules) {
console.log('key', key)
rules[key].required = newval == 'poly' ? true : false;
}
})
watch(() => useInfo.value, (newval) => {
console.log('newval',newval);
if (newval) {
form.shopDirectMerchant = mainEntryManagerRes.value||(shopMerchant.value?(shopMerchant.value.shopDirectMerchant):null )
form.nativeMerchantDTO = null
} else {
form.shopDirectMerchant = null
form.nativeMerchantDTO = entryManagerRes.value
}
})
</script>
<style lang="scss" scoped>
.min-page {
padding: 32rpx 28rpx;
}
.container {
background-color: #fff;
padding: 12rpx 28rpx;
border-radius: 16rpx;
margin-bottom: 32rpx;
&.container1 {
padding: 32rpx 28rpx;
}
}
:deep(.input-placeholder) {
font-size: 28rpx;
}
:deep(.u-form-item__body) {
padding: 10px 0 0 0;
}
.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;
}
}
}
</style>

View File

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