fix oss public/private logic, fixed merchant-info submit logic

This commit is contained in:
Daniel Kou 2026-05-22 16:47:41 +08:00
parent d3ac0573bf
commit 5fc340a47a
16 changed files with 1665 additions and 346 deletions

107
components/PrivateImage.vue Normal file
View File

@ -0,0 +1,107 @@
<template>
<view class="private-image">
<image v-if="signedUrl" :src="signedUrl" :mode="mode" :style="{ width: width, height: height }"
@click="onClick" @error="onError" />
<view v-else-if="loading" class="ph">加载中</view>
<view v-else-if="error" class="ph err">{{ error }}</view>
<view v-else-if="!path" class="ph">未上传</view>
</view>
</template>
<script>
// PrivateImage
//
//
// <PrivateImage :path="form.id_card_front" width="200rpx" height="120rpx" />
//
// targetUserId object key
import {
signPrivateView
} from '@/utils/uploadOSS.js'
export default {
name: 'PrivateImage',
props: {
path: {
type: String,
default: ''
},
width: {
type: String,
default: '200rpx'
},
height: {
type: String,
default: '120rpx'
},
mode: {
type: String,
default: 'aspectFill'
},
// mount click
trigger: {
type: String,
default: 'mount'
}
},
data() {
return {
signedUrl: '',
loading: false,
error: ''
}
},
watch: {
path: {
immediate: true,
handler(v) {
if (v && this.trigger === 'mount') this.fetch()
else this.signedUrl = ''
}
}
},
methods: {
async fetch() {
if (!this.path) return
this.loading = true
this.error = ''
try {
const res = await signPrivateView(this.path)
this.signedUrl = res.url
} catch (e) {
this.error = e.message || '加载失败'
} finally {
this.loading = false
}
},
onClick() {
if (this.signedUrl) {
uni.previewImage({
urls: [this.signedUrl]
})
} else if (this.trigger === 'click') {
this.fetch()
}
},
onError() {
this.error = '图片加载失败'
this.signedUrl = ''
}
}
}
</script>
<style scoped>
.private-image .ph {
display: inline-block;
text-align: center;
color: #999;
font-size: 22rpx;
padding: 16rpx;
border: 2rpx dashed #ccc;
}
.private-image .ph.err {
color: #f56c6c;
}
</style>

View File

@ -167,18 +167,21 @@ export default {
// pathscene
const pathParts = res.path.split('?');
if (pathParts.length > 1) {
const queryParams = pathParts[1].split('&');
for (const param of queryParams) {
const [key, value] = param.split('=');
if (key === 'scene') {
const params = {
id: value
}
uni.setStorageSync('merchantInfo', params);
NavgateTo('/packages/localLife/detail/index')
break;
}
}
if (pathParts[1].startsWith('scene=')){
const pathPart = decodeURIComponent(pathParts[1].substring(6))
const queryParams = pathPart.split('&');
for (const param of queryParams) {
const [key, value] = param.split('=');
if (key === 'id') {
const params = {
id: value
}
uni.setStorageSync('merchantInfo', params);
NavgateTo('/packages/localLife/detail/index')
break;
}
}
}
}
}
},

View File

@ -68,9 +68,9 @@ export default {
},
getAvatarUrl(record){
if(record.client_id_one == uni.getStorageSync('openId')){
return record.two.avatar ? picUrl + record.two.avatar : 'https://static.hshuishang.com/defaultTx.png'
return record.two && record.two.avatar ? picUrl + record.two.avatar : 'https://static.hshuishang.com/defaultTx.png'
}else{
return record.one.avatar ? picUrl + record.one.avatar : 'https://static.hshuishang.com/defaultTx.png'
return record.one && record.one.avatar ? picUrl + record.one.avatar : 'https://static.hshuishang.com/defaultTx.png'
}
},
//

View File

