Files
2026-01-07 11:00:09 +08:00

802 lines
17 KiB
Vue
Raw Permalink 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>
<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>