802 lines
17 KiB
Vue
802 lines
17 KiB
Vue
<template>
|
||
<view class="page-container">
|
||
<!-- 验证中提示 -->
|
||
<view v-if="validating" class="validating-container">
|
||
<view class="validating-content">
|
||
<uni-icons type="spinner-cycle" size="40" color="#6366f1"></uni-icons>
|
||
<text class="validating-text">正在验证...</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 错误提示页面 -->
|
||
<view v-if="!validating && !tokenValid && errorMessage" class="error-container">
|
||
<view class="error-content">
|
||
<view class="error-icon-wrapper">
|
||
<uni-icons type="closeempty" size="80" color="#ef4444"></uni-icons>
|
||
</view>
|
||
<text class="error-title">验证失败</text>
|
||
<text class="error-message">{{ errorMessage }}</text>
|
||
<button class="error-button" @click="goBack">返回</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 创建方式选择 -->
|
||
<view class="method-selector" v-if="tokenValid && !validating && !errorMessage">
|
||
<view class="selector-item" :class="{'active': createMethod === 'manual'}" @click="createMethod = 'manual'">
|
||
<view class="selector-dot" v-if="createMethod === 'manual'"></view>
|
||
<text class="selector-text">手动创建</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 表单内容 -->
|
||
<view class="form-container" v-if="tokenValid && !validating && !errorMessage">
|
||
<view class="form-header">
|
||
<text class="form-title">填写渠道信息</text>
|
||
<text class="form-subtitle">请完善下方信息以手动添加新的分销渠道。</text>
|
||
</view>
|
||
|
||
<view class="form-body">
|
||
<!-- 渠道名称 -->
|
||
<view class="form-item">
|
||
<view class="form-label">
|
||
<uni-icons type="shop" size="18" color="#6366f1"></uni-icons>
|
||
<text class="label-text">渠道名称</text>
|
||
<text class="required-mark">必填</text>
|
||
</view>
|
||
<view class="input-wrapper">
|
||
<input
|
||
class="form-input"
|
||
v-model="form.channelName"
|
||
placeholder="请输入渠道名称"
|
||
maxlength="50"
|
||
:focus="focusChannelName"
|
||
@blur="focusChannelName = false"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 联系电话 -->
|
||
<view class="form-item">
|
||
<view class="form-label">
|
||
<uni-icons type="phone" size="18" color="#6366f1"></uni-icons>
|
||
<text class="label-text">手机号</text>
|
||
<text class="required-mark">必填</text>
|
||
</view>
|
||
<view class="input-wrapper">
|
||
<input
|
||
class="form-input"
|
||
v-model="form.contactPhone"
|
||
placeholder="请输入手机号"
|
||
type="number"
|
||
maxlength="11"
|
||
:focus="focusContactPhone"
|
||
@blur="focusContactPhone = false"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 微信号 -->
|
||
<view class="form-item">
|
||
<view class="form-label">
|
||
<uni-icons type="chat" size="18" color="#6366f1"></uni-icons>
|
||
<text class="label-text">微信号</text>
|
||
</view>
|
||
<view class="input-wrapper">
|
||
<input
|
||
class="form-input"
|
||
v-model="form.wechatId"
|
||
placeholder="请输入微信号"
|
||
maxlength="50"
|
||
:focus="focusWechatId"
|
||
@blur="focusWechatId = false"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 备注 -->
|
||
<view class="form-item">
|
||
<view class="form-label">
|
||
<uni-icons type="info" size="18" color="#6366f1"></uni-icons>
|
||
<text class="label-text">备注</text>
|
||
</view>
|
||
<view class="input-wrapper">
|
||
<textarea
|
||
class="form-textarea"
|
||
v-model="form.remark"
|
||
placeholder="请输入备注信息 (选填)"
|
||
maxlength="200"
|
||
:auto-height="true"
|
||
:focus="focusRemark"
|
||
@blur="focusRemark = false"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部按钮 -->
|
||
<view class="button-container" v-if="tokenValid && !validating && !errorMessage">
|
||
<button class="cancel-button" @click="cancel">取消</button>
|
||
<button class="submit-button" :class="{'loading': isSubmitting}" @click="submit" :disabled="isSubmitting || !tokenValid">
|
||
<uni-icons v-if="!isSubmitting" type="checkmarkempty" size="20" color="#fff"></uni-icons>
|
||
<text class="button-text">{{ isSubmitting ? '提交中...' : '创建' }}</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import config from '@/config';
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
token: '',
|
||
createMethod: 'manual',
|
||
form: {
|
||
channelName: '',
|
||
contactPhone: '',
|
||
wechatId: '',
|
||
remark: ''
|
||
},
|
||
focusChannelName: false,
|
||
focusContactPhone: false,
|
||
focusWechatId: false,
|
||
focusRemark: false,
|
||
isSubmitting: false,
|
||
tokenValid: false,
|
||
validating: true,
|
||
errorMessage: ''
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
// 获取参数
|
||
let scene = options.scene;
|
||
let token = options.token;
|
||
|
||
console.log('页面参数:', { scene, token });
|
||
|
||
// 小程序端解析 scene 参数
|
||
// #ifdef MP-WEIXIN
|
||
if (scene != undefined) {
|
||
// 先对 scene 进行 URL 解码(处理 %2C 等编码字符)
|
||
try {
|
||
scene = decodeURIComponent(scene);
|
||
} catch (e) {
|
||
console.warn('scene 解码失败,使用原始值:', e);
|
||
}
|
||
|
||
// scene 中可能包含 token,或者直接就是 token
|
||
// 如果 scene 包含逗号,可能需要分割(根据实际业务需求调整)
|
||
if (scene.includes(',')) {
|
||
// 如果 scene 包含逗号,取第一部分作为 token
|
||
const parts = scene.split(',');
|
||
this.token = parts[0] || '';
|
||
} else {
|
||
// 如果不包含逗号,scene 本身就是 token
|
||
this.token = scene;
|
||
}
|
||
} else if (token != undefined) {
|
||
// 兼容直接传 token 参数
|
||
this.token = token;
|
||
} else {
|
||
// 没有参数,显示错误
|
||
this.token = '';
|
||
}
|
||
// #endif
|
||
|
||
// H5 端兼容处理
|
||
// #ifdef H5
|
||
if (token != undefined) {
|
||
// H5 端优先使用 token 参数
|
||
this.token = token;
|
||
} else if (scene != undefined) {
|
||
// H5 也可能有 scene 参数(从分享链接等)
|
||
// 先对 scene 进行 URL 解码
|
||
try {
|
||
scene = decodeURIComponent(scene);
|
||
} catch (e) {
|
||
console.warn('scene 解码失败,使用原始值:', e);
|
||
}
|
||
|
||
if (scene.includes(',')) {
|
||
const parts = scene.split(',');
|
||
this.token = parts[0] || '';
|
||
} else {
|
||
this.token = scene;
|
||
}
|
||
} else {
|
||
// 没有参数,显示错误
|
||
this.token = '';
|
||
}
|
||
// #endif
|
||
|
||
console.log('解析结果:', { token: this.token });
|
||
|
||
if (!this.token) {
|
||
this.validating = false;
|
||
this.tokenValid = false;
|
||
this.errorMessage = '缺少token参数,请重新扫码';
|
||
return;
|
||
}
|
||
|
||
uni.setNavigationBarTitle({
|
||
title: '新增渠道'
|
||
});
|
||
|
||
// 验证token有效性
|
||
this.validateToken();
|
||
|
||
// H5和小程序兼容处理
|
||
// #ifdef H5
|
||
// H5端回车键提交
|
||
document.onkeydown = (event) => {
|
||
const e = event || window.event;
|
||
if (e && e.keyCode === 13) {
|
||
this.submit();
|
||
}
|
||
};
|
||
// #endif
|
||
},
|
||
methods: {
|
||
// 验证token有效性
|
||
validateToken() {
|
||
this.validating = true;
|
||
uni.showLoading({
|
||
title: '验证中...',
|
||
mask: true
|
||
});
|
||
|
||
uni.request({
|
||
method: 'GET',
|
||
url: config.apiUrl2 + '/v1/frontend/distribution/channel/register',
|
||
data: {
|
||
token: this.token
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded'
|
||
},
|
||
success: (response) => {
|
||
uni.hideLoading();
|
||
this.validating = false;
|
||
|
||
if (response.data.code === 10000 || response.data.code === 200) {
|
||
this.tokenValid = true;
|
||
this.errorMessage = '';
|
||
// token验证成功,可以填写表单
|
||
} else {
|
||
this.tokenValid = false;
|
||
this.errorMessage = response.data.message || 'token无效或已过期,请重新扫码';
|
||
}
|
||
},
|
||
fail: (error) => {
|
||
uni.hideLoading();
|
||
this.validating = false;
|
||
this.tokenValid = false;
|
||
console.error('验证token失败:', error);
|
||
this.errorMessage = '网络错误,请检查网络连接后重试';
|
||
}
|
||
});
|
||
},
|
||
|
||
// 表单验证
|
||
validateForm() {
|
||
if (!this.form.channelName || !this.form.channelName.trim()) {
|
||
this.focusChannelName = true;
|
||
uni.showToast({
|
||
title: '请输入渠道名称',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return false;
|
||
}
|
||
|
||
// 手机号必填验证
|
||
if (!this.form.contactPhone || !this.form.contactPhone.trim()) {
|
||
this.focusContactPhone = true;
|
||
uni.showToast({
|
||
title: '请输入手机号',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return false;
|
||
}
|
||
|
||
// 验证手机号格式
|
||
const phoneReg = /^1[3-9]\d{9}$/;
|
||
if (!phoneReg.test(this.form.contactPhone.trim())) {
|
||
this.focusContactPhone = true;
|
||
uni.showToast({
|
||
title: '请输入正确的手机号',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
},
|
||
|
||
// 提交表单
|
||
submit() {
|
||
if (this.isSubmitting) {
|
||
return;
|
||
}
|
||
|
||
if (!this.validateForm()) {
|
||
return;
|
||
}
|
||
|
||
if (!this.tokenValid) {
|
||
uni.showToast({
|
||
title: 'token无效,请重新扫码',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
this.isSubmitting = true;
|
||
|
||
const data = {
|
||
token: this.token,
|
||
name: this.form.channelName.trim(),
|
||
phone: this.form.contactPhone.trim(),
|
||
wechatId: this.form.wechatId ? this.form.wechatId.trim() : '',
|
||
remarks: this.form.remark ? this.form.remark.trim() : ''
|
||
};
|
||
|
||
uni.request({
|
||
method: 'POST',
|
||
url: config.apiUrl2 + '/v1/frontend/distribution/channel/register',
|
||
data: data,
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded'
|
||
},
|
||
success: (response) => {
|
||
this.isSubmitting = false;
|
||
|
||
if (response.data.code === 10000 || response.data.code === 200) {
|
||
// 获取返回的companyId
|
||
const responseData = response.data.data || {};
|
||
const companyId = responseData.companyId || '';
|
||
|
||
console.log('渠道创建成功,返回数据:', responseData);
|
||
console.log('获取到的companyId:', companyId);
|
||
|
||
uni.showToast({
|
||
title: '提交成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
});
|
||
|
||
setTimeout(() => {
|
||
// 如果有companyId,跳转到渠道登录页
|
||
if (companyId) {
|
||
uni.redirectTo({
|
||
url: `/pages/channel/login?companyId=${companyId}`
|
||
});
|
||
} else {
|
||
// 如果没有companyId,返回上一页
|
||
uni.navigateBack();
|
||
}
|
||
}, 1500);
|
||
} else {
|
||
// 显示后端返回的错误信息
|
||
const errorMsg = response.data.message || response.data.msg || response.data.error || '提交失败,请重试';
|
||
uni.showToast({
|
||
title: errorMsg,
|
||
icon: 'none',
|
||
duration: 3000
|
||
});
|
||
}
|
||
},
|
||
fail: (error) => {
|
||
this.isSubmitting = false;
|
||
console.error('提交失败:', error);
|
||
uni.showToast({
|
||
title: '网络错误,请重试',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// 取消
|
||
cancel() {
|
||
uni.navigateBack();
|
||
},
|
||
|
||
// 返回
|
||
goBack() {
|
||
uni.navigateBack();
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page-container {
|
||
min-height: 100vh;
|
||
background-color: #f5f5f5;
|
||
padding-bottom: 160rpx;
|
||
}
|
||
|
||
/* 创建方式选择器 */
|
||
.method-selector {
|
||
padding: 30rpx;
|
||
background-color: #fff;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.selector-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx;
|
||
background-color: #f9fafb;
|
||
border-radius: 12rpx;
|
||
position: relative;
|
||
}
|
||
|
||
.selector-item.active {
|
||
background-color: #f0f4ff;
|
||
}
|
||
|
||
.selector-dot {
|
||
width: 24rpx;
|
||
height: 24rpx;
|
||
background-color: #6366f1;
|
||
border-radius: 50%;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.selector-text {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 表单容器 */
|
||
.form-container {
|
||
background-color: #fff;
|
||
margin: 20rpx;
|
||
border-radius: 16rpx;
|
||
padding: 40rpx 30rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.form-header {
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.form-title {
|
||
display: block;
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.form-subtitle {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.form-item {
|
||
margin-bottom: 40rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.form-label {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.label-text {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
margin-left: 8rpx;
|
||
}
|
||
|
||
.required-mark {
|
||
font-size: 24rpx;
|
||
color: #ef4444;
|
||
margin-left: auto;
|
||
}
|
||
|
||
.input-wrapper {
|
||
position: relative;
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
padding: 0 24rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
background-color: #f9fafb;
|
||
border: 2rpx solid #e5e7eb;
|
||
border-radius: 12rpx;
|
||
box-sizing: border-box;
|
||
transition: all 0.3s ease;
|
||
|
||
/* #ifdef H5 */
|
||
&:focus {
|
||
outline: none;
|
||
border-color: #6366f1;
|
||
background-color: #fff;
|
||
box-shadow: 0 0 0 4rpx rgba(99, 102, 241, 0.1);
|
||
}
|
||
/* #endif */
|
||
|
||
/* #ifdef MP-WEIXIN */
|
||
&:focus {
|
||
border-color: #6366f1;
|
||
background-color: #fff;
|
||
}
|
||
/* #endif */
|
||
}
|
||
|
||
.form-input::placeholder {
|
||
color: #999;
|
||
}
|
||
|
||
.form-textarea {
|
||
width: 100%;
|
||
min-height: 160rpx;
|
||
padding: 24rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
background-color: #f9fafb;
|
||
border: 2rpx solid #e5e7eb;
|
||
border-radius: 12rpx;
|
||
box-sizing: border-box;
|
||
line-height: 1.6;
|
||
transition: all 0.3s ease;
|
||
|
||
/* #ifdef H5 */
|
||
&:focus {
|
||
outline: none;
|
||
border-color: #6366f1;
|
||
background-color: #fff;
|
||
box-shadow: 0 0 0 4rpx rgba(99, 102, 241, 0.1);
|
||
}
|
||
/* #endif */
|
||
|
||
/* #ifdef MP-WEIXIN */
|
||
&:focus {
|
||
border-color: #6366f1;
|
||
background-color: #fff;
|
||
}
|
||
/* #endif */
|
||
}
|
||
|
||
.form-textarea::placeholder {
|
||
color: #999;
|
||
}
|
||
|
||
/* 底部按钮容器 */
|
||
.button-container {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
padding: 20rpx;
|
||
background-color: #fff;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
display: flex;
|
||
gap: 20rpx;
|
||
/* #ifdef H5 */
|
||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||
/* #endif */
|
||
z-index: 100;
|
||
}
|
||
|
||
.cancel-button {
|
||
flex: 1;
|
||
height: 88rpx;
|
||
background-color: #f5f5f5;
|
||
border: none;
|
||
border-radius: 12rpx;
|
||
color: #666;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
|
||
/* #ifdef H5 */
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover {
|
||
background-color: #e5e5e5;
|
||
}
|
||
/* #endif */
|
||
}
|
||
|
||
.submit-button {
|
||
flex: 2;
|
||
height: 88rpx;
|
||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||
border: none;
|
||
border-radius: 12rpx;
|
||
color: #fff;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(99, 102, 241, 0.3);
|
||
|
||
/* #ifdef H5 */
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover:not(:disabled) {
|
||
transform: translateY(-2rpx);
|
||
box-shadow: 0 12rpx 32rpx rgba(99, 102, 241, 0.4);
|
||
}
|
||
|
||
&:active:not(:disabled) {
|
||
transform: translateY(0);
|
||
}
|
||
/* #endif */
|
||
}
|
||
|
||
.submit-button.loading {
|
||
opacity: 0.7;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.submit-button:disabled {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.button-text {
|
||
margin-left: 8rpx;
|
||
}
|
||
|
||
/* H5适配 */
|
||
/* #ifdef H5 */
|
||
.page-container {
|
||
padding-bottom: 180rpx;
|
||
}
|
||
|
||
.button-container {
|
||
padding: 30rpx 40rpx;
|
||
}
|
||
/* #endif */
|
||
|
||
/* 小程序适配 */
|
||
/* #ifdef MP-WEIXIN */
|
||
.button-container {
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
}
|
||
/* #endif */
|
||
|
||
/* 验证中容器 */
|
||
.validating-container {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 9999;
|
||
}
|
||
|
||
.validating-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 24rpx;
|
||
}
|
||
|
||
.validating-text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
/* 错误容器 */
|
||
.error-container {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 9999;
|
||
padding: 40rpx;
|
||
}
|
||
|
||
.error-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
max-width: 600rpx;
|
||
width: 100%;
|
||
}
|
||
|
||
.error-icon-wrapper {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
border-radius: 50%;
|
||
background-color: #fef2f2;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 40rpx;
|
||
border: 4rpx solid #fee2e2;
|
||
}
|
||
|
||
.error-title {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.error-message {
|
||
font-size: 28rpx;
|
||
color: #6b7280;
|
||
line-height: 1.6;
|
||
margin-bottom: 60rpx;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.error-button {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||
border: none;
|
||
border-radius: 12rpx;
|
||
color: #fff;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(99, 102, 241, 0.3);
|
||
|
||
/* #ifdef H5 */
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover {
|
||
transform: translateY(-2rpx);
|
||
box-shadow: 0 12rpx 32rpx rgba(99, 102, 241, 0.4);
|
||
}
|
||
|
||
&:active {
|
||
transform: translateY(0);
|
||
}
|
||
/* #endif */
|
||
}
|
||
|
||
/* 响应式适配 */
|
||
@media (max-width: 375px) {
|
||
.form-container {
|
||
padding: 30rpx 20rpx;
|
||
}
|
||
|
||
.form-item {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
}
|
||
</style>
|
||
|