OfficeSystem/pages/customer/add/index.vue
2025-11-13 14:52:54 +08:00

409 lines
11 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="add-customer-page">
<!-- 自定义导航栏 -->
<CustomerFormNavbar title="客户信息" @cancel="handleCancel" />
<!-- 内容区域 -->
<scroll-view class="content-scroll" scroll-y>
<view class="scroll-content">
<!-- 客户信息部分 -->
<CustomerBasicInfo
:form-data="formData"
:customer-type-options="customerTypeOptions"
:intent-level-options="intentLevelOptions"
:customer-status-options="customerStatusOptions"
:lock-customer-type="true"
@update:form-data="formData = $event"
@open-picker="handleOpenPicker"
/>
<!-- 其他信息部分 -->
<CustomerOtherInfo
:form-data="formData"
@update:form-data="formData = $event"
@open-picker="handleOpenPicker"
/>
</view>
</scroll-view>
<!-- 保存按钮 -->
<view class="save-button-wrapper">
<button class="save-button" @click="handleSave" :disabled="saving">保存</button>
</view>
<!-- 选择器组件 -->
<CustomerPickers
:show-customer-type-picker="showCustomerTypePicker"
:show-source-picker="showSourcePicker"
:show-intent-picker="showIntentPicker"
:show-intent-level-picker="showIntentLevelPicker"
:show-customer-status-picker="showCustomerStatusPicker"
:show-work-wechat-picker="showWorkWechatPicker"
:show-next-follow-time-picker="showNextFollowTimePicker"
:show-follow-user-picker="showFollowUserPicker"
:customer-type-options="customerTypeOptions"
:source-options="sourceOptions"
:intent-options="intentOptions"
:intent-level-options="intentLevelOptions"
:customer-status-options="customerStatusOptions"
:work-wechat-options="workWechatOptions"
:follow-user-options="followUserOptions"
:address-list="addressList"
:region-loading="regionLoading"
:form-data="formData"
ref="pickersRef"
@update:form-data="formData = $event"
@close-picker="handleClosePicker"
@region-confirm="onRegionConfirm"
@region-change="onRegionChange"
/>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { createCustomer, getUserListAll } from '@/api';
import { useUserStore } from '@/store/user';
import { useCustomerForm } from '@/composables/useCustomerForm';
import CustomerFormNavbar from '@/components/customer/customer-form/CustomerFormNavbar.vue';
import CustomerBasicInfo from '@/components/customer/customer-form/CustomerBasicInfo.vue';
import CustomerOtherInfo from '@/components/customer/customer-form/CustomerOtherInfo.vue';
import CustomerPickers from '@/components/customer/customer-form/CustomerPickers.vue';
// 使用共享逻辑
const {
customerTypeOptions,
sourceOptions,
intentOptions,
intentLevelOptions,
customerStatusOptions,
workWechatOptions,
addressList,
regionLoading,
loadDictData,
loadWechatList,
loadRegionTree,
handleRegionChange,
handleRegionConfirm,
openRegionPicker
} = useCustomerForm();
// 表单数据
const formData = ref({
customerType: '2',
name: '',
mobile: '',
wechat: '',
source: '',
intents: [],
intentLevel: '',
customerStatus: '',
region: '',
regionIds: [],
workWechat: '',
workWechatId: null,
remark: '',
concern: '',
pain: '',
attention: '',
demand: '',
nextFollowTime: '',
followId: null,
followName: ''
});
// 显示状态
const saving = ref(false);
const showCustomerTypePicker = ref(false);
const showSourcePicker = ref(false);
const showIntentPicker = ref(false);
const showIntentLevelPicker = ref(false);
const showCustomerStatusPicker = ref(false);
const showWorkWechatPicker = ref(false);
const showNextFollowTimePicker = ref(false);
const showFollowUserPicker = ref(false);
// 用户列表
const followUserOptions = ref([]);
const pickersRef = ref(null);
// 打开选择器
const handleOpenPicker = (pickerType) => {
switch (pickerType) {
case 'customerType':
showCustomerTypePicker.value = true;
break;
case 'source':
showSourcePicker.value = true;
break;
case 'intent':
showIntentPicker.value = true;
break;
case 'intentLevel':
showIntentLevelPicker.value = true;
break;
case 'customerStatus':
showCustomerStatusPicker.value = true;
break;
case 'region':
openRegionPicker(formData.value, pickersRef.value);
break;
case 'workWechat':
showWorkWechatPicker.value = true;
break;
case 'nextFollowTime':
showNextFollowTimePicker.value = true;
break;
case 'followUser':
showFollowUserPicker.value = true;
break;
}
};
// 关闭选择器
const handleClosePicker = (pickerType) => {
switch (pickerType) {
case 'customerType':
showCustomerTypePicker.value = false;
break;
case 'source':
showSourcePicker.value = false;
break;
case 'intent':
showIntentPicker.value = false;
break;
case 'intentLevel':
showIntentLevelPicker.value = false;
break;
case 'customerStatus':
showCustomerStatusPicker.value = false;
break;
case 'workWechat':
showWorkWechatPicker.value = false;
break;
case 'nextFollowTime':
showNextFollowTimePicker.value = false;
break;
case 'followUser':
showFollowUserPicker.value = false;
break;
}
};
// uv-picker 的 change 事件处理(实现三级联动)
const onRegionChange = (e) => {
handleRegionChange(e, pickersRef.value);
};
// uv-picker 的 confirm 事件处理
const onRegionConfirm = (e) => {
handleRegionConfirm(e, formData.value);
};
// 取消
const handleCancel = () => {
uni.navigateBack();
};
// 加载用户列表
const loadUserList = async () => {
try {
const res = await getUserListAll();
if (res && res.data && Array.isArray(res.data)) {
followUserOptions.value = res.data;
} else if (res && Array.isArray(res)) {
followUserOptions.value = res;
} else {
followUserOptions.value = [];
}
} catch (error) {
console.error('加载用户列表失败:', error);
followUserOptions.value = [];
}
};
// 组件挂载时加载数据并设置默认值
onMounted(async () => {
await Promise.all([
loadRegionTree(),
loadDictData(),
loadWechatList(),
loadUserList()
]);
// 设置默认值:意向=电动车按label强度=中按value来源=抖音按label状态=意向按value
try {
// 客户意向(多选,组件使用的是 label
const defaultIntentLabel = '电动车';
if (!formData.value.intents || formData.value.intents.length === 0) {
const existsIntent = intentOptions.value.some(opt => opt.label === defaultIntentLabel);
if (existsIntent) {
formData.value.intents = [defaultIntentLabel];
}
}
// 意向强度(组件使用的是 value
const defaultIntentLevelLabel = '中';
if (!formData.value.intentLevel) {
const level = intentLevelOptions.value.find(opt => opt.label === defaultIntentLevelLabel);
if (level) {
formData.value.intentLevel = level.value;
}
}
// 客户来源(组件使用的是 label
const defaultSourceLabel = '抖音';
if (!formData.value.source) {
const existsSource = sourceOptions.value.some(opt => opt.label === defaultSourceLabel);
if (existsSource) {
formData.value.source = defaultSourceLabel;
}
}
// 客户状态(组件使用的是 value
const defaultStatusLabel = '高意向';
if (!formData.value.customerStatus) {
const status = customerStatusOptions.value.find(opt => opt.label === defaultStatusLabel);
if (status) {
formData.value.customerStatus = status.value;
}
}
} catch (e) {
console.warn('设置默认字典值失败:', e);
}
});
// 保存
const handleSave = async () => {
if (!formData.value.name || formData.value.name.trim() === '') {
uni.showToast({
title: '请输入客户名称',
icon: 'none'
});
return;
}
if (!formData.value.mobile || formData.value.mobile.trim() === '') {
uni.showToast({
title: '请输入联系电话',
icon: 'none'
});
return;
}
saving.value = true;
try {
const now = new Date();
const formatDateTime = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
const intentsArray = Array.isArray(formData.value.intents) ? formData.value.intents : [];
const regionIdsArray = formData.value.regionIds || [];
// 使用选中的跟进人ID如果没有选择则使用当前用户ID
const userStore = useUserStore();
const defaultUserId = userStore.userInfo?.id || userStore.userInfo?.userId || '1';
const followId = formData.value.followId || defaultUserId;
const submitData = {
id: null,
code: null,
name: formData.value.name.trim(),
status: null,
intentLevel: null,
mobile: formData.value.mobile.trim(),
wechat: formData.value.wechat.trim() || null,
source: formData.value.source || null,
intents: intentsArray,
followId: followId,
remark: formData.value.remark.trim() || null,
type: '2',
workWechatId: formData.value.workWechatId || null,
regionIds: regionIdsArray,
attention: formData.value.attention.trim() || null,
concern: formData.value.concern.trim() || null,
demand: formData.value.demand.trim() || null,
pain: formData.value.pain.trim() || null,
follow: {
followTime: formatDateTime(now),
nextFollowTime: formData.value.nextFollowTime || null,
customerIntentLevel: formData.value.intentLevel || null,
customerStatus: formData.value.customerStatus || null
},
nextFollowTime: formData.value.nextFollowTime || null,
customerIntentLevel: formData.value.intentLevel || null,
customerStatus: formData.value.customerStatus || null
};
await createCustomer(submitData);
uni.showToast({
title: '创建成功',
icon: 'success'
});
} catch (error) {
console.error('创建客户失败:', error);
uni.showToast({
title: error.message || '创建失败,请重试',
icon: 'none'
});
} finally {
saving.value = false;
}
};
</script>
<style lang="scss" scoped>
.add-customer-page {
display: flex;
flex-direction: column;
background-color: #f5f5f5;
}
.content-scroll {
flex: 1;
}
.scroll-content {
padding: 16px;
padding-bottom: 80px;
}
.save-button-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 12px 16px;
padding-bottom: calc(12px + env(safe-area-inset-bottom));
background-color: #fff;
border-top: 1px solid #eee;
z-index: 100;
}
.save-button {
width: 100%;
height: 44px;
background-color: #1976d2;
color: #fff;
font-size: 16px;
font-weight: 600;
border-radius: 6px;
border: none;
&:disabled {
background-color: #ccc;
opacity: 0.6;
}
}
</style>