Feat:管家入驻(小程序)

This commit is contained in:
LinCyJang
2025-10-10 23:42:37 +08:00
parent 0dd7b2ed2d
commit 9244c3215d
5 changed files with 795 additions and 0 deletions
+21
View File
@@ -64,3 +64,24 @@ export function removeApply() {
export function getHotelInfo(params) {
return request.get("merchantApply/info/hotel", params, {login: true})
}
/**
* 旅居管家入驻申请详情
*/
export function getTravelInfo(params) {
return request.get("merchantApply/info/travel", params, {login: true})
}
/**
* 提交旅居管家入驻申请
*/
export function saveTravel(params) {
return request.post("/merchantApply/travel/submit", params, {login: true})
}
/**
* 旅居管家申请草稿保存
*/
export function saveTravelDraft(params) {
return request.post("/merchantApply/travel/saveDraft", params, {login: true})
}
+7
View File
@@ -550,6 +550,13 @@
"style": {
"navigationBarTitleText": "采购批发商入驻"
}
},
{
"path": "views/travelResidence",
"style": {
"navigationBarTitleText": "旅居管家入驻",
"navigationBarBackgroundColor": "#FFFFFF"
}
}
]
},
+16
View File
@@ -32,6 +32,12 @@
mode="widthFix"
@click="goto('wholesaler')"
/>
<image
:src="images.wholesalerImage"
class="img-item"
mode="widthFix"
@click="toTravelResidence"
/>
</view>
</template>
@@ -68,6 +74,16 @@ export default {
})
},
methods: {
/**
* 跳转旅游 residences
*/
toTravelResidence() {
this.$yrouter.push('/pkg_join/views/travelResidence?partnerId=' + this.partnerId)
},
/**
* 跳转
* @param {string} target - 目标页面
*/
goto(target) {
switch (target) {
case 'goods_supplier':
+721
View File
@@ -0,0 +1,721 @@
<template>
<view class="container">
<view class="v12-pa-2 reason-box" v-if="form.reviewStatus === 2">
驳回原因: {{ form.reviewMsg }}
</view>
<view class="form-item">
<view class="title">
<text class="required">* </text>
名称:
</view>
<view class="input">
<u-textarea
border="none"
:disabled="form.reviewStatus === 0"
placeholder="请输入名称"
:placeholder-style="inputPlaceholderStyle"
v-model="form.name"
/>
</view>
</view>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start">
<view>
<view class="title">
<text class="required">* </text>
主图:
</view>
<view class="input">
<u-upload
:fileList="coverImg"
:disabled="coverImg.length > 0 || form.reviewStatus === 0"
:maxCount="1"
count="1"
name="coverImg"
multiple
@afterRead="afterRead($event, 'coverImg')"
@delete="deletePic($event, 'coverImg')"
></u-upload>
</view>
</view>
<view class="v12-ml-2">
<view class="title">
<text class="required">* </text>
头像:
</view>
<view class="input">
<u-upload
:fileList="avatarImg"
:disabled="avatarImg.length > 0 || form.reviewStatus === 0"
:maxCount="1"
count="1"
name="avatarImg"
multiple
@afterRead="afterRead($event, 'avatarImg')"
@delete="deletePic($event, 'avatarImg')"
></u-upload>
</view>
</view>
</view>
<view class="form-item v12-mt-2">
<view class="title">
<text class="required">* </text>
轮播图:
</view>
<view class="input">
<u-upload
:fileList="bannerImg"
:disabled="bannerImg.length > 8 || form.reviewStatus === 0"
:maxCount="9"
count="9"
name="bannerImg"
multiple
@afterRead="afterRead($event, 'bannerImg')"
@delete="deletePic($event, 'bannerImg')"
></u-upload>
</view>
</view>
<view class="form-item v12-mt-2">
<view class="title">
<text class="required">* </text>
视频:
</view>
<view class="input">
<u-upload
accept="video/*"
multiple
:max-count="9"
:fileList="videos"
:disabled="videos.length > 8 || form.reviewStatus === 0"
:maxCount="9"
count="9"
name="bannerImg"
@afterRead="afterRead($event, 'videos', 'video')"
@delete="deletePic($event, 'videos')"
></u-upload>
</view>
</view>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
公司名称
</view>
<view class="v12-ml-2">
<input
type="text"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.reviewStatus === 0"
placeholder="请输入公司名称"
v-model="form.companyName"
/>
</view>
</view>
<u-divider></u-divider>
<view @click="showTimePicker" class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
营业时间
</view>
<view class="v12-ml-2">
<view color="#999999" :text="`${form.serviceTime || '起始时间'} - ${form.serviceEndTime || '结束时间'}`">
<text :style="{'color': form.serviceTime? '#333' : '#999999'}">{{form.serviceTime || '起始时间'}}</text> - <text :style="{'color': form.serviceEndTime? '#333' : '#999999'}">{{form.serviceEndTime || '结束时间'}}</text>
</view>
<tpf-time-range
startTime="00:00"
endTime="23:59"
:disabled="form.reviewStatus === 0"
:startDefaultTime="form.serviceTime"
:endDefaultTime="form.serviceEndTime"
ref="TimePickerPopupRef"
@timeRange="timeRange"
>
</tpf-time-range>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
省市区
</view>
<view class="v12-ml-2">
<CitySelect
ref="cityselect"
:disabled="form.reviewStatus === 0"
:defaultValue="form.area || ''"
:items="district"
@callback="resultCity"
/>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
详细地址
</view>
<view class="v12-ml-2">
<input
:placeholder-style="inputPlaceholderStyle"
:disabled="form.reviewStatus === 0"
type="text"
placeholder="请输入详细地址"
v-model="form.address"
/>
</view>
</view>
<u-divider></u-divider>
<view @click="chooLocation" class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
地理定位
</view>
<view class="v12-ml-2">
<u--text v-if="form.position" :text="form.position"></u--text>
<u--text v-else text="请选择" color="#c0c4cc"></u--text>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
商家电话
</view>
<view class="v12-ml-2">
<u-input
type="text"
:placeholder-style="inputPlaceholderStyle"
:disabled="form.reviewStatus === 0"
placeholder="请输入商家电话"
v-model="form.phone"
border="none"
>
<template slot="suffix" v-if="form.reviewStatus !== 0">
<u-code
ref="uCode"
@change="codeChange"
seconds="30"
changeText="X秒重新获取"
></u-code>
<u-button
@tap="getCode"
:text="tips"
type="success"
color="#4A97FC"
></u-button>
</template>
</u-input>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
填写验证码
</view>
<view class="v12-ml-2">
<input
type="text"
:placeholder-style="inputPlaceholderStyle"
placeholder="请输入验证码"
v-model="form.captcha"
/>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2 v12-d-flex v12-justify-start v12-align-center">
<view class="title">
<text class="required">* </text>
商家微信
</view>
<view class="v12-ml-2">
<input
type="text"
:placeholder-style="inputPlaceholderStyle"
placeholder="请输入微信号"
v-model="form.wechat"
/>
</view>
</view>
<u-divider></u-divider>
<view class="form-item v12-mt-2">
<view class="title">
<text class="required">* </text>
文化:
</view>
<view class="input">
<u-upload
:fileList="cultureImg"
:disabled="form.reviewStatus === 0 || cultureImg.length > 8"
:maxCount="9"
count="9"
name="cultureImg"
multiple
@afterRead="afterRead($event, 'cultureImg')"
@delete="deletePic($event, 'cultureImg')"
></u-upload>
</view>
</view>
<view class="form-item v12-mt-2">
<view class="title">
<text class="required">* </text>
资质:
</view>
<view class="input">
<u-upload
:fileList="qualificationImg"
:disabled="form.reviewStatus === 0 || qualificationImg.length > 8"
:maxCount="9"
count="9"
name="qualificationImg"
multiple
@afterRead="afterRead($event, 'qualificationImg')"
@delete="deletePic($event, 'qualificationImg')"
></u-upload>
</view>
</view>
<view class="v12-primary v12-white-text submit-btn" @click="submitForm" :style="{'background-color': form.reviewStatus === 0 ? '#FA979F ' : '#4A97FC'}">
{{ form.reviewStatus === 0 ? '审核中' : form.reviewStatus === 2 ? '已驳回' : '提交审核' }}
</view>
</view>
</template>
<script>
import CitySelect from '@/components/CitySelect'
import {getCity, registerVerify} from '@/api/user'
import {
chooseImage,
uploadVideo,
uploadImage
} from '@/utils'
import {
getTravelInfo,
saveTravel,
saveTravelDraft
} from '@/api/join'
export default {
components: {
CitySelect
},
data() {
return {
tips: '获取验证码',
inputPlaceholderStyle: {
fontSize: '32rpx'
},
coverImg: [],
avatarImg: [],
// 轮播图
bannerImg: [],
videos: [],
district: [],
// 文化图片
cultureImg: [],
// 资质图片
qualificationImg: [],
partnerId: 0,
captcha: '',
form: {
/**
*
travelInfo object 旅居管家基本信息
|- name string 名称
|- cover string 主图
|- avatar string 头像
|- companyName string 公司名称
|- video string 视频
|- banner string 轮播图(逗号分隔字符串)
|- serviceTime string 营业开始时间
|- serviceEndTime string 营业结束时间
|- provinceId int 省份ID
|- provinceName string 省份名称
|- cityId int 城市ID
|- cityName string 城市名称
|- areaId int 区域ID
|- areaName string 区域名称
|- address string 详细地址
|- longitude number 经度
|- latitude number 纬度
|- contact string 联系人
|- phone string 联系方式
|- wechat string 联系微信
|- culture string 文化(逗号分隔字符串)
|- qualification string 资质(逗号分隔字符串)
*
*/
captcha: '',
name: '',
cover: '',
avatar: '',
companyName: '',
video: '',
banner: '',
serviceTime: '00:00',
serviceEndTime: '23:59',
provinceId: 0,
provinceName: '',
cityId: 0,
cityName: '',
areaId: 0,
areaName: '',
address: '',
longitude: 0,
latitude: 0,
contact: '',
phone: '',
wechat: '',
culture: '',
qualification: '',
reviewMsg: ''
}
}
},
onLoad(opts) {
this.partnerId = opts.partnerId
this.queryCity()
},
onShow() {
this.initInfo()
},
onHide() {
this.saveDraft()
},
methods: {
/**
* 字符串转换为数组
* @param {string} str 字符串
* @param {string} split 分隔符
* @returns {array} 数组
*/
stringToMapArr(str, split = ',') {
return str.split(split).map(item => ({url: item}))
},
/**
* 保存草稿
*/
saveDraft() {
this.form.cover = this.coverImg.map(item => item.url).join(',')
this.form.avatar = this.avatarImg.map(item => item.url).join(',')
this.form.banner = this.bannerImg.map(item => item.url).join(',')
this.form.video = this.videos.map(item => item.url).join(',')
this.form.culture = this.cultureImg.map(item => item.url).join(',')
this.form.qualification = this.qualificationImg.map(item => item.url).join(',')
const params = {
travelInfo: {...this.form},
partnerId: this.partnerId,
captcha: this.form.captcha
}
saveTravelDraft(params).then(res => {
if (res.status === 200) {
uni.$u.toast('草稿保存成功')
}
}).catch(err => {
uni.$u.toast(err.msg)
})
},
/**
* 初始化信息
*/
initInfo() {
getTravelInfo().then(res => {
const {travelInfo} = res.data
if (res.status === 200) {
this.form = {...this.form, ...travelInfo}
this.coverImg = this.stringToMapArr(travelInfo.cover)
this.avatarImg = this.stringToMapArr(travelInfo.avatar)
this.bannerImg = this.stringToMapArr(travelInfo.banner)
this.videos = this.stringToMapArr(travelInfo.video)
this.cultureImg = this.stringToMapArr(travelInfo.culture)
this.qualificationImg = this.stringToMapArr(travelInfo.qualification)
this.form.reviewStatus = res.data.reviewStatus
this.form.reviewMsg = res.data.reviewMsg
this.captcha = travelInfo.captcha
}
}).catch(err => {
uni.$u.toast(err.msg)
})
},
/**
* 提交表单
*/
submitForm() {
this.form.cover = this.coverImg.map(item => item.url).join(',')
this.form.cover = this.coverImg.map(item => item.url).join(',')
this.form.avatar = this.avatarImg.map(item => item.url).join(',')
this.form.banner = this.bannerImg.map(item => item.url).join(',')
this.form.video = this.videos.map(item => item.url).join(',')
this.form.culture = this.cultureImg.map(item => item.url).join(',')
this.form.qualification = this.qualificationImg.map(item => item.url).join(',')
if (!this.form.name) {
uni.$u.toast('请输入名称')
return
}
if (!this.form.cover) {
uni.$u.toast('请上传主图')
return
}
if (!this.form.avatar) {
uni.$u.toast('请上传头像')
return
}
if (!this.form.companyName) {
uni.$u.toast('请输入公司名称')
return
}
if (!this.form.video) {
uni.$u.toast('请上传视频')
return
}
if (!this.form.banner) {
uni.$u.toast('请上传轮播图')
return
}
if (!this.form.video) {
uni.$u.toast('请上传视频')
return
}
if (!this.form.companyName) {
uni.$u.toast('请输入公司名称')
return
}
// 请选择营业时间
if (!this.form.serviceTime) {
uni.$u.toast('请选择营业开始时间')
return
}
if (!this.form.serviceEndTime) {
uni.$u.toast('请选择营业结束时间')
return
}
// 请选择省市区
if (!this.form.provinceId) {
uni.$u.toast('请选择省份')
return
}
if (!this.form.cityId) {
uni.$u.toast('请选择城市')
return
}
if (!this.form.areaId) {
uni.$u.toast('请选择区域')
return
}
if (!this.form.address) {
uni.$u.toast('请输入详细地址')
return
}
// 请选择物理位置
if (!this.form.longitude) {
uni.$u.toast('请选择物理位置')
return
}
if (!this.form.latitude) {
uni.$u.toast('请选择物理位置')
return
}
if (!this.form.culture) {
uni.$u.toast('请上传文化')
return
}
if (!this.form.qualification) {
uni.$u.toast('请上传资质')
return
}
if (!this.form.phone) {
uni.$u.toast('请输入手机号')
return
}
if (!this.form.wechat) {
uni.$u.toast('请输入联系微信')
return
}
const params = {
travelInfo: {...this.form},
partnerId: this.partnerId,
captcha: this.form.captcha
}
saveTravel(params).then(res => {
if (res.status === 200) {
uni.showToast({
title: '您的入驻申请已提交成功,请耐心等待审核',
icon: 'none',
success: () => {
this.initInfo();
this.show = false;
}
})
}
}).catch(err => {
uni.$u.toast(err.msg)
})
},
/**
* 获取验证码
*/
getCode() {
// if (this.reviewing) return
if (!this.form.phone) {
uni.$u.toast('请输入手机号')
return
}
if (this.form.phone.length !== 11) {
uni.$u.toast('请输入11位手机号')
return
}
if (this.$refs.uCode.canGetCode) {
uni.showLoading({
title: '正在获取验证码'
})
registerVerify({
phone: this.form.phone,
type: 'merchantApply'
}).then(() => {
uni.hideLoading()
// 这里此提示会被this.start()方法中的提示覆盖
uni.$u.toast('验证码已发送')
// 通知验证码组件内部开始倒计时
this.$refs.uCode.start()
}).catch(err => {
uni.hideLoading()
uni.$u.toast(err.msg)
})
} else {
uni.$u.toast('倒计时结束后再发送')
}
},
/**
* 验证码按钮回调
*/
codeChange(text) {
this.tips = text
},
/**
* 选择位置
*/
chooLocation() {
if (this.form.reviewStatus === 0) {
uni.$u.toast('您的入驻申请正在审核中,暂不能选择位置')
return
}
uni.chooseLocation({
success: (res) => {
this.form.address = res.address
this.form.longitude = this.$forceToDecimal(res.longitude, 6)
this.form.latitude = this.$forceToDecimal(res.latitude, 6)
this.form.position = this.$forceToDecimal(res.longitude, 6) + ',' + this.$forceToDecimal(res.latitude, 6)
}
})
},
/**
* 选择城市回调
* @param {object} e - 事件对象
*/
resultCity(values) {
const address = {
province: values.province.name || '',
city: values.city.name || '',
district: values.district.name || '',
provinceId: values.province.id,
cityId: values.city.id,
areaId: values.district.id
}
this.form.area = `${address.province || ''} ${address.city || ''} ${address.district || ''}`
this.form.provinceId = address.provinceId
this.form.cityId = address.cityId
this.form.areaId = address.areaId
},
/**
* 选择城市
*/
queryCity() {
getCity().then(({data}) => {
this.district = data
})
},
/**
* 显示时间选择器
*/
showTimePicker() {
this.$refs.TimePickerPopupRef.open()
},
/**
* 选择时间范围
* @param {array} e - 时间范围数组,包含开始时间和结束时间
*/
timeRange(e) {
this.form.serviceTime = e[0]
this.form.serviceEndTime = e[1]
this.$forceUpdate()
},
/**
* 选择图片
* @param {string} imgField - 图片字段名
*/
chooseImage(imgField) {
chooseImage(img => {
this.form[imgField] = img;
})
},
/**
* 上传图片或视频
* @param {object} e - 事件对象
* @param {string} imgField - 图片字段名
* @param {string} type - 类型('video'或'image'
*/
afterRead(e, imgField, type) {
const uploadFunc = type == 'video' ? uploadVideo : uploadImage;
if(e.file.length > 0) {
e.file.forEach(item => {
uploadFunc(item.url, img => {
this[imgField].push({url: img});
})
})
}
},
/**
* 删除图片或视频
* @param {object} e - 事件对象
* @param {string} imgField - 图片字段名
*/
deletePic(e, imgField) {
this[imgField].splice(e.index, 1);
}
}
}
</script>
<style scoped>
.container {
padding: 20rpx;
background: #fff;
}
.title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.required {
color: #C52733;
}
.input{
margin-top: 20rpx;
}
.submit-btn{
width: 100%;
height: 100rpx;
text-align: center;
line-height: 100rpx;
}
.reason-box{
background: rgba(255,160,167,0.39);
border-radius: 8rpx;
color: #C52733;
font-size: 30rpx;
margin-bottom: 20rpx;
}
</style>
<style>
.input .u-textarea{
background-color: #F3F3F3 !important;
}
</style>
+30
View File
@@ -1199,3 +1199,33 @@ export function uploadImage(imgPath, callback) {
}
});
}
export function uploadVideo(videoPath, callback) {
uni.showLoading({
title: "视频上传中",
mask: true
});
uni.uploadFile({
url: `${VUE_APP_API_URL}/api/upload`,
filePath: videoPath,
header: {
Authorization: "Bearer " + store.getters.token
},
name: "file",
success: res => {
if (callback) {
callback(JSON.parse(res.data).link)
}
},
fail: err => {
uni.showToast({
title: "上传视频失败" + err,
icon: "none",
duration: 2000
});
},
complete: res => {
uni.hideLoading()
}
});
}