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

924 lines
21 KiB
Vue
Raw Normal View History

2025-03-29 16:01:43 +08:00
<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>
2025-03-29 16:01:43 +08:00
</view>
<view class="step-labels flex align-center">
<view>提交申请</view>
<view>平台审核</view>
<view>享受返利</view>
2025-03-29 16:01:43 +08:00
</view>
</view>
<!-- 咨询入口 -->
<view class="consult-box flex align-center justify-between">
<text class="consult-text">认证门店助手帮你解决认证问题快速上手赢取品牌返利</text>
<button class="consult-btn" @click="handleConsult">咨询</button>
2025-03-29 16:01:43 +08:00
</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>
2025-03-29 16:01:43 +08:00
</view>
<image src="@/static/bg/bgs.png" mode="aspectFit" class="material-image"></image>
2025-03-29 16:01:43 +08:00
</view>
<view class="material-item">
<view class="material-info">
<text class="material-name">2.门头照片</text>
<text class="material-desc">需提供营业执照对应的门店门头照片一张</text>
2025-03-29 16:01:43 +08:00
</view>
<image src="@/static/bg/bgs.png" mode="aspectFit" class="material-image"></image>
2025-03-29 16:01:43 +08:00
</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"
/>
2025-03-29 16:01:43 +08:00
</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>
2025-03-29 16:01:43 +08:00
</view>
<!-- 门店名称 -->
<view class="form-item">
<input
v-model="form.bar_name"
placeholder="门店名称"
class="form-input"
/>
2025-03-29 16:01:43 +08:00
</view>
<!-- 联系电话 -->
<view class="form-item">
<input
v-model="form.bar_contact_phone"
placeholder="联系电话"
type="number"
maxlength="11"
@input="validatePhone"
class="form-input"
/>
2025-03-29 16:01:43 +08:00
</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>
2025-03-29 16:01:43 +08:00
</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>
2025-03-29 16:01:43 +08:00
</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>
2025-03-29 16:01:43 +08:00
</view>
<!-- 底部提交按钮 -->
<view class="submit-bar">
<button
class="submit-btn"
:disabled="!isFormValid"
@click="submitForm"
>
提交认证门店
</button>
2025-03-29 16:01:43 +08:00
</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)
}
2025-03-29 16:01:43 +08:00
},
// 地区选择相关方法
handleShowAreaPicker() {
if (this.showPopup) return
this.showPopup = true
2025-03-29 16:01:43 +08:00
},
// 地区选择回调
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'
2025-03-29 16:01:43 +08:00
})
}
},
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()
}
}
})
}
}
2025-03-29 16:01:43 +08:00
})
}
})
},
// 位置选择方法
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'
})
}
2025-03-29 16:01:43 +08:00
}
})
},
// 手机号验证相关方法
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('选择图片失败')
2025-03-29 16:01:43 +08:00
}
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'
}
2025-03-29 16:01:43 +08:00
})
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])
}
2025-03-29 16:01:43 +08:00
})
// 监听上传进度
uploadTask.onProgressUpdate((res) => {
console.log('上传进度:', res.progress)
if (res.progress === 100) {
console.log('上传完成')
}
2025-03-29 16:01:43 +08:00
})
})
// 3. 处理上传结果
if (uploadError) {
console.error('上传错误:', uploadError)
throw new Error(uploadError.errMsg || '上传失败,请重试')
2025-03-29 16:01:43 +08:00
}
if (uploadRes.statusCode !== 200) {
console.error('服务器响应错误:', {
statusCode: uploadRes.statusCode,
data: uploadRes.data,
headers: uploadRes.header
2025-03-29 16:01:43 +08:00
})
throw new Error(`服务器响应错误: ${uploadRes.statusCode},请检查服务器日志`)
2025-03-29 16:01:43 +08:00
}
// 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('服务器响应格式错误')
2025-03-29 16:01:43 +08:00
}
// 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'
2025-03-29 16:01:43 +08:00
uni.showToast({
title: '上传成功',
icon: 'success'
2025-03-29 16:01:43 +08:00
})
} else {
console.error('业务处理失败:', result)
throw new Error(result.msg || '上传失败')
2025-03-29 16:01:43 +08:00
}
} 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 {
2025-03-29 16:01:43 +08:00
uni.showLoading({
title: '提交中',
mask: true
})
// 构建提交数据
const submitData = {
...this.form,
auth_state: 0 // 设置默认认证状态
}
2025-03-29 16:01:43 +08:00
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'
2025-03-29 16:01:43 +08:00
})
} finally {
uni.hideLoading()
2025-03-29 16:01:43 +08:00
}
},
// 其他方法
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
2025-03-29 16:01:43 +08:00
}
}
}
2025-03-29 16:01:43 +08:00
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background-color: #F7F8FA;
}
.page-content {
padding: 24rpx 24rpx 200rpx;
box-sizing: border-box;
}
2025-03-29 16:01:43 +08:00
// 步骤条样式
.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;
2025-03-29 16:01:43 +08:00
}
.step-arrow {
width: 44rpx;
height: 44rpx;
2025-03-29 16:01:43 +08:00
}
}
.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;
2025-03-29 16:01:43 +08:00
.material-info {
width: 60%;
.material-name {
display: block;
2025-03-29 16:01:43 +08:00
font-size: 28rpx;
color: #3D3D3D;
margin-bottom: 8rpx;
}
.material-desc {
font-size: 24rpx;
color: #666;
2025-03-29 16:01:43 +08:00
}
}
.material-image {
width: 166rpx;
height: 112rpx;
2025-03-29 16:01:43 +08:00
}
}
}
// 表单样式
.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);
2025-03-29 16:01:43 +08:00
}
&[readonly] {
background: #F5F5F5;
cursor: not-allowed;
2025-03-29 16:01:43 +08:00
}
}
}
// 位置选择器样式
.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;
2025-03-29 16:01:43 +08:00
}
}
.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;
2025-03-29 16:01:43 +08:00
}
.upload-success {
color: #67C23A;
margin-right: 16rpx;
font-size: 32rpx;
2025-03-29 16:01:43 +08:00
}
.upload-btn {
font-size: 28rpx;
color: #FEE034;
padding: 16rpx 0;
&.is-success {
color: #67C23A;
2025-03-29 16:01:43 +08:00
}
}
}
}
.image-preview {
margin: 16rpx 0;
width: 100%;
height: 300rpx;
background: #F5F5F5;
border-radius: 12rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
object-fit: contain;
2025-03-29 16:01:43 +08:00
}
}
// 底部提交按钮样式
.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;
2025-03-29 16:01:43 +08:00
font-size: 32rpx;
font-weight: 600;
transition: all 0.3s;
&:active {
transform: scale(0.98);
}
&[disabled] {
opacity: 0.6;
background: #999;
}
2025-03-29 16:01:43 +08:00
}
}
// 弹窗样式
.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;
}
2025-03-29 16:01:43 +08:00
}
}
2025-03-29 16:01:43 +08:00
.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;
2025-03-29 16:01:43 +08:00
}
.success-text {
font-size: 28rpx;
color: #333;
margin-bottom: 32rpx;
2025-03-29 16:01:43 +08:00
}
.success-btn {
width: 314rpx;
height: 76rpx;
line-height: 76rpx;
background: #4E63E0;
color: #FFF;
font-size: 28rpx;
border-radius: 38rpx;
2025-03-29 16:01:43 +08:00
}
}
// 通用样式
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 28rpx;
}
2025-03-29 16:01:43 +08:00
</style>