Files
smartDriveEEUniApp/pages-subpackage/furniture_reception/common_begin_reception.vue
2026-04-16 09:20:13 +08:00

902 lines
22 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>
<view class="reception-form-wrapper">
<scroll-view class="reception-form" scroll-y enable-back-to-top>
<view class="form-card">
<view class="form-item">
<text class="form-item__label">服务次数</text>
<view class="form-item__text">
<text>{{ formData.recordingCount || 0 }}</text>
</view>
</view>
<view class="form-item">
<text class="form-item__label">客户姓名</text>
<input
class="form-item__input"
v-model="formData.customerName"
placeholder="请输入客户姓名"
placeholder-style="color: #9ca3af"
/>
</view>
<view class="form-item">
<text class="form-item__label">客户电话</text>
<input
class="form-item__input"
v-model="formData.contact"
@blur="onContactBlur"
type="number"
placeholder="请输入客户电话"
placeholder-style="color: #9ca3af"
/>
</view>
<view class="form-item">
<text class="form-item__label">客户来源</text>
<view class="customer-source-select-wrapper">
<view
ref="customerSourceSelect"
class="customer-source-select"
@click.stop="toggleCustomerSourceDropdown"
>
<text
:class="formData.customerSource ? 'form-item__picker-text' : 'form-item__picker-placeholder'"
>
{{ formData.customerSource || "请选择客户来源" }}
</text>
<uni-icons type="bottom" size="16" color="#9ca3af"></uni-icons>
</view>
<view
ref="customerSourceDropdown"
v-if="showCustomerSourceDropdown"
class="customer-source-select-dropdown"
@click.stop
>
<view
class="customer-source-select-dropdown__item"
v-for="option in customerSourceOptions"
:key="option"
@click="selectCustomerSource(option)"
>
<text :class="{'active': option === formData.customerSource}">
{{ option }}
</text>
</view>
</view>
</view>
</view>
<view class="form-item">
<text class="form-item__label">门店名称</text>
<input
class="form-item__input"
v-model="formData.dealershipName"
placeholder="请输入门店名称"
placeholder-style="color: #9ca3af"
/>
</view>
<view class="form-item">
<text class="form-item__label">性别</text>
<radio-group
class="gender-radio-group"
@change="onGenderChange">
<label
class="gender-radio"
v-for="(option, index) in genderOptions"
:key="index">
<radio
:value="option"
:checked="formData.gender === option"
color="#2563eb" />
<text class="gender-radio__text">{{ option }}</text>
</label>
</radio-group>
</view>
<view class="form-item">
<text class="form-item__label">年龄</text>
<input
class="form-item__input"
v-model="formData.age"
type="number"
placeholder="请输入年龄"
placeholder-style="color: #9ca3af"
/>
</view>
<view class="form-item">
<text class="form-item__label">销售姓名</text>
<input
class="form-item__input"
v-model="formData.salesName"
placeholder="请输入销售姓名"
placeholder-style="color: #9ca3af"
/>
</view>
<view class="form-item">
<text class="form-item__label">销售电话</text>
<input
class="form-item__input"
v-model="formData.salesPhone"
type="number"
placeholder="请输入销售电话"
placeholder-style="color: #9ca3af"
/>
</view>
<view class="form-item">
<text class="form-item__label">住址</text>
<input
class="form-item__input"
v-model="formData.remarks"
placeholder="请输入详细住址"
placeholder-style="color: #9ca3af"
/>
</view>
</view>
</scroll-view>
<view class="form-actions form-actions--triple">
<view class="form-btn form-btn--cancel" @click="handleCancel">
<view class="form-btn__icon-circle">
<uni-icons type="close" size="16" color="#007AFF"></uni-icons>
</view>
<text class="form-btn__text">取消</text>
</view>
<view class="form-btn form-btn--save" @click="handleSave('save')">
<view class="form-btn__icon-circle">
<uni-icons type="checkmarkempty" size="16" color="#007AFF"></uni-icons>
</view>
<text class="form-btn__text">保存</text>
</view>
<view class="form-btn form-btn--start" @click="handleSave('start')">
<view class="form-btn__icon-circle">
<uni-icons type="checkmarkempty" size="16" color="#007AFF"></uni-icons>
</view>
<text class="form-btn__text">开始接待</text>
</view>
</view>
<!-- 遮罩层点击关闭下拉框 -->
<view
v-if="showCustomerSourceDropdown"
class="customer-source-select-mask"
@click="closeCustomerSourceDropdown"
></view>
</view>
</template>
<script>
import { getApiUrl } from "@/common/config.js";
export default {
name: 'ReceptionForm',
props: {
// 表单初始数据
initialData: {
type: Object,
default: () => ({})
}
},
data() {
return {
genderOptions: ["男", "女"],
customerSourceOptions: ["自然到店", "网络媒体", "其他类型"],
showCustomerSourceDropdown: false,
isFetchingContact: false,
formData: {
id: "",
customerName: "",
contact: "",
customerSource: "",
gender: "女",
age: "",
dealershipId: "",
dealershipName: "",
salesId: "",
salesName: "",
salesPhone: "",
recordingCount: 0,
intendedModel: "",
infoCard: "",
remark: "",
detailedAddress: "",
contactCount: 0
}
}
},
watch: {
initialData: {
handler(newVal) {
if (newVal && Object.keys(newVal).length > 0) {
this.formData = { ...this.formData, ...newVal };
}
},
immediate: true,
deep: true
}
},
mounted() {
// 加载当前登录用户信息,填充销售姓名和电话
this.loadCurrentUserInfo();
// 监听窗口大小变化,重新定位下拉框
uni.onWindowResize(() => {
if (this.showCustomerSourceDropdown) {
this.updateDropdownPosition();
}
});
},
methods: {
/**
* 加载当前登录用户信息,填充销售姓名和电话
*/
loadCurrentUserInfo() {
try {
// 从本地存储获取登录响应信息
const loginResponse = uni.getStorageSync('backend-login-response') || {};
// 填充销售姓名
if (loginResponse.userName) {
this.formData.salesName = loginResponse.userName;
}
// 填充销售电话:如果电话为空,则使用姓名作为电话
if (loginResponse.phone) {
this.formData.salesPhone = loginResponse.phone;
} else if (loginResponse.userName) {
// 如果电话为空,使用姓名作为电话
this.formData.salesPhone = loginResponse.userName;
}
// 如果有 userId也可以填充到 salesId
if (loginResponse.userId) {
this.formData.salesId = String(loginResponse.userId);
}
} catch (e) {
console.error('加载当前登录用户信息失败:', e);
}
},
onGenderChange(e) {
this.formData.gender = e.detail.value;
},
toggleCustomerSourceDropdown() {
this.showCustomerSourceDropdown = !this.showCustomerSourceDropdown;
if (this.showCustomerSourceDropdown) {
this.$nextTick(() => {
this.updateDropdownPosition();
});
}
},
updateDropdownPosition() {
const selectEl = this.$refs.customerSourceSelect;
if (selectEl) {
const rect = selectEl.getBoundingClientRect();
const dropdownEl = this.$refs.customerSourceDropdown;
if (dropdownEl) {
dropdownEl.style.top = (rect.bottom + 8) + 'px';
dropdownEl.style.left = rect.left + 'px';
dropdownEl.style.width = rect.width + 'px';
}
}
},
selectCustomerSource(option) {
this.formData.customerSource = option;
this.showCustomerSourceDropdown = false;
},
closeCustomerSourceDropdown() {
this.showCustomerSourceDropdown = false;
},
onContactBlur() {
const contact = this.formData.contact?.trim();
if (!contact) {
return;
}
if (!this.isValidPhoneNumber(contact)) {
uni.showToast({
title: "请输入有效的手机号",
icon: "none"
});
return;
}
this.fetchCustomerByContact();
},
isValidPhoneNumber(phone) {
const normalizedPhone = phone?.trim();
if (!normalizedPhone) {
return false;
}
return /^1[3-9]\d{9}$/.test(normalizedPhone);
},
async fetchCustomerByContact() {
const contact = this.formData.contact?.trim();
if (!contact || this.isFetchingContact) {
return;
}
if (!this.isValidPhoneNumber(contact)) {
return;
}
try {
this.isFetchingContact = true;
// 统一使用 getApiUrl根据 env.js 配置自动切换环境
const url = `${getApiUrl('/api/customerManagement/getByContact')}?contact=${encodeURIComponent(contact)}`;
// 获取认证信息
let tenantId = '';
let token = '';
try {
tenantId = uni.getStorageSync('backend-tenant-id') || '';
token = uni.getStorageSync('backend-token') || '';
} catch (e) {
console.error('获取认证信息失败:', e);
}
// 构建请求头
const headers = {};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
if (tenantId) {
headers['X-Tenant-Id'] = tenantId;
}
const res = await uni.request({
url: url,
method: 'GET',
header: headers,
timeout: 30000 // 增加超时时间到30秒
});
const list = res.data?.data;
if (res.statusCode === 200 && res.data?.success && Array.isArray(list) && list.length) {
const customer = list[0];
const retrievedId = customer.id || customer.customerId || "";
const resolvedSalesPhone = customer.salesPhone
|| customer.salesMobile
|| customer.salesTel
|| this.formData.salesPhone
|| "";
this.formData = {
...this.formData,
id: retrievedId ? String(retrievedId) : this.formData.id,
customerName: customer.customerName || this.formData.customerName,
customerSource: customer.customerSource || this.formData.customerSource,
dealershipId: customer.dealershipId ? String(customer.dealershipId) : this.formData.dealershipId,
dealershipName: customer.dealershipName || this.formData.dealershipName,
salesId: customer.salesId ? String(customer.salesId) : this.formData.salesId,
salesName: customer.salesName || this.formData.salesName,
salesPhone: String(resolvedSalesPhone),
recordingCount: Number(customer.recordingCount) || 0,
intendedModel: customer.intendedModel || this.formData.intendedModel,
infoCard: customer.infoCard || this.formData.infoCard,
remark: customer.remark || this.formData.remark,
detailedAddress: customer.detailedAddress || this.formData.detailedAddress,
contactCount: customer.contactCount ?? this.formData.contactCount
};
uni.showToast({
title: "已填充客户信息",
icon: "none"
});
}
// 未获取到数据时,不提示任何信息
} catch (error) {
// 查询失败时,也不提示任何信息
console.error("查询客户信息失败:", error);
} finally {
this.isFetchingContact = false;
}
},
handleCancel() {
// 重置表单
this.formData = {
id: "",
customerName: "",
contact: "",
customerSource: "",
gender: "女",
age: "",
dealershipId: "",
dealershipName: "",
salesId: "",
salesName: "",
salesPhone: "",
recordingCount: 0,
intendedModel: "",
infoCard: "",
remark: "",
detailedAddress: "",
contactCount: 0
};
// 重置后重新填充当前登录用户信息
this.loadCurrentUserInfo();
// 触发取消事件
this.$emit('cancel');
uni.showToast({
title: "已取消",
icon: "none"
});
},
async handleSave(action = 'save') {
// 表单验证
// 客户电话格式验证(如果填写了电话,则验证格式)
const trimmedContact = this.formData.contact?.trim();
if (trimmedContact && !this.isValidPhoneNumber(trimmedContact)) {
uni.showToast({
title: "请输入有效的客户电话",
icon: "none"
});
return;
}
const trimmedSalesPhone = this.formData.salesPhone?.trim();
if (trimmedSalesPhone && !this.isValidPhoneNumber(trimmedSalesPhone)) {
uni.showToast({
title: "请输入有效的销售电话",
icon: "none"
});
return;
}
// 构建请求参数
const normalizedCustomerId = this.formData.id?.toString().trim() || "";
const params = {
id: normalizedCustomerId,
customerName: this.formData.customerName?.trim(),
contact: trimmedContact,
customerSource: this.formData.customerSource?.trim() || "",
dealershipId: String(this.formData.dealershipId || '').trim() || "",
dealershipName: this.formData.dealershipName?.trim() || "",
salesId: String(this.formData.salesId || '').trim() || "",
salesName: this.formData.salesName?.trim() || "",
recordingCount: Number(this.formData.recordingCount) || 0,
intendedModel: this.formData.intendedModel?.trim() || "",
infoCard: this.formData.infoCard?.trim() || "",
remark: this.formData.remark?.trim() || "",
detailedAddress: this.formData.detailedAddress?.trim() || "",
salesPhone: trimmedSalesPhone || "",
contactCount: Number(this.formData.contactCount) || 0,
operationType: action
};
try {
uni.showLoading({
title: "保存中..."
});
// 获取认证信息
let tenantId = '';
let token = '';
let scenario = '';
try {
const loginResponse = uni.getStorageSync('backend-login-response') || {};
tenantId = uni.getStorageSync('backend-tenant-id') || '';
token = uni.getStorageSync('backend-token') || '';
// 场景值来自登录返回,兜底读取历史缓存字段
scenario = (loginResponse.scenario || uni.getStorageSync('backend-scenario') || '').toString().trim();
} catch (e) {
console.error('获取认证信息失败:', e);
}
// 构建请求头
const headers = {
'Content-Type': 'application/json'
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
if (tenantId) {
headers['X-Tenant-Id'] = tenantId;
}
if (scenario) {
headers['X-Scenario'] = scenario;
params.scenario = scenario;
}
const res = await uni.request({
url: getApiUrl('/api/customerManagement/add'),
method: "POST",
data: params,
header: headers,
timeout: 30000 // 增加超时时间到30秒
});
uni.hideLoading();
if (res.statusCode === 200 && res.data && res.data.success) {
uni.showToast({
title: "保存成功",
icon: "success"
});
// 保存成功后重置表单
this.handleCancel();
// 触发保存成功事件
this.$emit('save-success', { action, data: params });
} else {
uni.showToast({
title: res.data?.message || "保存失败",
icon: "none"
});
// 触发保存失败事件
this.$emit('save-error', { action, error: res.data?.message || "保存失败" });
}
} catch (error) {
uni.hideLoading();
console.error("保存失败:", error);
uni.showToast({
title: "保存失败,请重试",
icon: "none"
});
// 触发保存失败事件
this.$emit('save-error', { action, error: error.message || "保存失败,请重试" });
}
}
}
}
</script>
<style>
.reception-form-wrapper {
/* 使用绝对定位从tab页底部开始高度计算100vh - 导航栏(88rpx) - tab页(72rpx) - 底部导航栏(96rpx) */
position: absolute;
top: 160rpx; /* 导航栏(88rpx) + tab页(72rpx) = 160rpx */
left: 0;
right: 0;
bottom: 96rpx; /* 底部导航栏高度 */
background-color: #F5F5F5;
box-sizing: border-box;
width: 100%;
z-index: 0; /* 确保在标签页下方,但可以显示内容 */
display: flex;
flex-direction: column;
overflow: hidden; /* 防止内容溢出 */
}
.reception-form {
flex: 1;
width: 100%;
padding: 24rpx 16rpx;
box-sizing: border-box;
background-color: #F5F5F5;
overflow-y: auto;
/* 确保可以滚动 */
-webkit-overflow-scrolling: touch;
}
.form-card {
width: 100%;
box-sizing: border-box;
background-color: #ffffff;
border-radius: 16rpx;
padding: 40rpx 24rpx 32rpx 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
margin-bottom: 24rpx;
overflow: visible; /* 确保内容不被裁剪 */
}
.form-card .form-item:last-child {
margin-bottom: 0;
}
/* 第一个表单项特殊处理,确保顶部空间 */
.form-card .form-item:first-child {
padding-top: 24rpx;
margin-top: 16rpx;
}
.form-item {
display: flex !important;
align-items: flex-start;
margin-bottom: 32rpx;
min-height: 88rpx;
padding-top: 8rpx;
box-sizing: border-box;
width: 100%;
flex-wrap: nowrap !important;
overflow: hidden;
flex-shrink: 0;
justify-content: center;
flex-direction: row !important;
padding-left: 0;
}
/* 对于包含 textarea 的表单项label 顶部对齐 */
.form-item--textarea {
align-items: flex-start;
}
.gender-radio-group {
flex: 1;
flex-shrink: 1;
display: flex;
gap: 32rpx;
flex-wrap: nowrap;
align-items: center;
min-width: 0;
overflow: hidden;
height: 88rpx;
justify-content: flex-start;
}
.gender-radio {
display: flex;
align-items: center;
gap: 12rpx;
flex-shrink: 0;
height: 88rpx;
}
.gender-radio__text {
color: #374151;
font-size: 28rpx;
white-space: nowrap;
}
.form-item__label {
font-size: 28rpx;
color: #333;
font-weight: 500;
width: 140rpx;
flex-shrink: 0;
flex-grow: 0;
margin-right: 24rpx;
margin-left: 0;
padding: 12rpx 0 12rpx 16rpx;
white-space: nowrap;
box-sizing: border-box;
min-height: 88rpx;
line-height: 1.4;
text-align: left;
display: flex;
align-items: flex-start;
justify-content: flex-start;
/* 确保标签可见且不被遮挡 */
}
.form-item--half {
width: 50%;
}
.form-item__input {
flex: 1 !important;
flex-shrink: 1 !important;
flex-grow: 1 !important;
min-width: 0 !important;
max-width: none !important;
width: auto !important;
height: 88rpx;
line-height: 88rpx;
background-color: #f9fafb;
border-radius: 12rpx;
padding: 0 24rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
border: 2rpx solid #e5e7eb;
transition: all 0.2s ease;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.form-item__input:focus {
border-color: #007AFF;
background-color: #FFFFFF;
box-shadow: 0 0 0 4rpx rgba(0, 122, 255, 0.1);
}
.form-item__text {
flex: 1;
flex-shrink: 1;
min-width: 0;
height: 88rpx;
background-color: #f9fafb;
border-radius: 12rpx;
padding: 0 24rpx;
display: flex;
align-items: center;
justify-content: flex-start;
box-sizing: border-box;
overflow: hidden;
}
.form-item__text text {
font-size: 28rpx;
color: #333;
}
.form-actions {
display: flex;
gap: 16rpx;
padding: 24rpx 16rpx;
padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
background-color: #F5F5F5;
box-sizing: border-box;
flex-shrink: 0;
width: 100%;
border-top: 1px solid #E5E7EB;
}
.form-actions--triple {
gap: 16rpx;
}
.form-btn {
flex: 1;
height: 88rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
transition: all 0.2s ease;
background-color: #f8f9fa;
border: 1px solid #e5e7eb;
box-sizing: border-box;
}
.form-btn--cancel:active,
.form-btn--save:active,
.form-btn--start:active {
background-color: #f0f0f0;
border-color: #d1d5db;
opacity: 0.9;
}
.form-btn__icon-circle {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 122, 255, 0.08);
flex-shrink: 0;
}
.form-btn__text {
font-size: 30rpx;
font-weight: 500;
color: #007AFF;
}
.form-btn--cancel .form-btn__text,
.form-btn--save .form-btn__text,
.form-btn--start .form-btn__text {
color: #007AFF;
}
/* 客户来源下拉框样式 */
.customer-source-select-wrapper {
position: relative;
flex: 1;
flex-shrink: 1;
min-width: 0;
display: flex;
align-items: center;
overflow: hidden;
height: 88rpx;
}
.customer-source-select {
width: 100%;
min-width: 0;
height: 88rpx;
background-color: #f9fafb;
border-radius: 12rpx;
padding: 0 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
border: 1px solid transparent;
overflow: hidden;
}
.customer-source-select uni-icons {
flex-shrink: 0;
margin-left: 12rpx;
}
.customer-source-select-dropdown {
position: fixed;
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 12rpx 30rpx rgba(15, 23, 42, 0.1);
z-index: 9999;
padding: 12rpx 0;
box-sizing: border-box;
max-height: 400rpx;
overflow-y: auto;
min-width: 200rpx;
}
.customer-source-select-dropdown__item {
padding: 20rpx 32rpx;
}
.customer-source-select-dropdown__item text {
font-size: 28rpx;
color: #374151;
}
.customer-source-select-dropdown__item text.active {
color: #2A68FF;
font-weight: 500;
}
.customer-source-select-dropdown__item:active {
background-color: #f5f7fb;
}
.customer-source-select-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: transparent;
z-index: 9998;
}
.form-item__picker-text {
font-size: 28rpx;
color: #333;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.form-item__picker-placeholder {
font-size: 28rpx;
color: #9ca3af;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 小屏幕适配 */
@media (max-width: 750rpx) {
.form-item__label {
width: 120rpx;
font-size: 26rpx;
margin-right: 16rpx;
margin-left: 0;
padding: 10rpx 0 10rpx 12rpx;
min-height: 80rpx;
line-height: 1.4;
align-items: flex-start;
}
.form-item {
min-height: 80rpx;
padding-top: 6rpx;
}
.form-item__input,
.form-item__text,
.customer-source-select-wrapper,
.gender-radio-group,
.gender-radio {
height: 80rpx;
}
.form-item__input,
.form-item__text,
.customer-source-select-wrapper,
.gender-radio-group {
flex-shrink: 1;
}
.form-item__input,
.customer-source-select {
font-size: 26rpx;
padding: 0 16rpx;
}
.form-item__text {
padding: 0 16rpx;
}
.form-item__text text {
font-size: 26rpx;
}
.gender-radio-group {
gap: 24rpx;
}
}
</style>