beefast-website/src/components/PartnerRequest.vue
2025-03-04 11:52:11 +08:00

610 lines
14 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>
<div class="partner-request-container" @touchstart="handleTouchStart">
<div class="request-card">
<form @submit.prevent="submitRequest" class="request-form">
<div class="form-group">
<label for="name">您的姓名</label>
<input
type="text"
id="name"
v-model="formData.name"
placeholder="请输入您的姓名"
required
/>
</div>
<div class="form-group">
<label for="phone">您的电话</label>
<div class="phone-input-group">
<input
type="tel"
id="phone"
v-model="formData.phone"
placeholder="请输入您的联系电话"
required
pattern="[0-9]{11}"
/>
<button
type="button"
class="btn-send-code"
@click="sendVerificationCode"
:disabled="cooldown > 0"
>
{{ cooldown > 0 ? `${cooldown}秒后重发` : '发送验证码' }}
</button>
</div>
</div>
<div class="form-group">
<label for="phoneCode">验证码</label>
<input
type="text"
id="phoneCode"
v-model="formData.phone_code"
placeholder="请输入短信验证码"
required
/>
</div>
<div class="form-group">
<label>合伙类型</label>
<div class="partner-type-selector">
<div
class="partner-type-option"
:class="{ 'active': formData.partnerType === 'community' }"
@click="selectPartnerType('community')"
>
<div class="option-radio">
<div class="radio-inner" v-if="formData.partnerType === 'community'"></div>
</div>
<div class="option-label">小区合伙人</div>
</div>
<div
class="partner-type-option"
:class="{ 'active': formData.partnerType === 'city' }"
@click="selectPartnerType('city')"
>
<div class="option-radio">
<div class="radio-inner" v-if="formData.partnerType === 'city'"></div>
</div>
<div class="option-label">城市合伙人</div>
</div>
</div>
</div>
<div class="form-group">
<label for="serviceArea">
{{ formData.partnerType === 'community' ? '服务小区' : '服务城市' }}
</label>
<input
type="text"
id="serviceArea"
v-model="formData.serviceArea"
:placeholder="formData.partnerType === 'community' ? '请输入小区名称' : '请输入城市名称'"
required
/>
</div>
<div class="form-actions">
<button type="button" class="btn-cancel" @click="goBack">取消</button>
<button type="submit" class="btn-submit" :disabled="isSubmitting">
{{ isSubmitting ? '提交中...' : '提交申请' }}
</button>
</div>
</form>
</div>
<!-- 成功弹窗 -->
<div class="modal" v-if="showModal">
<div class="modal-content">
<div class="modal-icon" :class="{ 'success-icon': isSuccess, 'error-icon': !isSuccess }">
<span v-if="isSuccess"></span>
<span v-else>!</span>
</div>
<h3>{{ modalTitle }}</h3>
<p>{{ modalMessage }}</p>
<button @click="closeModal" class="btn-confirm">确定</button>
</div>
</div>
</div>
</template>
<script>
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import apiClient from '../services/api'
export default {
name: 'PartnerRequest',
setup() {
const router = useRouter()
const route = useRoute()
const isSubmitting = ref(false)
const showModal = ref(false)
const isSuccess = ref(true)
const modalTitle = ref('')
const modalMessage = ref('')
const userId = ref('')
const lastTouchEnd = ref(0)
const cooldown = ref(0)
const formData = reactive({
name: '',
phone: '',
phone_code: '',
partnerType: 'community',
serviceArea: ''
})
onMounted(() => {
// 从URL获取userId
userId.value = route.query.userid || ''
if (!userId.value) {
console.warn('未找到用户ID可能影响提交功能')
}
// 检测是否在小程序环境中
checkMiniProgramEnvironment()
// 设置页面标题
document.title = '合伙人申请'
// 添加额外的触摸事件处理
const handleTouchEnd = (event) => {
const now = Date.now()
if (now - lastTouchEnd.value <= 300) {
event.preventDefault()
}
lastTouchEnd.value = now
}
document.addEventListener('touchend', handleTouchEnd)
onBeforeUnmount(() => {
document.removeEventListener('touchend', handleTouchEnd)
document.title = '蜂快到家'
})
})
const checkMiniProgramEnvironment = () => {
// 检查是否在微信小程序环境中
const userAgent = navigator.userAgent.toLowerCase()
const isWechatMiniProgram = /miniprogram/.test(userAgent) || window.__wxjs_environment === 'miniprogram'
console.log('是否在小程序环境中:', isWechatMiniProgram)
}
const selectPartnerType = (type) => {
formData.partnerType = type
}
const sendVerificationCode = async () => {
// 验证手机号格式
if (!/^1[3-9]\d{9}$/.test(formData.phone)) {
alert('请输入正确的手机号码')
return
}
try {
// 发送验证码请求
await apiClient.post('/api/user/send-code', {
phone: formData.phone
})
// 设置倒计时
cooldown.value = 60
const timer = setInterval(() => {
cooldown.value--
if (cooldown.value <= 0) {
clearInterval(timer)
}
}, 1000)
alert('验证码已发送,请注意查收')
} catch (error) {
console.error('发送验证码失败:', error)
alert('发送验证码失败,请稍后重试')
}
}
const submitRequest = async () => {
if (isSubmitting.value) return
isSubmitting.value = true
try {
// 构建请求数据
const requestData = {
user_id: userId.value || 0,
name: formData.name,
phone: formData.phone,
phone_code: formData.phone_code,
type: formData.partnerType,
service_target: formData.serviceArea
}
// 发送请求
const response = await apiClient.post('/api/feedback/partner-apply', requestData)
// 检查返回的状态码
if (response.data && response.data.code === 200) {
console.log('申请提交成功:', response.data)
// 显示成功弹窗
isSuccess.value = true
modalTitle.value = '申请提交成功'
modalMessage.value = '我们将尽快处理您的合伙人申请'
showModal.value = true
// 重置表单
formData.name = ''
formData.phone = ''
formData.phone_code = ''
formData.serviceArea = ''
} else {
// 显示错误弹窗
const errorMessage = response.data?.message || '提交失败,请稍后重试'
console.error('提交申请失败:', errorMessage)
isSuccess.value = false
modalTitle.value = '提交失败'
modalMessage.value = errorMessage
showModal.value = true
}
} catch (error) {
console.error('提交申请失败:', error)
// 尝试从错误响应中获取详细信息
const errorMessage = error.response?.data?.message || '提交失败,请稍后重试'
isSuccess.value = false
modalTitle.value = '提交失败'
modalMessage.value = errorMessage
showModal.value = true
} finally {
isSubmitting.value = false
}
}
const closeModal = () => {
showModal.value = false
// 如果是成功状态,关闭弹窗后返回上一页
if (isSuccess.value) {
goBack()
}
}
const goBack = () => {
// 如果在小程序环境中,调用小程序的返回方法
if (window.wx && window.wx.miniProgram) {
window.wx.miniProgram.navigateBack({ delta: 1 })
} else {
// 否则使用路由返回
router.back()
}
}
const handleTouchStart = (event) => {
// 防止iOS双击缩放
if (event.touches.length > 1) {
event.preventDefault()
}
}
return {
formData,
isSubmitting,
showModal,
isSuccess,
modalTitle,
modalMessage,
selectPartnerType,
submitRequest,
closeModal,
goBack,
handleTouchStart,
sendVerificationCode,
cooldown
}
}
}
</script>
<style scoped>
.partner-request-container {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 20px 15px 30px;
background: linear-gradient(to bottom, #FFCC00, #FFFFFF);
position: relative;
overflow: hidden;
}
.request-card {
width: 100%;
max-width: 98%;
background-color: #FFFFFF;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 25px 20px;
}
.page-title {
font-size: 24px;
font-weight: bold;
color: #333333;
margin-bottom: 25px;
text-align: center;
}
.request-form {
display: flex;
flex-direction: column;
gap: 20px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-group label {
font-size: 16px;
font-weight: 500;
color: #333333;
}
.form-group input {
padding: 12px 15px;
border: 1px solid #DDDDDD;
border-radius: 8px;
font-size: 16px;
color: #333333;
background-color: #F9F9F9;
transition: border-color 0.3s, box-shadow 0.3s;
}
.form-group input:focus {
border-color: #FFCC00;
box-shadow: 0 0 0 2px rgba(255, 204, 0, 0.2);
outline: none;
}
/* 手机号输入组样式 */
.phone-input-group {
display: flex;
gap: 10px;
}
.phone-input-group input {
flex: 1;
}
.btn-send-code {
padding: 0 15px;
background-color: #FFCC00;
color: #333333;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
white-space: nowrap;
transition: background-color 0.3s;
}
.btn-send-code:hover {
background-color: #FFD633;
}
.btn-send-code:disabled {
background-color: #DDDDDD;
color: #999999;
cursor: not-allowed;
}
/* 合伙类型选择器样式 */
.partner-type-selector {
display: flex;
gap: 15px;
}
.partner-type-option {
flex: 1;
display: flex;
align-items: center;
padding: 12px 15px;
border: 1px solid #DDDDDD;
border-radius: 8px;
background-color: #F9F9F9;
cursor: pointer;
transition: all 0.3s;
}
.partner-type-option.active {
border-color: #FFCC00;
background-color: rgba(255, 204, 0, 0.1);
}
.option-radio {
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid #DDDDDD;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
transition: all 0.3s;
}
.partner-type-option.active .option-radio {
border-color: #FFCC00;
}
.radio-inner {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #FFCC00;
}
.option-label {
font-size: 16px;
color: #333333;
}
.form-actions {
display: flex;
gap: 15px;
margin-top: 10px;
}
.btn-cancel, .btn-submit {
flex: 1;
padding: 12px 15px;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
text-align: center;
}
.btn-cancel {
background-color: #F2F2F2;
color: #666666;
border: none;
}
.btn-submit {
background-color: #FFCC00;
color: #333333;
border: none;
}
.btn-cancel:hover {
background-color: #E6E6E6;
}
.btn-submit:hover {
background-color: #FFD633;
}
.btn-submit:disabled {
background-color: #DDDDDD;
color: #999999;
cursor: not-allowed;
}
/* 成功弹窗样式 */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background-color: #FFFFFF;
border-radius: 15px;
padding: 30px;
width: 80%;
max-width: 350px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.modal-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
color: white;
margin-bottom: 20px;
}
.success-icon {
background-color: #FFCC00;
}
.error-icon {
background-color: #FF6B6B;
}
.modal-content h3 {
font-size: 20px;
margin-bottom: 10px;
color: #333333;
}
.modal-content p {
color: #666666;
margin-bottom: 25px;
}
.btn-confirm {
background-color: #FFCC00;
color: #333333;
border: none;
border-radius: 8px;
padding: 12px 15px;
width: 100%;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s;
}
.btn-confirm:hover {
background-color: #FFD633;
}
/* 响应式调整 */
@media (max-width: 480px) {
.request-card {
padding: 20px 15px;
}
.page-title {
font-size: 20px;
margin-bottom: 20px;
}
.form-group label {
font-size: 15px;
}
.form-group input,
.btn-cancel,
.btn-submit {
padding: 10px 12px;
font-size: 15px;
}
.partner-type-option {
padding: 10px 12px;
}
.option-label {
font-size: 14px;
}
}
</style>