@ -160,6 +160,7 @@ export default {
const options = {
clientId: this.selfClientId
}
console.log('clientId:', options.clientId)
//
const callbacks = {

View File

@ -0,0 +1,16 @@
<template>
<view class="container">
<view>提交订单</view>
</view>
</template>
<script>
export default {
data() {
return {};
},
};
</script>
<style scoped>
</style>

View File

@ -100,7 +100,18 @@ export default {
}
},
onLoad(options) {
this.itemObj = JSON.parse(options.itemObj);
if (options && options.itemObj) {
try {
const raw = decodeURIComponent(options.itemObj)
this.itemObj = JSON.parse(raw)
} catch (e) {
console.error('解析审核状态参数失败:', e)
this.itemObj = {}
}
} else if (options && options.status) {
// status
this.itemObj = { status: Number(options.status) }
}
},
methods: {
goShopManage() {

View File

@ -0,0 +1,95 @@
page {
background-color: #f6f7fb;
min-height: 100vh;
}
.container {
padding: 30rpx;
}
.header {
padding: 40rpx 0 60rpx;
text-align: center;
}
.header-title {
font-size: 40rpx;
font-weight: bold;
color: #222222;
margin-bottom: 16rpx;
}
.header-desc {
font-size: 28rpx;
color: #999999;
}
.type-list {
margin-bottom: 40rpx;
}
.type-card {
display: flex;
align-items: center;
background: #FFFFFF;
border-radius: 20rpx;
padding: 40rpx 30rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}
.type-card:active {
opacity: 0.8;
}
.type-icon {
width: 100rpx;
height: 100rpx;
margin-right: 24rpx;
flex-shrink: 0;
}
.type-icon image {
width: 100%;
height: 100%;
}
.type-info {
flex: 1;
}
.type-name {
font-size: 32rpx;
font-weight: bold;
color: #222222;
margin-bottom: 10rpx;
}
.type-desc {
font-size: 24rpx;
color: #999999;
}
.type-arrow {
flex-shrink: 0;
}
.tips {
background: #FFFFFF;
border-radius: 20rpx;
padding: 30rpx;
}
.tips-title {
font-size: 28rpx;
font-weight: bold;
color: #222222;
margin-bottom: 20rpx;
}
.tips-item {
font-size: 24rpx;
color: #666666;
line-height: 1.8;
margin-bottom: 10rpx;
}

View File

@ -0,0 +1,63 @@
<template>
<view class="container">
<view class="header">
<view class="header-title">选择入驻类型</view>
<view class="header-desc">请选择您的商家入驻身份</view>
</view>
<view class="type-list">
<!-- 个人入驻 -->
<view class="type-card" @click="chooseType('personal')">
<view class="type-icon">
<image src="https://static.hshuishang.com/property-img-file/icon_personal.png" mode="aspectFit"></image>
</view>
<view class="type-info">
<view class="type-name">个人入驻</view>
<view class="type-desc">适用于个体工商户自然人经营者</view>
</view>
<view class="type-arrow">
<u-icon name="arrow-right" color="#999" size="28"></u-icon>
</view>
</view>
<!-- 企业入驻 -->
<view class="type-card" @click="chooseType('enterprise')">
<view class="type-icon">
<image src="https://static.hshuishang.com/property-img-file/icon_enterprise.png" mode="aspectFit"></image>
</view>
<view class="type-info">
<view class="type-name">企业入驻</view>
<view class="type-desc">适用于企业公司法人</view>
</view>
<view class="type-arrow">
<u-icon name="arrow-right" color="#999" size="28"></u-icon>
</view>
</view>
</view>
<view class="tips">
<view class="tips-title">入驻须知</view>
<view class="tips-item">1. 个人入驻需提供身份证银行卡等个人资质信息</view>
<view class="tips-item">2. 企业入驻需提供营业执照法人身份证对公账户等企业资质信息</view>
<view class="tips-item">3. 提交后将由平台工作人员进行审核预计1-3个工作日</view>
</view>
</view>
</template>
<script>
import { NavgateTo } from '../../../utils';
export default {
data() {
return {}
},
methods: {
chooseType(type) {
NavgateTo('/packages/shopEnter/index/index?enterType=' + type);
}
}
}
</script>
<style>
@import url("./index.css");
</style>

View File

@ -0,0 +1,66 @@
page {
background-color: #f6f7fb;
min-height: 100vh;
}
.container {
padding: 30rpx;
}
.example-section {
background: #FFFFFF;
border-radius: 20rpx;
padding: 30rpx;
}
.title {
font-size: 34rpx;
font-weight: bold;
color: #222222;
margin-bottom: 30rpx;
text-align: center;
}
.sub-section {
margin-bottom: 40rpx;
}
.sub-title {
font-size: 28rpx;
color: #333333;
margin-bottom: 20rpx;
font-weight: bold;
}
.example-img {
width: 100%;
margin-bottom: 20rpx;
display: flex;
justify-content: center;
}
.example-img image {
width: 500rpx;
border-radius: 10rpx;
border: 2rpx dashed #ddd;
}
.tips {
background: #FFF8F6;
border-radius: 10rpx;
padding: 24rpx;
margin-top: 20rpx;
}
.tips-title {
font-size: 26rpx;
font-weight: bold;
color: #FF370B;
margin-bottom: 16rpx;
}
.tips-item {
font-size: 24rpx;
color: #666666;
line-height: 1.8;
}

View File

@ -0,0 +1,69 @@
<template>
<view class="container">
<view class="example-section" v-if="type === 'license'">
<view class="title">营业执照拍摄示例</view>
<view class="example-img">
<image :src="picUrl + '/static/example/license.jpg'" mode="widthFix"></image>
</view>
<view class="tips">
<view class="tips-title">拍摄要求</view>
<view class="tips-item">1. 请确保营业执照在有效期内</view>
<view class="tips-item">2. 照片需清晰完整四角完整可见</view>
<view class="tips-item">3. 不得有遮挡涂改反光</view>
<view class="tips-item">4. 支持原件拍照或彩色扫描件</view>
</view>
</view>
<view class="example-section" v-if="type === 'idcard'">
<view class="title">身份证拍摄示例</view>
<view class="sub-section">
<view class="sub-title">身份证正面人像面</view>
<view class="example-img">
<image :src="picUrl + '/static/example/front.jpg'" mode="widthFix"></image>
</view>
</view>
<view class="sub-section">
<view class="sub-title">身份证反面国徽面</view>
<view class="example-img">
<image :src="picUrl + '/static/example/back.jpg'" mode="widthFix"></image>
</view>
</view>
<view class="tips">
<view class="tips-title">拍摄要求</view>
<view class="tips-item">1. 请确保身份证在有效期内</view>
<view class="tips-item">2. 照片需清晰完整四角完整可见</view>
<view class="tips-item">3. 不得有遮挡涂改反光</view>
<view class="tips-item">4. 仅支持二代身份证</view>
</view>
</view>
</view>
</template>
<script>
import { picUrl } from '../../../utils/index';
export default {
data() {
return {
picUrl,
type: 'license'
}
},
onLoad(options) {
if (options.type) {
this.type = options.type;
}
//
const titleMap = {
license: '营业执照示例',
idcard: '身份证示例'
}
uni.setNavigationBarTitle({
title: titleMap[this.type] || '资质示例'
})
}
}
</script>
<style>
@import url("./index.css");
</style>

View File

@ -1,116 +1,420 @@
/* ============================================================
* 商家入驻 - 基本信息 tab 全新样式按设计稿 PDF 1
* 主色 #FF370B 辅色 #FFF1ED 灰边 #EEE 灰字 #999 深字 #222
* ============================================================ */
page {
background-color: #f6f7fb;
min-height: 100vh;
padding-bottom: 60rpx;
background-color: #F6F7F9;
min-height: 100vh;
padding-bottom: 200rpx;
}
.container {
background: url(https://static.hshuishang.com/property-img-file/shopEn_apply.png) no-repeat;
background-size: 750rpx 497rpx;
box-sizing: border-box;
padding-top: 290rpx;
padding-bottom: 40rpx;
box-sizing: border-box;
}
.Msg {
width: 710rpx;
background: #FFFFFF;
border-radius: 20rpx 20rpx 20rpx 20rpx;
margin: 0 auto;
padding: 0 20rpx;
box-sizing: border-box;
/* ================= 顶部进度条(红底胶囊) ================= */
.step-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 30rpx 28rpx;
background: linear-gradient(91deg, #FF7658 0%, #FF370B 100%);
}
.row {
padding-top: 30rpx;
display: flex;
justify-content: space-between;
.step-pill {
flex: 1;
height: 60rpx;
margin: 0 8rpx;
border-radius: 30rpx;
background: rgba(255, 255, 255, 0.18);
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.65);
font-size: 26rpx;
}
.row_label {
font-size: 28rpx;
color: #999999;
width: 180rpx;
.step-pill.active {
background: #FFFFFF;
color: #FF370B;
font-weight: 600;
}
.red {
color: #FF370B;
margin-left: 5rpx;
/* ================= Tab 切换 ================= */
.tab-bar {
display: flex;
background: #FFFFFF;
padding: 0 60rpx;
}
.row_con {
flex: 1;
padding-bottom: 30rpx;
border-bottom: 1rpx solid #EBEBEB;
display: flex;
.tab-item {
flex: 1;
text-align: center;
padding: 28rpx 0 22rpx;
font-size: 30rpx;
color: #222;
position: relative;
}
.row_con input {
flex: 1;
.tab-item.active {
color: #FF370B;
font-weight: 600;
}
.nonebor {
border-bottom: none;
.tab-item.active::after {
content: '';
position: absolute;
bottom: 8rpx;
left: 50%;
transform: translateX(-50%);
width: 56rpx;
height: 6rpx;
background: #FF370B;
border-radius: 3rpx;
}
.row2 {
display: flex;
align-items: center;
/* ================= 表单卡片 ================= */
.form-card {
background: #FFFFFF;
margin: 20rpx 24rpx 0;
padding: 24rpx;
border-radius: 16rpx;
}
.mt {
margin-top: 26rpx;
padding-top: 36rpx;
padding-bottom: 36rpx;
.field {
margin-top: 20rpx;
}
.imgCon {
font-size: 18rpx;
color: #222222;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 120rpx;
height: 120rpx;
background: #F6F7FB;
border: 1rpx solid #D1D1D1;
border-radius: 10rpx 10rpx 10rpx 10rpx;
.field:first-child {
margin-top: 0;
}
.imgCon image {
width: 34rpx;
height: 34rpx;
margin-bottom: 8rpx;
.field-label {
font-size: 26rpx;
color: #222;
margin-bottom: 14rpx;
display: flex;
align-items: center;
}
.mt2 {
margin-top: 30rpx;
.field-label .star {
color: #FF370B;
margin-right: 4rpx;
font-size: 26rpx;
}
.u-upload__wrap__preview {
width: 120rpx;
height: 120rpx;
border-radius: 10rpx 0rpx 10rpx 10rpx !important;
.field-input {
height: 80rpx;
border: 1rpx solid #EBEBEB;
border-radius: 8rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
background: #FFFFFF;
}
.u-upload__wrap__preview__image {
width: 100% !important;
height: 100% !important;
object-fit: cover;
.field-input input {
flex: 1;
font-size: 28rpx;
color: #222;
height: 80rpx;
line-height: 80rpx;
}
.addBtn {
font-size: 36rpx;
color: #FFFFFF;
width: 600rpx;
height: 90rpx;
background: linear-gradient(91deg, #FF7658 0%, #FF370B 100%);
border-radius: 100rpx 100rpx 100rpx 100rpx;
margin: 0 auto;
margin-top: 58rpx;
display: flex;
align-items: center;
justify-content: center;
}
.field-input input::placeholder,
.field-input .placeholder {
color: #B7B7B7;
}
.field-input.disabled {
background: #FAFAFA;
}
.field-value {
flex: 1;
font-size: 28rpx;
color: #222;
}
.field-value.placeholder {
color: #B7B7B7;
}
.field-arrow {
color: #C8C8C8;
font-size: 24rpx;
margin-left: 12rpx;
}
/* ================= 上传图片块 ================= */
.upload-section {
margin-top: 20rpx;
}
.upload-title {
font-size: 26rpx;
color: #222;
margin-bottom: 16rpx;
display: flex;
align-items: center;
}
.upload-title .star {
color: #FF370B;
margin-right: 4rpx;
}
.upload-title .count {
color: #999;
font-size: 24rpx;
margin-left: 8rpx;
font-weight: normal;
}
.upload-grid {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
/* uview u-upload 内部样式微调(让占位框跟设计稿一致) */
.upload-section /deep/ .u-upload__wrap__preview {
width: 168rpx !important;
height: 168rpx !important;
border-radius: 12rpx !important;
overflow: hidden;
}
.upload-section /deep/ .u-upload__button {
width: 168rpx !important;
height: 168rpx !important;
border-radius: 12rpx !important;
background: #FAFAFA !important;
border: 1rpx dashed #D1D1D1 !important;
}
.upload-add {
width: 168rpx;
height: 168rpx;
background: #FAFAFA;
border: 1rpx dashed #D1D1D1;
border-radius: 12rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #999;
font-size: 22rpx;
}
.upload-add-icon {
font-size: 48rpx;
color: #C8C8C8;
line-height: 1;
margin-bottom: 6rpx;
}
/* ================= 底部红色提示框(门店信息要求) ================= */
.tip-box {
margin: 20rpx 24rpx 0;
background: #FFF7F4;
border: 1rpx solid #FFD9CD;
border-radius: 12rpx;
padding: 20rpx 24rpx 24rpx;
display: flex;
gap: 12rpx;
}
.tip-box .tip-icon {
color: #FF370B;
font-size: 28rpx;
font-weight: bold;
flex-shrink: 0;
line-height: 40rpx;
}
.tip-box .tip-content {
flex: 1;
}
.tip-box .tip-title {
font-size: 26rpx;
color: #FF370B;
font-weight: 600;
margin-bottom: 8rpx;
}
.tip-box .tip-item {
font-size: 24rpx;
color: #FF370B;
line-height: 40rpx;
}
/* ================= 底部按钮(吸底) ================= */
.bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #FFFFFF;
padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
display: flex;
gap: 16rpx;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.04);
z-index: 99;
}
.btn-primary {
flex: 1;
height: 88rpx;
background: linear-gradient(91deg, #FF7658 0%, #FF370B 100%);
border-radius: 44rpx;
color: #FFFFFF;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.btn-secondary {
flex: 1;
height: 88rpx;
background: #FFFFFF;
border: 1rpx solid #FF370B;
border-radius: 44rpx;
color: #FF370B;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
/* ================= 协议勾选 ================= */
.agreement {
display: flex;
align-items: center;
padding: 24rpx 24rpx 0;
}
.agreement-text {
font-size: 24rpx;
color: #666;
margin-left: 8rpx;
}
.agreement-link {
color: #FF370B;
}
/* ================= 资质示例链接 ================= */
.example-link {
font-size: 24rpx;
color: #FF370B;
margin-top: 10rpx;
}
/* ================= 弹层选择器(所在地区/类目/营业状态) ================= */
.popup-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
align-items: flex-end;
}
.popup-sheet {
width: 100%;
background: #FFFFFF;
border-radius: 24rpx 24rpx 0 0;
max-height: 80vh;
display: flex;
flex-direction: column;
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 32rpx;
border-bottom: 1rpx solid #F0F0F0;
}
.popup-header .pop-cancel {
color: #999;
font-size: 28rpx;
}
.popup-header .pop-title {
color: #222;
font-size: 30rpx;
font-weight: 600;
}
.popup-header .pop-confirm {
color: #FF370B;
font-size: 28rpx;
}
.popup-body {
flex: 1;
display: flex;
overflow: hidden;
}
.popup-col {
flex: 1;
overflow-y: auto;
padding: 12rpx 0;
}
.popup-col-item {
padding: 24rpx 32rpx;
font-size: 28rpx;
color: #222;
display: flex;
align-items: center;
justify-content: space-between;
}
.popup-col-item.selected {
color: #FF370B;
}
.popup-col-item .check {
color: #FF370B;
font-size: 24rpx;
}
/* 单列选择列表(营业状态) */
.popup-list {
flex: 1;
overflow-y: auto;
}
.popup-list-item {
padding: 30rpx 32rpx;
font-size: 30rpx;
color: #222;
text-align: center;
border-bottom: 1rpx solid #F5F5F5;
}
.popup-list-item.selected {
color: #FF370B;
}
/* ================= 类目选择搜索框 ================= */
.popup-search {
margin: 16rpx 32rpx;
background: #F5F5F5;
border-radius: 8rpx;
padding: 14rpx 20rpx;
display: flex;
align-items: center;
gap: 12rpx;
font-size: 26rpx;
color: #B7B7B7;
}

View File

@ -1,119 +1,342 @@
<template>
<view class="container">
<view class="Msg">
<view class="row">
<view class="row_label">门店名称<text class="red">*</text></view>
<view class="row_con">
<input type="text" v-model="store_name" placeholder="需与门牌照名称一致">
<!-- 顶部进度条红底胶囊三段 -->
<view class="step-bar">
<view class="step-pill" :class="{ active: currentTab >= 1 }">填写信息</view>
<view class="step-pill" :class="{ active: currentTab >= 4 }">提交审核</view>
<view class="step-pill" :class="{ active: currentTab >= 5 }">入驻成功</view>
</view>
<!-- Tab 切换 -->
<view class="tab-bar">
<view class="tab-item" :class="{ active: currentTab === 1 }" @click="switchTab(1)">基本信息</view>
<view class="tab-item" :class="{ active: currentTab === 2 }" @click="switchTab(2)">资质信息</view>
<view class="tab-item" :class="{ active: currentTab === 3 }" @click="switchTab(3)">结算信息</view>
</view>
<!-- ============ Tab 1: 基本信息 ============ -->
<view v-show="currentTab === 1">
<!-- 文本字段 -->
<view class="form-card">
<view class="field">
<view class="field-label"><text class="star">*</text>门店名称</view>
<view class="field-input">
<input type="text" v-model="store_name" placeholder="需与门牌照名称一致" />
</view>
</view>
<view class="field">
<view class="field-label"><text class="star">*</text>所在地区</view>
<view class="field-input" @click="openRegion">
<view class="field-value" :class="{ placeholder: !regionLabel }">
{{ regionLabel || '请选择省/市/区' }}
</view>
<u-icon name="arrow-right" color="#C8C8C8" size="24"></u-icon>
</view>
</view>
<view class="field">
<view class="field-label"><text class="star">*</text>详细地址</view>
<view class="field-input">
<input type="text" v-model="address" placeholder="请输入详细地址" />
</view>
</view>
<view class="field">
<view class="field-label"><text class="star">*</text>商家类目</view>
<view class="field-input" @click="openClassify">
<view class="field-value" :class="{ placeholder: !confirmClassify.cate_name }">
{{ confirmClassify.cate_name || '选择经营品类' }}
</view>
<u-icon name="arrow-right" color="#C8C8C8" size="24"></u-icon>
</view>
</view>
<view class="field">
<view class="field-label"><text class="star">*</text>营业状态</view>
<view class="field-input" @click="showBizStatus = true">
<view class="field-value" :class="{ placeholder: !bizStatusLabel }">
{{ bizStatusLabel || '选择营业状态' }}
</view>
<u-icon name="arrow-right" color="#C8C8C8" size="24"></u-icon>
</view>
</view>
<view class="field">
<view class="field-label"><text class="star">*</text>联系人</view>
<view class="field-input">
<input type="text" v-model="contact_name" placeholder="请输入联系人姓名" />
</view>
</view>
<view class="field">
<view class="field-label"><text class="star">*</text>手机号</view>
<view class="field-input">
<input type="number" v-model="contact_phone" placeholder="请输入联系方式" maxlength="11" />
</view>
</view>
</view>
<view class="row">
<view class="row_label">所在省<text class="red">*</text></view>
<view class="row_con" @click="chooseCity">
<input type="text" disabled v-model="confirmProv.ad_name" placeholder="请选择所在省">
<u-icon name="arrow-right" color="#999999" size="28"></u-icon>
<!-- 店内环境 -->
<view class="form-card">
<view class="upload-section">
<view class="upload-title">
<text class="star">*</text>店内环境<text class="count">({{ imgList3.length }}/3)</text>
</view>
<u-upload :fileList="imgList3" @afterRead="afterReadImg2" @delete="deletePic2" name="2" :maxCount="3" multiple>
<view class="upload-add">
<view class="upload-add-icon">+</view>
<text>{{ imgList3.length }}/3</text>
</view>
</u-upload>
</view>
</view>
<view class="row">
<view class="row_label">所在市<text class="red">*</text></view>
<view class="row_con" @click="chooseCity2">
<input type="text" disabled v-model="confirmCity.short_name" placeholder="请选择所在市">
<u-icon name="arrow-right" color="#999999" size="28"></u-icon>
<!-- 门头环境 -->
<view class="form-card">
<view class="upload-section">
<view class="upload-title">
<text class="star">*</text>门头环境<text class="count">({{ imgList.length }}/3)</text>
</view>
<u-upload :fileList="imgList" @afterRead="afterReadImg" @delete="deletePic" name="1" :maxCount="3" multiple>
<view class="upload-add">
<view class="upload-add-icon">+</view>
<text>{{ imgList.length }}/3</text>
</view>
</u-upload>
</view>
</view>
<view class="row">
<view class="row_label">所在区<text class="red">*</text></view>
<view class="row_con" @click="chooseCity3">
<input type="text" disabled v-model="confirmBusiness.short_name" placeholder="请选择所在区">
<u-icon name="arrow-right" color="#999999" size="28"></u-icon>
</view>
</view>
<view class="row">
<view class="row_label">详细地址<text class="red">*</text></view>
<view class="row_con">
<input type="text" v-model="address" placeholder="请输入详细地址">
</view>
</view>
<view class="row">
<view class="row_label">联系人<text class="red">*</text></view>
<view class="row_con">
<input type="text" v-model="contact_name" placeholder="请输入联系人">
</view>
</view>
<view class="row">
<view class="row_label">手机号<text class="red">*</text></view>
<view class="row_con">
<input type="text" v-model="contact_phone" placeholder="请输入联系方式">
</view>
</view>
<view class="row">
<view class="row_label">商家分类<text class="red">*</text></view>
<view class="row_con" @click="chooseClassify">
<input type="text" disabled v-model="confirmClassify.cate_name" placeholder="请选择商家分类">
<u-icon name="arrow-right" color="#999999" size="28"></u-icon>
</view>
</view>
<view class="row">
<view class="row_label">银行卡号</view>
<view class="row_con nonebor">
<input type="text" v-model="bank_card" placeholder="请输入银行卡号">
<!-- 红色提示框 -->
<view class="tip-box">
<view class="tip-icon">!</view>
<view class="tip-content">
<view class="tip-title">门店信息要求</view>
<view class="tip-item">· 门店名称需与门店招牌上的名称一致</view>
<view class="tip-item">· 门店品类需与门店主要经营范围的分类一致</view>
<view class="tip-item">· 门店地址需与门店实际经营地址一致</view>
</view>
</view>
</view>
<view class="Msg mt">
<view class="row2">
<view class="row_label">门脸照</view>
<view class="row_con2">
<u-upload :fileList="imgList" @afterRead="afterReadImg" @delete="deletePic" name="1" multiple
:maxCount="10">
<view class="imgCon">
<image src="https://static.hshuishang.com/property-img-file/com_imageImg.png"
mode="widthFix"></image>
上传图片
</view>
</u-upload>
<!-- Tab 2: 资质信息 -->
<view v-show="currentTab === 2">
<view class="Msg">
<view class="row" v-if="enterType === 'enterprise'">
<view class="row_label">统一社会信用代码<text class="red">*</text></view>
<view class="row_con">
<input type="text" v-model="credit_code" placeholder="请输入统一社会信用代码">
</view>
</view>
<view class="row" v-if="enterType === 'enterprise'">
<view class="row_label">法人姓名<text class="red">*</text></view>
<view class="row_con">
<input type="text" v-model="legal_person" placeholder="请输入法人姓名">
</view>
</view>
<view class="row" v-if="enterType === 'enterprise'">
<view class="row_label">法人身份证号<text class="red">*</text></view>
<view class="row_con nonebor">
<input type="text" v-model="legal_id_card" placeholder="请输入法人身份证号">
</view>
</view>
</view>
<view class="row2 mt2">
<view class="row_label">店内环境</view>
<view class="row_con2">
<u-upload :fileList="imgList3" @afterRead="afterReadImg2" @delete="deletePic2" name="1" multiple
:maxCount="10">
<view class="imgCon">
<image src="https://static.hshuishang.com/property-img-file/com_imageImg.png"
mode="widthFix"></image>
上传图片
</view>
</u-upload>
<view class="Msg mt">
<view class="row2">
<view class="row_label">营业执照<text class="red">*</text></view>
<view class="row_con2">
<u-upload :fileList="imgList5" @afterRead="afterReadImg3" @delete="deletePic3" name="1" :maxCount="1">
<view class="imgCon">
<image src="https://static.hshuishang.com/property-img-file/com_imageImg.png" mode="widthFix"></image>
上传图片
</view>
</u-upload>
<view class="example-link" @click="showExample('license')">查看示例</view>
</view>
</view>
<view class="row2 mt2">
<view class="row_label">{{ enterType === 'enterprise' ? '法人身份证正面' : '身份证正面' }}<text class="red">*</text></view>
<view class="row_con2">
<u-upload :fileList="idCardFrontList" @afterRead="afterReadIdFront" @delete="deleteIdFront" name="1" :maxCount="1">
<view class="imgCon">
<image src="https://static.hshuishang.com/property-img-file/com_imageImg.png" mode="widthFix"></image>
上传图片
</view>
</u-upload>
<view class="example-link" @click="showExample('idcard')">查看示例</view>
</view>
</view>
<view class="row2 mt2">
<view class="row_label">{{ enterType === 'enterprise' ? '法人身份证反面' : '身份证反面' }}<text class="red">*</text></view>
<view class="row_con2">
<u-upload :fileList="idCardBackList" @afterRead="afterReadIdBack" @delete="deleteIdBack" name="1" :maxCount="1">
<view class="imgCon">
<image src="https://static.hshuishang.com/property-img-file/com_imageImg.png" mode="widthFix"></image>
上传图片
</view>
</u-upload>
</view>
</view>
</view>
<view class="btn-group">
<view class="prevBtn" @click="currentTab = 1">上一步</view>
<view class="nextBtn" @click="nextTab(3)">下一步</view>
</view>
</view>
<view class="row2 mt2">
<view class="row_label">营业执照<text class="red">*</text></view>
<view class="row_con2">
<u-upload :fileList="imgList5" @afterRead="afterReadImg3" @delete="deletePic3" name="1" multiple
:maxCount="1">
<view class="imgCon">
<image src="https://static.hshuishang.com/property-img-file/com_imageImg.png"
mode="widthFix"></image>
上传图片
<!-- Tab 3: 结算信息 -->
<view v-show="currentTab === 3">
<view class="Msg">
<template v-if="enterType === 'personal'">
<view class="row">
<view class="row_label">开户人姓名</view>
<view class="row_con">
<input type="text" v-model="account_name" placeholder="请输入开户人姓名">
</view>
</u-upload>
</view>
<view class="row">
<view class="row_label">银行卡号</view>
<view class="row_con">
<input type="text" v-model="bank_card" placeholder="请输入银行卡号">
</view>
</view>
<view class="row">
<view class="row_label">开户银行</view>
<view class="row_con">
<input type="text" v-model="bank_name" placeholder="请输入开户银行">
</view>
</view>
<view class="row">
<view class="row_label">开户支行</view>
<view class="row_con nonebor">
<input type="text" v-model="bank_branch" placeholder="请输入开户支行">
</view>
</view>
</template>
<template v-else>
<view class="row">
<view class="row_label">企业开户名称</view>
<view class="row_con">
<input type="text" v-model="account_name" placeholder="请输入企业开户名称">
</view>
</view>
<view class="row">
<view class="row_label">对公银行账号</view>
<view class="row_con">
<input type="text" v-model="bank_card" placeholder="请输入对公银行账号">
</view>
</view>
<view class="row">
<view class="row_label">开户银行</view>
<view class="row_con">
<input type="text" v-model="bank_name" placeholder="请输入开户银行">
</view>
</view>
<view class="row">
<view class="row_label">开户支行</view>
<view class="row_con nonebor">
<input type="text" v-model="bank_branch" placeholder="请输入开户支行">
</view>
</view>
</template>
</view>
<!-- 协议勾选 -->
<view class="agreement">
<view class="agreement-check" @click="agreeProtocol = !agreeProtocol">
<u-icon :name="agreeProtocol ? 'checkmark-circle-fill' : 'checkmark-circle'" :color="agreeProtocol ? '#FF370B' : '#ccc'" size="36"></u-icon>
</view>
<view class="agreement-text">
我已阅读并同意<text class="agreement-link" @click.stop="viewProtocol">商家入驻协议</text>
</view>
</view>
<view class="btn-group">
<view class="prevBtn" @click="currentTab = 2">上一步</view>
<view class="nextBtn" @click="submit">提交入驻</view>
</view>
</view>
<!-- 所在地区弹层多列联动 -->
<view class="popup-mask" v-if="showRegion" @click.self="showRegion = false">
<view class="popup-sheet">
<view class="popup-header">
<text class="pop-cancel" @click="showRegion = false">取消</text>
<text class="pop-title">所在地区</text>
<text class="pop-confirm" @click="confirmRegion">确定</text>
</view>
<view class="popup-body" style="height: 50vh;">
<scroll-view class="popup-col" scroll-y>
<view v-for="item in pro" :key="item.ad_code"
class="popup-col-item" :class="{ selected: tempProv.ad_code === item.ad_code }"
@click="tapProv(item)">
<text>{{ item.ad_name }}</text>
<text class="check" v-if="tempProv.ad_code === item.ad_code"></text>
</view>
</scroll-view>
<scroll-view class="popup-col" scroll-y>
<view v-for="item in city" :key="item.ad_code"
class="popup-col-item" :class="{ selected: tempCity.ad_code === item.ad_code }"
@click="tapCity(item)">
<text>{{ item.short_name }}</text>
<text class="check" v-if="tempCity.ad_code === item.ad_code"></text>
</view>
</scroll-view>
<scroll-view class="popup-col" scroll-y>
<view v-for="item in buss" :key="item.ad_code"
class="popup-col-item" :class="{ selected: tempBuss.ad_code === item.ad_code }"
@click="tapBuss(item)">
<text>{{ item.short_name }}</text>
<text class="check" v-if="tempBuss.ad_code === item.ad_code"></text>
</view>
</scroll-view>
</view>
</view>
</view>
<view class="addBtn" @click="submit">立即入驻</view>
<!-- 类目选择弹层多选示意 + 搜索框 -->
<view class="popup-mask" v-if="showClassifyPop" @click.self="showClassifyPop = false">
<view class="popup-sheet">
<view class="popup-header">
<text class="pop-cancel" @click="showClassifyPop = false">取消</text>
<text class="pop-title">选择类目</text>
<text class="pop-confirm" @click="confirmClassifyPop">确定</text>
</view>
<view class="popup-search">
<u-icon name="search" color="#B7B7B7" size="28"></u-icon>
<text>搜索类目</text>
</view>
<scroll-view class="popup-list" scroll-y style="max-height: 50vh;">
<view v-for="item in classify" :key="item.id"
class="popup-list-item" :class="{ selected: tempClassify.id === item.id }"
@click="tempClassify = item">
{{ item.cate_name }}
</view>
</scroll-view>
</view>
</view>
<!-- 营业状态弹层 -->
<view class="popup-mask" v-if="showBizStatus" @click.self="showBizStatus = false">
<view class="popup-sheet">
<view class="popup-header">
<text class="pop-cancel" @click="showBizStatus = false">取消</text>
<text class="pop-title">营业状态</text>
<text class="pop-confirm" @click="showBizStatus = false">确定</text>
</view>
<view class="popup-list">
<view v-for="opt in bizStatusList" :key="opt.value"
class="popup-list-item" :class="{ selected: bizStatus === opt.value }"
@click="selectBizStatus(opt)">
{{ opt.label }}
</view>
</view>
</view>
</view>
<!-- 底部固定按钮 Tab1 显示其余 tab 沿用各自原按钮暂未改造 -->
<view class="bottom-bar" v-if="currentTab === 1">
<view class="btn-primary" @click="nextTab(2)">下一步</view>
</view>
<u-picker :show="show" :columns="[pro]" keyName="ad_name" @confirm="clickPro" @cancel="cancelPro"></u-picker>
<u-picker :show="show2" :columns="[city]" keyName="short_name" @confirm="clickCity"
@cancel="cancelCity"></u-picker>
<u-picker :show="show3" :columns="[buss]" keyName="short_name" @confirm="clickBuss"
@cancel="cancelBuss"></u-picker>
<u-picker :show="show4" :columns="[classify]" keyName="cate_name" @confirm="clickClassify"
@cancel="cancelClassify"></u-picker>
</view>
</template>
@ -130,10 +353,13 @@ import {
upload,
NavgateTo
} from '../../../utils';
import { signPrivateView } from '../../../utils/uploadOSS.js';
export default {
data() {
return {
picUrl,
currentTab: 1,
enterType: 'personal',
area: "",
confirmCity: "",
confirmArea: "",
@ -146,12 +372,26 @@ export default {
imgList5: [],
imgList6: [],
//
idCardFrontList: [],
idCardFrontPath: [],
idCardBackList: [],
idCardBackPath: [],
contact_name: "",
contact_phone: "",
bank_card: "",
bank_name: "",
bank_branch: "",
account_name: "",
store_name: "",
address: "",
//
credit_code: "",
legal_person: "",
legal_id_card: "",
confirmProv: "",
confirmCity: "",
confirmBusiness: "",
@ -166,9 +406,208 @@ export default {
show4: false,
itemObj: {},
agreeProtocol: false,
//
showRegion: false,
showClassifyPop: false,
showBizStatus: false,
tempProv: {},
tempCity: {},
tempBuss: {},
tempClassify: {},
bizStatus: 0, // 1 2 3
bizStatusList: [
{ value: 1, label: '营业中' },
{ value: 2, label: '即将开业' },
{ value: 3, label: '休息中' },
],
}
},
computed: {
regionLabel() {
const a = this.confirmProv && this.confirmProv.ad_name
const b = this.confirmCity && this.confirmCity.short_name
const c = this.confirmBusiness && this.confirmBusiness.short_name
if (!a) return ''
return [a, b, c].filter(Boolean).join('/')
},
bizStatusLabel() {
const opt = this.bizStatusList.find(o => o.value === this.bizStatus)
return opt ? opt.label : ''
},
},
methods: {
// ===== =====
async openRegion() {
if (!this.pro || !this.pro.length) await this.getPro()
this.tempProv = this.confirmProv && this.confirmProv.ad_code ? this.confirmProv : {}
this.tempCity = this.confirmCity && this.confirmCity.ad_code ? this.confirmCity : {}
this.tempBuss = this.confirmBusiness && this.confirmBusiness.ad_code ? this.confirmBusiness : {}
if (this.tempProv.ad_code) await this.getCity(this.tempProv.ad_code)
if (this.tempCity.ad_code) await this.getBuss(this.tempCity.ad_code)
this.showRegion = true
},
async tapProv(item) {
this.tempProv = item
this.tempCity = {}
this.tempBuss = {}
this.city = []
this.buss = []
await this.getCity(item.ad_code)
},
async tapCity(item) {
this.tempCity = item
this.tempBuss = {}
this.buss = []
await this.getBuss(item.ad_code)
},
tapBuss(item) {
this.tempBuss = item
},
confirmRegion() {
if (!this.tempProv.ad_code) {
uni.showToast({ title: '请选择省', icon: 'none' }); return
}
if (!this.tempCity.ad_code) {
uni.showToast({ title: '请选择市', icon: 'none' }); return
}
if (!this.tempBuss.ad_code) {
uni.showToast({ title: '请选择区', icon: 'none' }); return
}
this.confirmProv = this.tempProv
this.confirmCity = this.tempCity
this.confirmBusiness = this.tempBuss
this.showRegion = false
},
async openClassify() {
if (!this.classify || !this.classify.length) await this.getClassify()
this.tempClassify = this.confirmClassify && this.confirmClassify.id ? this.confirmClassify : {}
this.showClassifyPop = true
},
confirmClassifyPop() {
if (!this.tempClassify.id) {
uni.showToast({ title: '请选择类目', icon: 'none' }); return
}
this.confirmClassify = this.tempClassify
this.showClassifyPop = false
},
selectBizStatus(opt) {
this.bizStatus = opt.value
this.showBizStatus = false
},
switchTab(tab) {
// Tab Tab
if (tab > this.currentTab) {
if (this.currentTab === 1 && !this.validateTab1()) return;
if (tab === 3 && !this.validateTab2()) return;
}
this.currentTab = tab;
uni.pageScrollTo({ scrollTop: 0, duration: 200 });
},
nextTab(tab) {
if (tab === 2) {
if (!this.validateTab1()) return;
}
if (tab === 3) {
if (!this.validateTab2()) return;
}
this.currentTab = tab;
uni.pageScrollTo({ scrollTop: 0, duration: 200 });
},
validateTab1() {
//
if (!this.store_name) { uni.showToast({ title: '请输入门店名称', icon: 'none' }); return false; }
if (!this.confirmProv.ad_code) { uni.showToast({ title: '请选择所在省', icon: 'none' }); return false; }
if (!this.confirmCity.ad_code) { uni.showToast({ title: '请选择所在市', icon: 'none' }); return false; }
if (!this.confirmBusiness.ad_code) { uni.showToast({ title: '请选择所在区', icon: 'none' }); return false; }
if (!this.address) { uni.showToast({ title: '请输入详细地址', icon: 'none' }); return false; }
if (!this.contact_name) { uni.showToast({ title: '请输入联系人姓名', icon: 'none' }); return false; }
if (!this.contact_phone) { uni.showToast({ title: '请输入联系人手机号', icon: 'none' }); return false; }
if (!isPhone(this.contact_phone)) { uni.showToast({ title: '联系人手机号格式不正确', icon: 'none' }); return false; }
if (!this.confirmClassify.id) { uni.showToast({ title: '请选择商家分类', icon: 'none' }); return false; }
return true;
},
validateTab2() {
//
if (this.enterType === 'enterprise') {
if (!this.credit_code) { uni.showToast({ title: '请输入统一社会信用代码', icon: 'none' }); return false; }
if (!/^[0-9A-HJ-NPQRTUWXY]{18}$/.test(this.credit_code.toUpperCase())) {
uni.showToast({ title: '统一社会信用代码格式不正确18位', icon: 'none' });
return false;
}
if (!this.legal_person) { uni.showToast({ title: '请输入法人姓名', icon: 'none' }); return false; }
if (!this.legal_id_card) { uni.showToast({ title: '请输入法人身份证号', icon: 'none' }); return false; }
if (!/(^\d{15}$)|(^\d{17}([0-9X])$)/.test(this.legal_id_card.toUpperCase())) {
uni.showToast({ title: '法人身份证号格式不正确', icon: 'none' });
return false;
}
}
if (!this.imgList6.length) { uni.showToast({ title: '请上传营业执照', icon: 'none' }); return false; }
if (!this.idCardFrontPath.length) { uni.showToast({ title: '请上传身份证正面照片', icon: 'none' }); return false; }
if (!this.idCardBackPath.length) { uni.showToast({ title: '请上传身份证反面照片', icon: 'none' }); return false; }
return true;
},
validateTab3() {
// /
if (!this.account_name) {
uni.showToast({
title: this.enterType === 'enterprise' ? '请输入企业开户名称' : '请输入开户人姓名',
icon: 'none'
});
return false;
}
if (!this.bank_card) {
uni.showToast({
title: this.enterType === 'enterprise' ? '请输入对公银行账号' : '请输入银行卡号',
icon: 'none'
});
return false;
}
if (!/^\d{15,20}$/.test(this.bank_card)) {
uni.showToast({ title: '银行账号格式不正确15-20位数字', icon: 'none' });
return false;
}
if (!this.bank_name) { uni.showToast({ title: '请输入开户银行', icon: 'none' }); return false; }
if (!this.bank_branch) { uni.showToast({ title: '请输入开户支行', icon: 'none' }); return false; }
return true;
},
showExample(type) {
//
NavgateTo('/packages/shopEnter/example/index?type=' + type);
},
// bucket
afterReadIdFront(e) {
const files = Array.isArray(e.file) ? e.file : [e.file]
files.forEach(item => {
upload(item.url, res => {
// UI path
this.idCardFrontList.push({ url: item.url })
this.idCardFrontPath.push(res.data.path)
}, 'merchant_private')
})
},
deleteIdFront(e) {
this.idCardFrontList.splice(e.index, 1);
this.idCardFrontPath.splice(e.index, 1);
},
// bucket
afterReadIdBack(e) {
const files = Array.isArray(e.file) ? e.file : [e.file]
files.forEach(item => {
upload(item.url, res => {
this.idCardBackList.push({ url: item.url })
this.idCardBackPath.push(res.data.path)
}, 'merchant_private')
})
},
deleteIdBack(e) {
this.idCardBackList.splice(e.index, 1);
this.idCardBackPath.splice(e.index, 1);
},
// ad_code
parseAdCode(adCode) {
if (!adCode) return;
@ -207,7 +646,6 @@ export default {
this.show3 = false;
},
clickBuss(e) {
console.log(e);
this.show3 = false;
this.confirmBusiness = e.value[0]
},
@ -270,11 +708,13 @@ export default {
})
},
afterReadImg3(e) {
e.file.forEach(item => {
// bucket
const files = Array.isArray(e.file) ? e.file : [e.file]
files.forEach(item => {
upload(item.url, res => {
this.imgList5.push({ url: this.picUrl + res.data.path })
this.imgList5.push({ url: item.url })
this.imgList6.push(res.data.path)
})
}, 'merchant_private')
})
},
deletePic2(e) {
@ -285,101 +725,102 @@ export default {
this.imgList5.splice(e.index, 1);
this.imgList6.splice(e.index, 1);
},
viewProtocol() {
//
uni.showModal({
title: '商家入驻协议',
content: '本协议是您与平台之间关于商家入驻服务的法律协议。入驻后您需遵守平台规则,按时提供商品或服务,保证信息真实有效。平台有权对违规商家进行处罚。',
showCancel: false,
confirmText: '我知道了'
});
},
// bucket URL fileList
async signPrivatePaths(paths, listKey) {
if (!paths || !paths.length) {
this[listKey] = []
return
}
const result = []
for (const p of paths) {
try {
const r = await signPrivateView(p)
result.push({ url: r.url })
} catch (e) {
console.error('签发私密 URL 失败:', p, e)
result.push({ url: '' })
}
}
this[listKey] = result
},
submit() {
let that = this
if (!that.store_name) {
return uni.showToast({
title: '请输入门店名称',
duration: 2000
});
if (!that.validateTab3()) return;
if (!that.agreeProtocol) {
uni.showToast({ title: '请先阅读并同意商家入驻协议', icon: 'none' });
return;
}
if (!that.confirmProv.ad_code) {
return uni.showToast({
title: '请选择所在省',
duration: 2000
});
}
if (!that.confirmCity.ad_code) {
return uni.showToast({
title: '请选择所在市',
duration: 2000
});
}
if (!that.confirmBusiness.ad_code) {
return uni.showToast({
title: '请选择所在区',
duration: 2000
});
}
if (!that.address) {
return uni.showToast({
title: '请输入详细地址',
duration: 2000
});
}
if (!that.contact_name) {
return uni.showToast({
title: '请输入联系人姓名',
duration: 2000
});
}
if (!that.contact_phone) {
return uni.showToast({
title: '请输入联系人手机号',
duration: 2000
});
}
if (!that.confirmClassify.id) {
return uni.showToast({
title: '请选择商家分类',
duration: 2000
});
}
if (!that.imgList6.length) {
return uni.showToast({
title: '请上传营业执照',
duration: 2000
});
}
let interior_photo = that.imgList4.join(",")
let facade_photo = that.imgList2.join(",")
let license_photo = that.imgList6.join(",")
request(apiArr.createStore, "POST", {
let id_card_front = that.idCardFrontPath.join(",")
let id_card_back = that.idCardBackPath.join(",")
let params = {
enter_type: that.enterType === 'enterprise' ? 2 : 1,
contact_name: that.contact_name,
phone: that.contact_phone,
bank_card: that.bank_card,
bank_name: that.bank_name,
bank_branch: that.bank_branch,
account_name: that.account_name,
merchant_name: that.store_name,
address: that.address,
ad_code: that.confirmBusiness.ad_code,
facade_photo,
interior_photo,
license_photo,
id_card_front,
id_card_back,
merchant_cate_id: that.confirmClassify.id,
}).then(res => {
that.contact_name = ''
that.contact_phone = ''
that.bank_card = ''
that.store_name = ''
that.address = ''
that.confirmProv = ''
that.confirmCity = ''
that.confirmBusiness = ''
that.imgList = []
that.imgList2 = []
that.imgList3 = []
that.imgList4 = []
that.imgList5 = []
that.imgList6 = []
that.confirmClassify = ''
NavgateTo("../sucess/index")
}).catch(res => {
if (res.message.includes("agent_nil")) {
uni.showToast({
title: '未找到对应的代理商信息',
icon: 'none',
});
}
if (that.enterType === 'enterprise') {
params.credit_code = that.credit_code;
params.legal_person = that.legal_person;
params.legal_id_card = that.legal_id_card;
params.account_type = 2;
} else {
params.account_type = 1;
}
request(apiArr.createStore, "POST", params).then(res => {
// + itemObj auditStatus
const newItem = {
...params,
id: res && res.id,
status: 1,
create_time: new Date().toLocaleString('sv-SE').replace('T', ' '),
handle_time: '',
merchant_code: '',
remark: ''
}
NavgateTo("../auditStatus/index?itemObj=" + encodeURIComponent(JSON.stringify(newItem)))
}).catch(res => {
const msg = res && res.message ? res.message : '提交失败,请稍后重试'
if (msg.includes("agent_nil")) {
uni.showModal({
title: '提交失败',
content: '未找到对应的代理商信息,请联系平台',
showCancel: false
})
return
}
//
uni.showModal({
title: '提交失败',
content: msg,
showCancel: false
})
})
},
@ -414,9 +855,14 @@ export default {
},
onLoad(options) {
//
if (options.enterType) {
this.enterType = options.enterType;
}
//
Promise.all([this.getPro(), this.getClassify()]).then(() => {
//
//
if (options.itemObj) {
this.itemObj = JSON.parse(options.itemObj)
this.store_name = this.itemObj.merchant_name
@ -425,6 +871,18 @@ export default {
this.contact_phone = this.itemObj.phone
this.confirmClassify = this.classify.find(item => item.id == this.itemObj.merchant_cate_id)
this.bank_card = this.itemObj.bank_card
this.bank_name = this.itemObj.bank_name || ''
this.bank_branch = this.itemObj.bank_branch || ''
this.account_name = this.itemObj.account_name || ''
this.credit_code = this.itemObj.credit_code || ''
this.legal_person = this.itemObj.legal_person || ''
this.legal_id_card = this.itemObj.legal_id_card || ''
//
if (this.itemObj.enter_type === 2) {
this.enterType = 'enterprise';
}
// ad_code
if (this.itemObj.ad_code) {
this.parseAdCode(this.itemObj.ad_code);
@ -433,8 +891,14 @@ export default {
this.imgList2 = this.itemObj.facade_photo ? this.itemObj.facade_photo.split(",") : []
this.imgList3 = this.itemObj.interior_photo ? this.itemObj.interior_photo.split(",").map(item => ({ url: this.picUrl + item })) : []
this.imgList4 = this.itemObj.interior_photo ? this.itemObj.interior_photo.split(",") : []
this.imgList5 = this.itemObj.license_photo?.split(",").map(item => ({ url: this.picUrl + item }))
this.imgList6 = this.itemObj.license_photo?.split(",")
// bucket URL
this.imgList6 = this.itemObj.license_photo?.split(",").filter(Boolean) || []
this.idCardFrontPath = this.itemObj.id_card_front ? this.itemObj.id_card_front.split(",").filter(Boolean) : []
this.idCardBackPath = this.itemObj.id_card_back ? this.itemObj.id_card_back.split(",").filter(Boolean) : []
this.signPrivatePaths(this.imgList6, 'imgList5')
this.signPrivatePaths(this.idCardFrontPath, 'idCardFrontList')
this.signPrivatePaths(this.idCardBackPath, 'idCardBackList')
}
})
}
@ -443,4 +907,4 @@ export default {
<style>
@import url("./index.css");
</style>
</style>

View File

@ -572,6 +572,13 @@
{
"root": "packages/shopEnter",
"pages": [
{
"path": "choose/index",
"style": {
"navigationBarTitleText": "商家入驻",
"navigationBarBackgroundColor": "#fff"
}
},
{
"path": "index/index",
"style": {
@ -593,6 +600,13 @@
"navigationBarTitleText": "审核状态",
"navigationBarBackgroundColor": "#fff"
}
},
{
"path": "example/index",
"style": {
"navigationBarTitleText": "资质示例",
"navigationBarBackgroundColor": "#fff"
}
}
]
},

View File

@ -203,7 +203,7 @@
买单返积分
</view>
</view>
<view class="merchantItem_right_con_right" @click="toJump(item)">
<view class="merchantItem_right_con_right" @click.stop="toJump(item)">
<image src="https://static.hshuishang.com/property-img-file/local_review.png" mode="aspectFill">
</image>
点评
@ -665,14 +665,14 @@ export default {
async headershopEnterClick() {
if (!uni.getStorageSync('userId')) {
NavgateTo('/packages/shopEnter/index/index', { isLogin: false });
NavgateTo('/packages/shopEnter/choose/index', { isLogin: false });
return
}
const res = await request(apiArr2.statusQuery, "POST", {}, { silent: false });
if (res.status) {
NavgateTo('/packages/shopEnter/auditStatus/index?itemObj=' + JSON.stringify(res));
} else {
NavgateTo('/packages/shopEnter/index/index');
NavgateTo('/packages/shopEnter/choose/index');
}
},

View File

@ -3,8 +3,8 @@ const environments = {
development: {
apiUrl: "https://test.hshuishang.com",
picUrl: "https://test.hshuishang.com",
aliyunOssUrl: "https://wechat-img-file.oss-cn-beijing.aliyuncs.com",
staticUrl: "https://static.hshuishang.com",
aliyunOssUrl: "https://wechat-img-file-dev.oss-cn-beijing.aliyuncs.com",
staticUrl: "https://static-dev.hshuishang.com",
},
production: {
apiUrl: "https://api.hshuishang.com",
@ -25,7 +25,7 @@ const getCurrentEnvironment = () => {
if (envVersion === "release") {
return "production"; // 正式版
} else if (envVersion === "trial") {
return "production"; // 体验版
return "development"; // 体验版
} else if (envVersion === "develop") {
return "development"; // 开发版
}
@ -54,14 +54,14 @@ const getCurrentEnvironment = () => {
};
// 获取当前环境配置
const currentEnv = "production";//getCurrentEnvironment();
const currentEnv = getCurrentEnvironment();
const envConfig = environments[currentEnv] || environments.production;
export const RequsetUrl = envConfig.apiUrl; // 请求地址前缀
// export const picUrl = envConfig.picUrl; // 图片地址前缀
export const picUrl = currentEnv==='production'?envConfig.aliyunOssUrl:envConfig.picUrl; // 图片地址前缀
export const aliyunOssUrl = envConfig.aliyunOssUrl; // 阿里云OSS地址
export const staticUrl = envConfig.staticUrl; // 静态资源地址
// 公开图片域名:统一走 CDNdev: static-dev.hshuishang.com / prod: static.hshuishang.com
export const picUrl = envConfig.staticUrl;
export const aliyunOssUrl = envConfig.aliyunOssUrl; // 阿里云OSS直链地址(私密文件签名 URL 已自带 host一般不需要直接拼
export const staticUrl = envConfig.staticUrl; // CDN 加速域名
/**
* 处理图片URL根据环境自动替换
@ -326,70 +326,26 @@ export const floatCalculate = (num1, num2, operator) => {
};
/**
* 图片上传
* @param {string} filename - 图片上传地址
* @param {Function} fn - 接口回调函数
* 图片上传 OSS 直传回调形状对齐旧后端 { code, data: { path } }path / 开头
* @param {string} filename - 本地文件路径
* @param {Function} fn - 回调函数
* @param {string} [scene] - 场景默认 common
*/
export const upload = (filename, fn) => {
uni.showLoading({
title: "上传中",
mask: true,
});
uni.uploadFile({
url: RequsetUrl + "/api/v1/public/upload-image",
filePath: filename,
name: "image",
formData: {
uid: uni.getStorageSync("uid"),
},
success: (f) => {
uni.hideLoading();
fn(JSON.parse(f.data));
},
fail: (res) => {
uni.hideLoading();
console.log(res);
uni.showToast({
title: "上传文件失败",
icon: "none",
});
},
complete: () => {},
});
export const upload = (filename, fn, scene = "common") => {
// 引入此处避免顶层循环依赖
const { uploadOSSCompat } = require("./uploadOSS.js");
uploadOSSCompat(filename, fn, scene);
};
/**
* 视频上传
* @param {string} filename - 图片上传地址
* @param {Function} fn - 接口回调函数
* 视频上传 OSS 直传
* @param {string} filename - 本地文件路径
* @param {Function} fn - 回调函数
* @param {string} [scene] - 场景默认 video
*/
export const uploadVideo = (filename, fn) => {
uni.showLoading({
title: "上传中",
mask: true,
});
uni.uploadFile({
url: RequsetUrl + "/api/v1/public/upload-video",
filePath: filename,
name: "file",
formData: {
uid: uni.getStorageSync("uid"),
},
success: (f) => {
uni.hideLoading();
fn(JSON.parse(f.data));
},
fail: (res) => {
uni.hideLoading();
console.log(res);
uni.showToast({
title: "上传文件失败",
icon: "none",
});
},
complete: () => {},
});
export const uploadVideo = (filename, fn, scene = "video") => {
const { uploadOSSCompat } = require("./uploadOSS.js");
uploadOSSCompat(filename, fn, scene);
};
/**

150
utils/uploadOSS.js Normal file
View File

@ -0,0 +1,150 @@
// uniapp-ZHSQ —— OSS 直传客户端
//
// 用法:
// import { uploadOSS } from '@/utils/uploadOSS'
// const { objectKey, url } = await uploadOSS({ filePath: tempFilePath, scene: 'avatar' })
// // objectKey: OSS 上的相对 key存数据库
// // url: 公开访问 URL如果走签名 URL 显示,前端再单独处理)
//
// 支持的 scene 由服务端 wechatOssSceneCfg 控制,目前:
// - avatar 用户头像 (jpg/jpeg/png/webp<=5MB)
// - merchant 商家入驻资料 (jpg/jpeg/png/webp/pdf<=10MB)
import { RequsetUrl, aliyunOssUrl } from "@/utils/index.js";
const inferExt = (filePath) => {
if (!filePath) return "";
const idx = filePath.lastIndexOf(".");
if (idx < 0) return "";
return filePath.substring(idx + 1).toLowerCase();
};
const fetchCredential = (scene, ext) => {
return new Promise((resolve, reject) => {
uni.request({
url: RequsetUrl + "/api/v1/wechat/oss/credential",
method: "POST",
header: {
Authorization: uni.getStorageSync("ctoken") || "",
"Content-Type": "application/json",
},
data: { scene, ext },
success: (res) => {
if (res.statusCode !== 200) return reject(new Error("获取上传凭证失败:" + res.statusCode));
const body = res.data || {};
// 兼容服务端通用响应包装
const data = body.data || body;
if (!data || !data.host) return reject(new Error("上传凭证字段缺失"));
resolve(data);
},
fail: (err) => reject(err),
});
});
};
const postToOSS = (filePath, cred) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: cred.host,
filePath,
name: "file", // 必须是 fileOSS PostObject 协议约定
formData: {
key: cred.object_key,
OSSAccessKeyId: cred.access_key_id,
"x-oss-security-token": cred.security_token,
policy: cred.policy,
signature: cred.signature,
success_action_status: "200",
},
success: (res) => {
// OSS 200 -> 空 body 视为成功
if (res.statusCode === 200 || res.statusCode === 204) {
resolve();
} else {
reject(new Error("OSS 上传失败:" + res.statusCode + " " + (res.data || "")));
}
},
fail: (err) => reject(err),
});
});
};
/**
* 直传 OSS
* @param {Object} opts
* @param {string} opts.filePath 本地文件临时路径uni.chooseImage 拿到的 path
* @param {string} opts.scene 上传场景avatar / merchant
* @param {string} [opts.ext] 扩展名不含点不传则从 filePath 推断
* @param {boolean} [opts.showLoading=true]
* @returns {Promise<{ objectKey: string, url: string }>}
*/
export const uploadOSS = async ({ filePath, scene, ext, showLoading = true }) => {
if (!filePath) throw new Error("filePath 不能为空");
if (!scene) throw new Error("scene 不能为空");
const finalExt = (ext || inferExt(filePath)).toLowerCase();
if (!finalExt) throw new Error("无法识别文件扩展名");
if (showLoading) uni.showLoading({ title: "上传中", mask: true });
try {
const cred = await fetchCredential(scene, finalExt);
await postToOSS(filePath, cred);
return {
objectKey: cred.object_key,
url: aliyunOssUrl + "/" + cred.object_key,
};
} finally {
if (showLoading) uni.hideLoading();
}
};
/**
* 兼容旧版 utils/index.js upload(filename, fn) 的回调式 API
* 内部走 OSS 直传回调 res 形状对齐旧版res.data.path 为以 / 开头的 object key
* 调用方 picUrl + res.data.path 可以直接拼成完整 URL
*
* @param {string} filePath
* @param {Function} fn 接收 { code, msg, data: { path } }
* @param {string} [scene='merchant']
*/
export const uploadOSSCompat = (filePath, fn, scene = "merchant") => {
uploadOSS({ filePath, scene })
.then(({ objectKey }) => {
fn && fn({ code: 1, msg: "ok", data: { path: "/" + objectKey } });
})
.catch((err) => {
console.error("uploadOSSCompat 失败:", err);
uni.showToast({ title: err.message || "上传文件失败", icon: "none" });
});
};
/**
* 取私密文件查看 URL小程序商家本人查看自己上传的证件 / 合同
* 服务端会校验 path 必须以 wechat/merchant_private/{当前 user_id}/ 开头
*
* @param {string} objectKey 存数据库的 path带或不带前导 /
* @returns {Promise<{ url: string, expireSeconds: number }>}
*/
export const signPrivateView = (objectKey) => {
return new Promise((resolve, reject) => {
if (!objectKey) return reject(new Error("object_key 不能为空"));
const cleanKey = objectKey.replace(/^\/+/, "");
uni.request({
url: RequsetUrl + "/api/v1/wechat/oss/sign-private-view",
method: "POST",
header: {
Authorization: uni.getStorageSync("ctoken") || "",
"Content-Type": "application/json",
},
data: { object_key: cleanKey },
success: (res) => {
if (res.statusCode !== 200) return reject(new Error("签发失败:" + res.statusCode));
const body = res.data || {};
const data = body.data || body;
if (!data || !data.url) return reject(new Error("签发结果字段缺失"));
resolve({ url: data.url, expireSeconds: data.expire_seconds });
},
fail: (err) => reject(err),
});
});
};