zdtap-uniapp-main/pages/index/registration.vue

924 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page-container">
<view class="page-content">
<!-- 步骤条 -->
<view class="step-bar">
<view class="step-icons flex align-center">
<view><image src="@/static/one.png" class="step-icon" mode="aspectFit"></image></view>
<view><image src="@/static/arrow-right.png" class="step-arrow" mode="aspectFit"></image></view>
<view><image src="@/static/two.png" class="step-icon" mode="aspectFit"></image></view>
<view><image src="@/static/arrow-right.png" class="step-arrow" mode="aspectFit"></image></view>
<view><image src="@/static/three.png" class="step-icon" mode="aspectFit"></image></view>
</view>
<view class="step-labels flex align-center">
<view>提交申请</view>
<view>平台审核</view>
<view>享受返利</view>
</view>
</view>
<!-- 咨询入口 -->
<view class="consult-box flex align-center justify-between">
<text class="consult-text">认证门店助手帮你解决认证问题快速上手赢取品牌返利</text>
<button class="consult-btn" @click="handleConsult">咨询</button>
</view>
<!-- 所需材料说明 -->
<view class="materials-box">
<view class="section-title">所需材料</view>
<view class="material-item">
<view class="material-info">
<text class="material-name">1.营业执照</text>
<text class="material-desc">需提供有效期内商家本人的与门头照片相符合的营业执照</text>
</view>
<image src="@/static/bg/bgs.png" mode="aspectFit" class="material-image"></image>
</view>
<view class="material-item">
<view class="material-info">
<text class="material-name">2.门头照片</text>
<text class="material-desc">需提供营业执照对应的门店门头照片一张</text>
</view>
<image src="@/static/bg/bgs.png" mode="aspectFit" class="material-image"></image>
</view>
</view>
<!-- 门店资料表单 -->
<view class="store-form">
<view class="section-title">门店资料</view>
<!-- 所在地区 -->
<view class="form-item">
<input
v-model="displayArea"
placeholder="所在地区"
readonly
disabled
@click.stop="handleShowAreaPicker"
class="form-input location-input"
/>
</view>
<!-- 门店地址 -->
<view class="form-item">
<view class="location-picker" @click="getMyLocation">
<text class="location-text" :class="{ 'is-placeholder': !form.address }">
{{ form.address || '门店地址' }}
</text>
<text class="cuIcon-locationfill location-icon"></text>
</view>
</view>
<!-- 门店名称 -->
<view class="form-item">
<input
v-model="form.bar_name"
placeholder="门店名称"
class="form-input"
/>
</view>
<!-- 联系电话 -->
<view class="form-item">
<input
v-model="form.bar_contact_phone"
placeholder="联系电话"
type="number"
maxlength="11"
@input="validatePhone"
class="form-input"
/>
</view>
<!-- 营业执照上传 -->
<view class="form-item">
<view class="upload-box">
<text class="upload-label">营业执照</text>
<view class="upload-right">
<text v-if="uploadStatus.business_license === 'uploading'" class="upload-progress">上传中...</text>
<text v-else-if="uploadStatus.business_license === 'success'" class="upload-success"></text>
<text
class="upload-btn"
:class="{'is-success': form.business_license}"
@click="handleUpload('business_license')"
>
{{ form.business_license ? '重新上传' : '点击上传' }}
</text>
</view>
</view>
<view v-if="previewImages.business_license" class="image-preview">
<image :src="previewImages.business_license" mode="aspectFit"></image>
</view>
</view>
<!-- 门头照片上传 -->
<view class="form-item">
<view class="upload-box">
<text class="upload-label">门头照片</text>
<view class="upload-right">
<text v-if="uploadStatus.storefront_photo === 'uploading'" class="upload-progress">上传中...</text>
<text v-else-if="uploadStatus.storefront_photo === 'success'" class="upload-success"></text>
<text
class="upload-btn"
:class="{'is-success': form.storefront_photo}"
@click="handleUpload('storefront_photo')"
>
{{ form.storefront_photo ? '重新上传' : '点击上传' }}
</text>
</view>
</view>
<view v-if="previewImages.storefront_photo" class="image-preview">
<image :src="previewImages.storefront_photo" mode="aspectFit"></image>
</view>
</view>
</view>
<!-- 地区选择器组件 -->
<regionPicker
:show.sync="showPopup"
@selected="onAreaSelected"
@close="handlePopupClose"
/>
<!-- 咨询弹窗 -->
<uni-popup ref="consultRefs" type="center" :is-mask-click="false">
<view class="consult-popup">
<view class="popup-close" @click="consultClick">
<image src="@/static/icons/cencels.svg" mode="aspectFit" class="close-icon"></image>
</view>
<view class="popup-content"></view>
</view>
</uni-popup>
<!-- 成功提示弹窗 -->
<uni-popup ref="successRef" type="center" :is-mask-click="false">
<view class="success-popup">
<image src="@/static/cg.png" mode="aspectFit" class="success-icon"></image>
<text class="success-text">提交成功等待审核</text>
<button class="success-btn" @click="toHome">进入首页</button>
</view>
</uni-popup>
</view>
<!-- 底部提交按钮 -->
<view class="submit-bar">
<button
class="submit-btn"
:disabled="!isFormValid"
@click="submitForm"
>
提交认证门店
</button>
</view>
</view>
</template>
<script>
import regionPicker from '@/components/regionPicker.vue'
import { barRegister } from '@/api/login.js'
import { base_url } from '@/api/config.js'
export default {
name: 'Registration',
components: {
regionPicker
},
data() {
return {
// 表单数据
form: {
bar_name: '', // 酒吧名称必填最大255
bar_contact_phone: '', // 联系电话最大20
province: '', // 所在省最大20
city: '', // 所在城市最大20
district: '', // 所在区最大50
address: '', // 详细地址最大255
business_license: '', // 营业执照最大255
storefront_photo: '', // 门头照片最大255
latitude: '', // 纬度
longitude: '', // 经度
openId: '', // 用户openId
auth_state: 0, // 认证状态0待审核
},
// 用于显示的完整地区文本
displayArea: '',
// 图片预览
previewImages: {
business_license: '',
storefront_photo: ''
},
// 上传状态
uploadStatus: {
business_license: '', // '', 'uploading', 'success', 'error'
storefront_photo: ''
},
// 地区选择
showPopup: false,
// 用户信息
userInfo: {},
}
},
computed: {
// 表单验证
isFormValid() {
return (
this.form.bar_name && // 酒吧名称
this.form.bar_contact_phone && // 联系电话
this.checkPhoneFormat() && // 手机号格式
this.form.province && // 省
this.form.city && // 市
this.form.district && // 区
this.form.address && // 详细地址
this.form.latitude && // 纬度
this.form.longitude && // 经度
this.form.business_license && // 营业执照
this.form.storefront_photo // 门头照片
)
}
},
watch: {
showPopup(newVal) {
if (!newVal && !this.form.bar) {
this.form.province = ''
this.form.city = ''
this.form.district = ''
}
}
},
onLoad({ openId }) {
this.form.openId = openId
},
onHide() {
uni.hideLoading()
},
methods: {
// 防抖函数
debounce(fn, delay = 500) {
let timer = null
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay)
}
},
// 地区选择相关方法
handleShowAreaPicker() {
if (this.showPopup) return
this.showPopup = true
},
// 地区选择回调
onAreaSelected(province, city, area) {
try {
if (province?.label && city?.label && area?.label) {
// 分别存储省市区
this.form.province = province.label
this.form.city = city.label
this.form.district = area.label
// 更新显示文本
this.displayArea = `${province.label} ${city.label} ${area.label}`.trim()
this.showPopup = false
} else {
throw new Error('地区信息不完整')
}
} catch (error) {
uni.showToast({
title: error.message || '请选择完整的地区信息',
icon: 'none'
})
}
},
handlePopupClose() {
this.showPopup = false
},
// 位置选择相关方法
getMyLocation() {
uni.authorize({
scope: 'scope.userLocation',
success: () => {
this.handleChooseLocation()
},
fail: () => {
uni.showModal({
title: '位置权限',
content: '需要获取您的地理位置,请确认授权',
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: (settingRes) => {
if (settingRes.authSetting['scope.userLocation']) {
this.handleChooseLocation()
}
}
})
}
}
})
}
})
},
// 位置选择方法
handleChooseLocation() {
uni.chooseLocation({
success: (res) => {
if (res.address && res.latitude && res.longitude) {
// 更新地址和经纬度
this.form.address = res.address
this.form.latitude = res.latitude
this.form.longitude = res.longitude
// 尝试从地址中解析省市区(如果后端没有提供)
const addressParts = res.address.split(' ')
if (addressParts.length >= 3) {
// 通常第一部分是省,第二部分是市,第三部分是区
if (!this.form.province) this.form.province = addressParts[0]
if (!this.form.city) this.form.city = addressParts[1]
if (!this.form.district) this.form.district = addressParts[2]
}
} else {
uni.showToast({
title: '请选择具体的位置信息',
icon: 'none'
})
}
},
fail: (err) => {
if (err.errMsg !== 'chooseLocation:fail cancel') {
uni.showToast({
title: '获取位置失败',
icon: 'none'
})
}
}
})
},
// 手机号验证相关方法
validatePhone(e) {
const phone = e.target.value
const formatted = phone.replace(/\D/g, '').slice(0, 11)
// 确保不超过数据库字段长度
this.form.bar_contact_phone = formatted.slice(0, 20)
},
checkPhoneFormat() {
return /^1[3-9]\d{9}$/.test(this.form.bar_contact_phone)
},
// 图片上传相关方法
async handleUpload(key) {
try {
// 1. 选择图片
const [chooseError, chooseRes] = await uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
// 限制图片大小
maxSize: 5 * 1024 * 1024 // 5MB
}).then(res => [null, res]).catch(err => [err, null])
if (chooseError) {
console.error('选择图片错误:', chooseError)
throw new Error('选择图片失败')
}
const tempFilePath = chooseRes.tempFilePaths[0]
// 更新预览和状态
this.previewImages[key] = tempFilePath
this.uploadStatus[key] = 'uploading'
// 2. 上传图片
const [uploadError, uploadRes] = await new Promise((resolve) => {
console.log('开始上传图片:', {
key,
tempFilePath,
formData: {
type: 'image',
module: key,
fileType: 'image/jpeg',
businessType: 'bar_registration'
}
})
const uploadTask = uni.uploadFile({
url: base_url + '/bar/uploadImg',
filePath: tempFilePath,
name: 'file',
header: {
'content-type': 'multipart/form-data',
'Authorization': uni.getStorageSync('token') || ''
},
formData: {
type: 'image',
module: key,
fileType: 'image/jpeg',
businessType: 'bar_registration'
},
success: (res) => {
console.log('上传成功响应:', {
statusCode: res.statusCode,
data: res.data,
key: key,
url: res.data?.url,
headers: res.header
})
resolve([null, res])
},
fail: (err) => {
console.error('上传失败:', {
error: err,
key: key,
tempFilePath: tempFilePath
})
resolve([err, null])
}
})
// 监听上传进度
uploadTask.onProgressUpdate((res) => {
console.log('上传进度:', res.progress)
if (res.progress === 100) {
console.log('上传完成')
}
})
})
// 3. 处理上传结果
if (uploadError) {
console.error('上传错误:', uploadError)
throw new Error(uploadError.errMsg || '上传失败,请重试')
}
if (uploadRes.statusCode !== 200) {
console.error('服务器响应错误:', {
statusCode: uploadRes.statusCode,
data: uploadRes.data,
headers: uploadRes.header
})
throw new Error(`服务器响应错误: ${uploadRes.statusCode},请检查服务器日志`)
}
// 4. 解析响应数据
let result
try {
result = typeof uploadRes.data === 'string' ?
JSON.parse(uploadRes.data) : uploadRes.data
console.log('解析后的响应数据:', result)
} catch (error) {
console.error('响应数据解析错误:', error)
throw new Error('服务器响应格式错误')
}
// 5. 处理业务结果
if (result.code === 200 && result.data?.url) {
// 验证URL长度
if (result.data.url.length > 255) {
throw new Error('图片URL超出长度限制')
}
this.form[key] = result.data.url
this.uploadStatus[key] = 'success'
uni.showToast({
title: '上传成功',
icon: 'success'
})
} else {
console.error('业务处理失败:', result)
throw new Error(result.msg || '上传失败')
}
} catch (error) {
console.error('完整上传错误:', error)
this.previewImages[key] = ''
this.uploadStatus[key] = 'error'
uni.showToast({
title: error.message || '上传失败',
icon: 'none',
duration: 2000
})
}
},
// 表单提交相关方法
async submitForm() {
if (!this.isFormValid || !this.validateForm()) {
return
}
try {
uni.showLoading({
title: '提交中',
mask: true
})
// 构建提交数据
const submitData = {
...this.form,
auth_state: 0 // 设置默认认证状态
}
const res = await barRegister(submitData)
if (res.code === 200) {
this.userInfo = res.data.register
uni.setStorageSync('token', res.data.token)
uni.setStorageSync('userInfo', this.userInfo)
uni.reLaunch({
url: '/pages/index/index'
})
} else {
throw new Error(res.msg || '注册失败')
}
} catch (error) {
uni.showToast({
title: error.message || '提交失败',
icon: 'none'
})
} finally {
uni.hideLoading()
}
},
// 其他方法
toHome() {
uni.setStorageSync('userInfo', this.userInfo)
uni.reLaunch({
url: '/pages/index/index'
})
},
handleConsult() {
uni.navigateTo({
url: '/pagesMy/addAiad'
})
},
consultClick() {
this.$refs.consultRefs.close()
},
// 字段验证方法
validateField(value, maxLength) {
if (!value) return false
return String(value).length <= maxLength
},
// 表单验证方法
validateForm() {
// 字段长度验证
const validations = {
bar_name: { value: this.form.bar_name, maxLength: 255, label: '酒吧名称' },
bar_contact_phone: { value: this.form.bar_contact_phone, maxLength: 20, label: '联系电话' },
province: { value: this.form.province, maxLength: 20, label: '所在省' },
city: { value: this.form.city, maxLength: 20, label: '所在城市' },
district: { value: this.form.district, maxLength: 50, label: '所在区' },
address: { value: this.form.address, maxLength: 255, label: '详细地址' },
business_license: { value: this.form.business_license, maxLength: 255, label: '营业执照' },
storefront_photo: { value: this.form.storefront_photo, maxLength: 255, label: '门头照片' }
}
for (const [field, config] of Object.entries(validations)) {
if (!this.validateField(config.value, config.maxLength)) {
uni.showToast({
title: `${config.label}超出长度限制`,
icon: 'none'
})
return false
}
}
return true
}
}
}
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background-color: #F7F8FA;
}
.page-content {
padding: 24rpx 24rpx 200rpx;
box-sizing: border-box;
}
// 步骤条样式
.step-bar {
background: #FFFFFF;
padding: 42rpx 66rpx;
border-radius: 12rpx;
box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
.step-icons {
justify-content: space-between;
margin-bottom: 18rpx;
.step-icon {
width: 94rpx;
height: 72rpx;
}
.step-arrow {
width: 44rpx;
height: 44rpx;
}
}
.step-labels {
justify-content: space-between;
font-size: 24rpx;
font-weight: 600;
color: #333;
}
}
// 咨询入口样式
.consult-box {
margin-top: 32rpx;
padding: 24rpx;
background: #FFF;
border-radius: 12rpx;
box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
.consult-text {
width: 52%;
font-size: 24rpx;
color: #333;
}
.consult-btn {
width: 124rpx;
height: 48rpx;
margin-left: 180rpx;
line-height: 48rpx;
background: #D42E78;
color: #FFF;
font-size: 24rpx;
border-radius: 12rpx;
}
}
// 材料说明样式
.materials-box {
margin-top: 32rpx;
padding: 24rpx;
background: #FFF;
border-radius: 12rpx;
box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
.material-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 28rpx;
.material-info {
width: 60%;
.material-name {
display: block;
font-size: 28rpx;
color: #3D3D3D;
margin-bottom: 8rpx;
}
.material-desc {
font-size: 24rpx;
color: #666;
}
}
.material-image {
width: 166rpx;
height: 112rpx;
}
}
}
// 表单样式
.store-form {
margin-top: 32rpx;
padding: 24rpx;
background: #FFF;
border-radius: 12rpx;
box-shadow: 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
.form-item {
margin-bottom: 16rpx;
}
.form-input {
width: 100%;
height: 96rpx;
padding: 0 30rpx;
background: #FFF;
border: 1px solid #E5E5E5;
border-radius: 16rpx;
font-size: 28rpx;
color: #333;
transition: all 0.3s;
&:focus {
border-color: #4E63E0;
box-shadow: 0 0 0 2px rgba(78, 99, 224, 0.1);
}
&[readonly] {
background: #F5F5F5;
cursor: not-allowed;
}
}
}
// 位置选择器样式
.location-picker {
display: flex;
align-items: center;
justify-content: space-between;
height: 96rpx;
padding: 0 30rpx;
background: #FFF;
border: 1px solid #E5E5E5;
border-radius: 16rpx;
.location-text {
flex: 1;
font-size: 28rpx;
color: #333;
&.is-placeholder {
color: #999;
}
}
.location-icon {
color: #4E63E0;
font-size: 42rpx;
}
}
// 上传组件样式
.upload-box {
display: flex;
align-items: center;
justify-content: space-between;
height: 96rpx;
padding: 0 30rpx;
background: #FFF;
border: 1px solid #E5E5E5;
border-radius: 16rpx;
.upload-label {
font-size: 28rpx;
color: #333;
}
.upload-right {
display: flex;
align-items: center;
.upload-progress {
font-size: 28rpx;
color: #4E63E0;
margin-right: 16rpx;
}
.upload-success {
color: #67C23A;
margin-right: 16rpx;
font-size: 32rpx;
}
.upload-btn {
font-size: 28rpx;
color: #FEE034;
padding: 16rpx 0;
&.is-success {
color: #67C23A;
}
}
}
}
.image-preview {
margin: 16rpx 0;
width: 100%;
height: 300rpx;
background: #F5F5F5;
border-radius: 12rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
object-fit: contain;
}
}
// 底部提交按钮样式
.submit-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 32rpx 48rpx 24rpx;
background: #FFF;
box-shadow: 0 -1rpx 3rpx rgba(0, 0, 0, 0.1);
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: linear-gradient(135deg, #19367A, #2C4C99);
border-radius: 44rpx;
color: #FFF;
font-size: 32rpx;
font-weight: 600;
transition: all 0.3s;
&:active {
transform: scale(0.98);
}
&[disabled] {
opacity: 0.6;
background: #999;
}
}
}
// 弹窗样式
.consult-popup {
position: relative;
width: 676rpx;
background: #FFF;
border-radius: 12rpx;
.popup-close {
position: absolute;
top: 10rpx;
right: 20rpx;
padding: 10rpx;
.close-icon {
width: 48rpx;
height: 48rpx;
}
}
}
.success-popup {
width: 676rpx;
height: 596rpx;
background: #FFF;
border-radius: 12rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.success-icon {
width: 376rpx;
height: 262rpx;
margin-bottom: 32rpx;
}
.success-text {
font-size: 28rpx;
color: #333;
margin-bottom: 32rpx;
}
.success-btn {
width: 314rpx;
height: 76rpx;
line-height: 76rpx;
background: #4E63E0;
color: #FFF;
font-size: 28rpx;
border-radius: 38rpx;
}
}
// 通用样式
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 28rpx;
}
</style>