This commit is contained in:
aaron 2025-02-28 23:01:43 +08:00
parent 4d9d0b939c
commit 4693af2e8f
9 changed files with 695 additions and 19 deletions

1
.env.development Normal file
View File

@ -0,0 +1 @@
VUE_APP_ENV=dev

1
.env.production Normal file
View File

@ -0,0 +1 @@
VUE_APP_ENV=prd

1
public/index.html Normal file
View File

@ -0,0 +1 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

View File

@ -1,8 +1,60 @@
<template> <template>
<div class="min-h-screen bg-gradient-to-b from-primary/5 to-primary/10"> <div id="app" @touchstart="handleTouchStart">
<router-view></router-view> <div class="min-h-screen bg-gradient-to-b from-primary/5 to-primary/10">
<router-view></router-view>
</div>
</div> </div>
</template> </template>
<script setup> <script>
export default {
name: 'App',
data() {
return {
lastTouchEnd: 0
}
},
methods: {
handleTouchStart(event) {
//
if (event.touches.length > 1) {
event.preventDefault()
}
}
},
mounted() {
//
document.addEventListener('touchend', (event) => {
const now = Date.now()
if (now - this.lastTouchEnd <= 300) {
event.preventDefault()
}
this.lastTouchEnd = now
}, { passive: false })
//
document.addEventListener('touchmove', (event) => {
if (event.touches.length > 1) {
event.preventDefault()
}
}, { passive: false })
},
beforeUnmount() {
//
document.removeEventListener('touchend')
document.removeEventListener('touchmove')
}
}
</script> </script>
<style>
#app {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version */
touch-action: manipulation; /* 优化触摸操作 */
}
</style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="community-request-container"> <div class="community-request-container" @touchstart="handleTouchStart">
<div class="request-card"> <div class="request-card">
<h2 class="page-title">小区开通申请</h2> <h2 class="page-title">小区开通申请</h2>
@ -47,9 +47,9 @@
</template> </template>
<script> <script>
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import axios from 'axios' import apiClient from '../services/api'
export default { export default {
name: 'CommunityRequest', name: 'CommunityRequest',
@ -59,6 +59,7 @@ export default {
const isSubmitting = ref(false) const isSubmitting = ref(false)
const showSuccessModal = ref(false) const showSuccessModal = ref(false)
const userId = ref('') const userId = ref('')
const lastTouchEnd = ref(0)
const formData = reactive({ const formData = reactive({
communityName: '', communityName: '',
@ -78,8 +79,48 @@ export default {
// //
document.title = '小区开通申请' document.title = '小区开通申请'
//
const handleTouchEnd = (event) => {
const now = Date.now()
if (now - lastTouchEnd.value <= 300) {
event.preventDefault()
}
lastTouchEnd.value = now
}
const handleTouchMove = (event) => {
if (event.touches.length > 1) {
event.preventDefault()
}
}
document.addEventListener('touchend', handleTouchEnd, { passive: false })
document.addEventListener('touchmove', handleTouchMove, { passive: false })
// 便
window._communityRequestTouchHandlers = {
handleTouchEnd,
handleTouchMove
}
}) })
onBeforeUnmount(() => {
//
if (window._communityRequestTouchHandlers) {
document.removeEventListener('touchend', window._communityRequestTouchHandlers.handleTouchEnd)
document.removeEventListener('touchmove', window._communityRequestTouchHandlers.handleTouchMove)
delete window._communityRequestTouchHandlers
}
})
const handleTouchStart = (event) => {
//
if (event.touches.length > 1) {
event.preventDefault()
}
}
const checkMiniProgramEnvironment = () => { const checkMiniProgramEnvironment = () => {
// //
const ua = navigator.userAgent.toLowerCase() const ua = navigator.userAgent.toLowerCase()
@ -97,20 +138,17 @@ export default {
isSubmitting.value = true isSubmitting.value = true
// // user_id
const submitData = { const submitData = {
userId: userId.value, user_id: userId.value,
communityName: formData.communityName, community_name: formData.communityName,
address: formData.address community_address: formData.address
} }
// API // 使 apiClient axios
// const response = await axios.post('/api/community/request', submitData) const response = await apiClient.post('/api/feedback/community-apply', submitData)
// API console.log('提交成功:', response.data)
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('提交的数据:', submitData)
// //
showSuccessModal.value = true showSuccessModal.value = true
@ -145,7 +183,8 @@ export default {
showSuccessModal, showSuccessModal,
submitRequest, submitRequest,
closeSuccessModal, closeSuccessModal,
goBack goBack,
handleTouchStart
} }
} }
} }
@ -153,6 +192,15 @@ export default {
<style scoped> <style scoped>
.community-request-container { .community-request-container {
/* 添加触摸相关样式 */
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
touch-action: manipulation;
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -0,0 +1,506 @@
<template>
<div class="partner-request-container" @touchstart="handleTouchStart">
<div class="request-card">
<h2 class="page-title">合伙人申请</h2>
<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>
<input
type="tel"
id="phone"
v-model="formData.phone"
placeholder="请输入您的联系电话"
required
pattern="[0-9]{11}"
/>
</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="success-modal" v-if="showSuccessModal">
<div class="modal-content">
<div class="success-icon"></div>
<h3>申请提交成功</h3>
<p>我们将尽快审核您的合伙人申请</p>
<button @click="closeSuccessModal" 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 showSuccessModal = ref(false)
const userId = ref('')
const lastTouchEnd = ref(0)
const formData = reactive({
name: '',
phone: '',
partnerType: 'community', //
serviceArea: ''
})
onMounted(() => {
// URLuserId
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
}
const handleTouchMove = (event) => {
if (event.touches.length > 1) {
event.preventDefault()
}
}
document.addEventListener('touchend', handleTouchEnd, { passive: false })
document.addEventListener('touchmove', handleTouchMove, { passive: false })
// 便
window._partnerRequestTouchHandlers = {
handleTouchEnd,
handleTouchMove
}
})
onBeforeUnmount(() => {
//
if (window._partnerRequestTouchHandlers) {
document.removeEventListener('touchend', window._partnerRequestTouchHandlers.handleTouchEnd)
document.removeEventListener('touchmove', window._partnerRequestTouchHandlers.handleTouchMove)
delete window._partnerRequestTouchHandlers
}
})
const handleTouchStart = (event) => {
//
if (event.touches.length > 1) {
event.preventDefault()
}
}
const checkMiniProgramEnvironment = () => {
//
const ua = navigator.userAgent.toLowerCase()
const isMiniProgram = ua.indexOf('miniprogram') > -1 || window.__wxjs_environment === 'miniprogram'
console.log('Is Mini Program:', isMiniProgram)
}
const selectPartnerType = (type) => {
formData.partnerType = type
//
formData.serviceArea = ''
}
const submitRequest = async () => {
try {
if (!userId.value) {
alert('缺少用户ID无法提交申请')
return
}
isSubmitting.value = true
// user_id
const submitData = {
user_id: userId.value,
name: formData.name,
phone: formData.phone,
type: formData.partnerType,
service_target: formData.serviceArea
}
// 使 apiClient axios
const response = await apiClient.post('/api/feedback/partner-apply', submitData)
console.log('提交成功:', response.data)
//
showSuccessModal.value = true
} catch (error) {
console.error('提交失败:', error)
alert('提交失败,请稍后重试')
} finally {
isSubmitting.value = false
}
}
const closeSuccessModal = () => {
showSuccessModal.value = false
//
goBack()
}
const goBack = () => {
//
if (window.wx && window.wx.miniProgram) {
window.wx.miniProgram.navigateBack({ delta: 1 })
} else {
// 使
router.back()
}
}
return {
formData,
isSubmitting,
showSuccessModal,
submitRequest,
closeSuccessModal,
goBack,
handleTouchStart,
selectPartnerType
}
}
}
</script>
<style scoped>
.partner-request-container {
/* 添加触摸相关样式 */
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
touch-action: manipulation;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 15px;
background: linear-gradient(to bottom, #FFCC00, #FFFFFF);
}
.request-card {
width: 100%;
max-width: 90%;
background-color: #FFFFFF;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 25px 20px;
margin-top: 20px;
}
.page-title {
font-size: 22px;
font-weight: bold;
color: #333333;
text-align: center;
margin-bottom: 25px;
}
.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;
}
/* 合伙类型选择器样式 */
.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;
justify-content: space-between;
margin-top: 10px;
}
.btn-cancel,
.btn-submit,
.btn-confirm {
padding: 12px 25px;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
}
.btn-cancel {
background-color: #F5F5F5;
color: #666666;
border: 1px solid #DDDDDD;
}
.btn-submit {
background-color: #FFCC00;
color: #333333;
border: none;
}
.btn-submit:disabled {
background-color: #FFE580;
cursor: not-allowed;
}
.btn-cancel:hover {
background-color: #EEEEEE;
}
.btn-submit:hover:not(:disabled) {
background-color: #FFD633;
}
/* 成功弹窗样式 */
.success-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
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;
}
.success-icon {
width: 60px;
height: 60px;
background-color: #FFCC00;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
color: white;
margin-bottom: 20px;
}
.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;
width: 100%;
}
.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-selector {
flex-direction: column;
gap: 10px;
}
.partner-type-option {
padding: 10px 12px;
}
.option-label {
font-size: 15px;
}
}
</style>

View File

@ -1,6 +1,12 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import axios from 'axios'
import './assets/main.css' import './assets/main.css'
createApp(App).use(router).mount('#app') // 配置 axios 默认值
axios.defaults.baseURL = 'https://api-dev.beefast.co'
const app = createApp(App)
app.use(router)
app.mount('#app')

View File

@ -5,6 +5,7 @@ import UserAgreement from '../components/UserAgreement.vue'
import Dashboard from '../components/Dashboard.vue' import Dashboard from '../components/Dashboard.vue'
import HowToUse from '../components/HowToUse.vue' import HowToUse from '../components/HowToUse.vue'
import CommunityRequest from '../components/CommunityRequest.vue' import CommunityRequest from '../components/CommunityRequest.vue'
import PartnerRequest from '../components/PartnerRequest.vue'
const routes = [ const routes = [
{ {
@ -45,6 +46,14 @@ const routes = [
meta: { meta: {
title: '小区开通申请' title: '小区开通申请'
} }
},
{
path: '/partner-request',
name: 'PartnerRequest',
component: PartnerRequest,
meta: {
title: '合伙人申请'
}
} }
] ]

52
src/services/api.js Normal file
View File

@ -0,0 +1,52 @@
import axios from 'axios'
// 根据环境变量确定基础URL
const getBaseURL = () => {
const appEnv = import.meta.env.VITE_APP_ENV || 'local'
switch (appEnv) {
case 'dev':
return 'https://api-dev.beefast.co'
case 'prd':
return 'https://api.beefast.ci'
default:
return 'http://localhost:8000'
}
}
// 创建 axios 实例
const apiClient = axios.create({
baseURL: getBaseURL(),
timeout: 10000, // 请求超时时间
headers: {
'Content-Type': 'application/json'
}
})
// 请求拦截器
apiClient.interceptors.request.use(
config => {
// 在发送请求之前做些什么
console.log(`API请求到: ${config.baseURL}${config.url}`)
return config
},
error => {
// 对请求错误做些什么
return Promise.reject(error)
}
)
// 响应拦截器
apiClient.interceptors.response.use(
response => {
// 对响应数据做点什么
return response
},
error => {
// 对响应错误做点什么
console.error('API请求错误:', error)
return Promise.reject(error)
}
)
export default apiClient