diff --git a/api/apiRoute.js b/api/apiRoute.js index 1c17ad9..49f3ed2 100644 --- a/api/apiRoute.js +++ b/api/apiRoute.js @@ -326,8 +326,12 @@ export default { async xy_orderTableAdd(data = {}) { return await http.post('/xy/orderTable/add', data); }, - +// 获取通话记录 async listCallUp(data={}) { - return await http.get('/member/list_call_up', data); + return await http.get('/per_list_call_up', data); + }, + // 更新通话记录 + async updateCallUp(data={}) { + return await http.post('/per_update_call_up', data); }, } \ No newline at end of file diff --git a/api/market.js b/api/market.js index b9dba6c..1453c92 100644 --- a/api/market.js +++ b/api/market.js @@ -83,14 +83,6 @@ export default { }) }, - //拨打电话记录列表 - listCallUp(data={}) { - let url = '/member/list_call_up' - return http.get(url, data).then(res => { - return res; - }) - }, - //公海-领取客户 getSales(data = {}) { diff --git a/common/axios.js b/common/axios.js index 4f07913..f391540 100644 --- a/common/axios.js +++ b/common/axios.js @@ -135,6 +135,9 @@ export default { }); console.log('请求配置:', interceptedConfig); + console.log('请求URL:', interceptedConfig.url); + console.log('请求方法:', interceptedConfig.method); + console.log('请求数据:', interceptedConfig.data); uni.request({ ...interceptedConfig, @@ -166,7 +169,7 @@ export default { } }); }); - }, 300), + }, 100), // 封装请求方法 post(url, data = {}) { diff --git a/common/util.js b/common/util.js index 9295a69..1ae1fc6 100644 --- a/common/util.js +++ b/common/util.js @@ -224,25 +224,57 @@ function loginOut() { * @returns {Promise} */ async function getDict(dictKey) { + console.log(`util.getDict - 开始获取字典: ${dictKey}`); + const cacheKey = `dict_${dictKey}`; const cache = uni.getStorageSync(cacheKey); const now = Date.now(); + + // 检查缓存是否有效 if (cache && cache.data && cache.expire > now) { + console.log(`util.getDict - 使用缓存数据: ${dictKey}, 数据条数: ${cache.data.length}`); return cache.data; } + + console.log(`util.getDict - 缓存无效,请求接口: ${dictKey}`); + // 缓存无效,请求接口 - const res = await marketApi.common_Dictionary({ key: dictKey }); - if (res && res.code === 1) { - // 处理接口返回的数据,确保返回格式一致 - // 接口返回的是 [{name: "抖音", value: "1", sort: 0, memo: ""}, ...] - const formattedData = Array.isArray(res.data) ? res.data : []; + try { + console.log(`util.getDict - 调用API: common_Dictionary, key=${dictKey}`); - uni.setStorageSync(cacheKey, { - data: formattedData, - expire: now + 3600 * 1000 + // 添加超时处理 + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error(`字典加载超时: ${dictKey}`)), 3000); }); - return formattedData; - } else { + + // 实际API请求 + const apiPromise = marketApi.common_Dictionary({ key: dictKey }); + + // 使用Promise.race来实现超时控制 + const res = await Promise.race([apiPromise, timeoutPromise]); + + console.log(`util.getDict - API响应:`, res); + + if (res && res.code === 1) { + // 处理接口返回的数据,确保返回格式一致 + // 接口返回的是 [{name: "抖音", value: "1", sort: 0, memo: ""}, ...] + const formattedData = Array.isArray(res.data) ? res.data : []; + + console.log(`util.getDict - 保存缓存: ${dictKey}, 数据条数: ${formattedData.length}`); + uni.setStorageSync(cacheKey, { + data: formattedData, + expire: now + 3600 * 1000 + }); + console.log(`util.getDict - 字典获取成功: ${dictKey}`); + return formattedData; + } else { + console.error(`util.getDict - API请求失败: ${dictKey}`, res); + // 返回空数组而不是抛出异常,避免阻塞流程 + return []; + } + } catch (error) { + console.error(`util.getDict - 异常: ${dictKey}`, error); + // 返回空数组,避免阻塞流程 return []; } } diff --git a/components/AQ/AQTabber.vue b/components/AQ/AQTabber.vue index 2886369..337fa1e 100644 --- a/components/AQ/AQTabber.vue +++ b/components/AQ/AQTabber.vue @@ -142,7 +142,7 @@ }, { text: "数据", - urlPath: '/pages/market/data/index', //自定义页面跳转路径 + urlPath: '/pages/market/data/statistics', //自定义页面跳转路径 iconPath: util.img('/uniapp_src/static/images/tabbar/timetable.png'), selectedIconPath: util.img('/uniapp_src/static/images/tabbar/timetables.png'), }, diff --git a/pages/market/clue/clue_info.vue b/pages/market/clue/clue_info.vue index 6c41db7..57e0ee8 100644 --- a/pages/market/clue/clue_info.vue +++ b/pages/market/clue/clue_info.vue @@ -10,17 +10,17 @@ - {{ clientInfo.customerResource.name }} + {{ safeGet(clientInfo, 'customerResource.name', '未知客户') }} - {{ $util.formatToDateTime((clientInfo.customerResource.updated_at || ''),'m-d H:i') }} 跟进 + {{ $util.formatToDateTime((safeGet(clientInfo, 'customerResource.updated_at', '')),'m-d H:i') }} 跟进 - - - + @@ -68,23 +68,23 @@ 来源渠道 - {{clientInfo.customerResource.source_channel_name}} + {{safeGet(clientInfo, 'customerResource.source_channel_name', '未知渠道')}} 来源 - {{clientInfo.customerResource.source_name}} + {{safeGet(clientInfo, 'customerResource.source_name', '未知来源')}} 顾问 - {{clientInfo.customerResource.consultant_name}} + {{safeGet(clientInfo, 'customerResource.consultant_name', '未知顾问')}} 学生姓名 - {{clientInfo.customerResource.name}} + {{safeGet(clientInfo, 'customerResource.name', '未知学生')}} 性别 - {{clientInfo.customerResource.gender_name}} + {{safeGet(clientInfo, 'customerResource.gender_name', '未知性别')}} @@ -92,11 +92,11 @@ 已成交次数 - {{clientInfo.customerResource.cj_count || 0}}次 + {{safeGet(clientInfo, 'customerResource.cj_count', 0)}}次 体验课程 - {{clientInfo.customerResource.trial_class_count}}次 + {{safeGet(clientInfo, 'customerResource.trial_class_count', 0)}}次 @@ -120,6 +120,10 @@ + + + + - 呼叫对象:{{v.student_name}} - 呼叫号码:{{v.student_phone}} + 呼叫时间:{{v.created_at}} + 呼叫备注:{{v.remarks}} @@ -267,6 +271,21 @@ 修改记录 订单列表 + + + + + + + + @@ -297,51 +316,144 @@ //当前登录的员工信息 userInfo:{}, + + // 备注相关 + remark_content: '', } }, onLoad(options) { + console.log('onLoad - 接收到参数:', options); + + // 检查options是否存在且包含resource_sharing_id + if (!options || !options.resource_sharing_id) { + console.error('onLoad - 缺少必要参数resource_sharing_id'); + uni.showToast({ + title: '缺少必要参数', + icon: 'none' + }); + + // 延迟返回上一页 + setTimeout(() => { + uni.navigateBack(); + }, 1500); + return; + } + this.resource_sharing_id = options.resource_sharing_id//共享资源表id + console.log('onLoad - 设置 resource_sharing_id:', this.resource_sharing_id); }, onShow(){ + console.log('onShow - 开始初始化'); this.init() }, methods: { async init(){ - await this.getInfo()//客户详情 - this.getUserInfo()//获取当前登录的员工信息 - this.getListCallUp()//获取通话记录 + console.log('init - 开始初始化流程'); + + // 预加载常用字典数据 + try { + console.log('init - 开始预加载字典数据'); + const dictPromises = [ + this.$util.getDict('SourceChannel'), // 来源渠道 + this.$util.getDict('source'), // 来源 + this.$util.getDict('preliminarycustomerintention'), // 客户初步意向度 + this.$util.getDict('kh_status'), // 客户状态 + ]; + + // 使用Promise.all并行加载字典,但设置超时处理 + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('字典加载超时')), 5000) + ); + + await Promise.race([ + Promise.all(dictPromises), + timeoutPromise + ]).catch(err => { + console.warn('字典加载异常或超时,继续执行流程:', err); + }); + + console.log('init - 字典数据预加载完成或已超时'); + } catch (error) { + console.warn('init - 字典数据预加载失败,继续执行流程:', error); + } + + // 恢复为串行请求处理,确保数据依赖关系正确 + try { + // 先获取客户详情,因为其他操作可能依赖于客户详情 + console.log('init - 开始获取客户详情'); + await this.getInfo(); + console.log('init - 客户详情获取完成'); + + // 获取员工信息和通话记录可以并行 + console.log('init - 开始获取员工信息和通话记录'); + await Promise.all([ + this.getUserInfo(), + this.getListCallUp() + ]); + console.log('init - 员工信息和通话记录获取完成'); + } catch (error) { + console.error('init - 数据加载出错:', error); + } }, //获取当前登录的员工信息 async getUserInfo(){ - let res = await apiRoute.getPersonnelInfo({}) - if (res.code != 1) { - uni.showToast({ - title: res.msg, - icon: 'none' - }) - return + console.log('getUserInfo - 开始获取员工信息'); + try { + let res = await apiRoute.getPersonnelInfo({}) + if (res.code != 1) { + uni.showToast({ + title: res.msg, + icon: 'none' + }) + return false; + } + + this.userInfo = res.data + console.log('getUserInfo - 员工信息获取成功'); + return true; + } catch (error) { + console.error('getUserInfo - 获取员工信息失败:', error); + return false; } - - this.userInfo = res.data - // console.log('员工',this.userInfo) }, //获取客户详情 async getInfo(){ - let data = { - resource_sharing_id:this.resource_sharing_id//共享资源表id - } - let res = await apiRoute.xs_resourceSharingInfo(data) - if(res.code != 1){ - uni.showToast({ - title: res.msg, - icon: 'none' - }) - return - } - this.clientInfo = res.data - console.log('详情',this.clientInfo) + console.log('getInfo - 开始获取客户详情, resource_sharing_id:', this.resource_sharing_id); + try { + // 检查resource_sharing_id是否存在 + if (!this.resource_sharing_id) { + console.error('getInfo - resource_sharing_id为空,无法获取客户详情'); + uni.showToast({ + title: '缺少必要参数', + icon: 'none' + }); + return false; + } + + let data = { + resource_sharing_id:this.resource_sharing_id//共享资源表id + } + console.log('getInfo - 发起请求:', data); + let res = await apiRoute.xs_resourceSharingInfo(data) + console.log('getInfo - 请求响应:', res); + if(res.code != 1){ + console.error('getInfo - 请求失败:', res.msg); + uni.showToast({ + title: res.msg, + icon: 'none' + }) + return false; + } + this.clientInfo = res.data + console.log('getInfo - 客户详情数据:', this.clientInfo); + console.log('getInfo - 客户详情获取完成'); + return true; + } catch (error) { + console.error('getInfo - 获取客户详情失败:', error); + return false; + } }, //获取跟进记录 @@ -362,22 +474,46 @@ //获取通话记录 async getListCallUp(){ - let data = { - sales_id:this.resource_sharing_id// - } - let res = await apiRoute.listCallUp(data) - if(res.code != 1){ - uni.showToast({ - title: res.msg, - icon: 'none' - }) - return + console.log('getListCallUp - 开始获取通话记录'); + try { + // 检查resource_sharing_id是否存在 + if (!this.resource_sharing_id) { + console.error('getListCallUp - resource_sharing_id为空,无法获取通话记录'); + return false; + } + + let data = { + sales_id:this.resource_sharing_id// + } + let res = await apiRoute.listCallUp(data) + if(res.code != 1){ + uni.showToast({ + title: res.msg, + icon: 'none' + }) + return false; + } + this.listCallUp = res.data + console.log('getListCallUp - 通话记录获取成功'); + return true; + } catch (error) { + console.error('getListCallUp - 获取通话记录失败:', error); + return false; } - this.listCallUp = res.data }, //跳转页面-编辑客户详情 openViewEditClues(){ + // 检查resource_sharing_id是否存在 + if (!this.resource_sharing_id) { + console.error('openViewEditClues - resource_sharing_id为空,无法跳转'); + uni.showToast({ + title: '缺少必要参数', + icon: 'none' + }); + return; + } + let resource_sharing_id = this.resource_sharing_id//共享资源表id this.$navigateTo({ url: `/pages/market/clue/edit_clues?resource_sharing_id=${resource_sharing_id}` @@ -386,7 +522,17 @@ //跳转页面-客户信息修改记录 openViewEditCluesLog() { - let resource_id = this.clientInfo.resource_id + // 检查clientInfo.resource_id是否存在 + const resource_id = this.safeGet(this.clientInfo, 'resource_id'); + if (!resource_id) { + console.error('openViewEditCluesLog - resource_id为空,无法跳转'); + uni.showToast({ + title: '缺少必要参数', + icon: 'none' + }); + return; + } + this.$navigateTo({ url: `/pages/market/clue/edit_clues_log?resource_id=${resource_id}` }) @@ -394,11 +540,20 @@ //跳转页面-订单列表 openViewOrder() { - let resource_id = this.clientInfo.resource_id//客户资源id - let resource_name = this.clientInfo.customerResource.name || ''//客户资源id姓名 - - let staff_id = this.userInfo.id//员工id - let staff_id_name = this.userInfo.name || ''//员工姓名 + // 检查必要参数是否存在 + const resource_id = this.safeGet(this.clientInfo, 'resource_id'); + if (!resource_id) { + console.error('openViewOrder - resource_id为空,无法跳转'); + uni.showToast({ + title: '缺少必要参数', + icon: 'none' + }); + return; + } + + let resource_name = this.safeGet(this.clientInfo, 'customerResource.name', '')//客户资源id姓名 + let staff_id = this.safeGet(this.userInfo, 'id', '')//员工id + let staff_id_name = this.safeGet(this.userInfo, 'name', '')//员工姓名 this.$navigateTo({ url: `/pages/market/clue/order_list?resource_id=${resource_id}&resource_name=${resource_name}&staff_id=${staff_id}&staff_id_name=${staff_id_name}` @@ -451,29 +606,100 @@ }) }, - // 拨打电话 + // 打开添加备注弹窗 + openAddRemark() { + this.remark_content = ''; + this.$refs.remarkPopup.open(); + }, + + // 确认添加备注 + async confirmRemark() { + if (!this.remark_content.trim()) { + uni.showToast({ + title: '请输入备注内容', + icon: 'none' + }); + return; + } + + try { + uni.showLoading({ + title: '提交中...', + mask: true + }); + + const params = { + staff_id: this.userInfo.id, // 员工id + resource_id: this.clientInfo.resource_id, // 资源ID + resource_type: '', // 资源类型 + communication_type: 'note', // 沟通类型: phone-电话, note-备注 + communication_result: 'success', // 沟通结果 + remarks: this.remark_content, // 备注内容 + tag: null // 标签 + }; + + const res = await apiRoute.xs_communicationRecordsAdd(params); + + if (res.code !== 1) { + uni.showToast({ + title: res.msg || '添加备注失败', + icon: 'none' + }); + return; + } + + uni.showToast({ + title: '添加备注成功', + icon: 'success' + }); + + // 刷新通话记录列表 + this.getListCallUp(); + + } catch (error) { + console.error('添加备注失败:', error); + uni.showToast({ + title: '添加备注失败,请重试', + icon: 'none' + }); + } finally { + uni.hideLoading(); + this.$refs.remarkPopup.close(); + } + }, + + // 关闭备注弹窗 + closeRemark() { + this.$refs.remarkPopup.close(); + }, + + // 拨打电话按钮点击 handleMakeCall() { - if (!this.clientInfo.customerResource.phone_number) { + if (!this.clientInfo.customerResource.phone) { uni.showToast({ title: '电话号码为空', icon: 'none' }); return; } - this.callTel(this.clientInfo.customerResource.phone_number); + this.callTel(this.clientInfo.customerResource.phone); }, - // 发送消息 + // 发送消息按钮点击 handleSendMessage() { - if (!this.clientInfo.customerResource.staff_id) { + if (!this.clientInfo.customerResource.member_id) { uni.showToast({ - title: '未找到相关人员', + title: '该客户未注册账号,无法发送消息', icon: 'none' }); return; } + + let member_id = this.clientInfo.customerResource.member_id; + let member_name = this.clientInfo.customerResource.name || ''; + this.$navigateTo({ - url: `/pages/common/im_chat_info?hair_staff_id=${this.clientInfo.customerResource.staff_id}` + url: `/pages/market/clue/chat?member_id=${member_id}&member_name=${member_name}` }); }, @@ -484,6 +710,20 @@ getSelect(type){ this.select_type = type }, + + // 安全访问对象属性的方法 + safeGet(obj, path, defaultValue = '') { + if (!obj) return defaultValue; + const keys = path.split('.'); + let result = obj; + for (const key of keys) { + if (result === null || result === undefined || !result.hasOwnProperty(key)) { + return defaultValue; + } + result = result[key]; + } + return result || defaultValue; + }, } } @@ -501,6 +741,26 @@ min-height: 28vh; } + // 操作按钮样式 + .action-buttons { + display: flex; + align-items: center; + + .btn-item { + width: 60rpx; + height: 60rpx; + display: flex; + align-items: center; + justify-content: center; + margin-left: 20rpx; + + .btn-icon { + width: 40rpx; + height: 40rpx; + } + } + } + //统计信息 .count_section { width: 100%; @@ -699,24 +959,4 @@ padding: 12rpx 8rpx; text-align: center; } - .action-buttons { - display: flex; - gap: 20rpx; - align-items: center; - - .btn-item { - display: flex; - align-items: center; - justify-content: center; - width: 60rpx; - height: 60rpx; - border-radius: 50%; - background: #f5f5f5; - - .btn-icon { - width: 40rpx; - height: 40rpx; - } - } - } \ No newline at end of file diff --git a/pages/market/clue/edit_clues.vue b/pages/market/clue/edit_clues.vue index 8946159..7ce1aff 100644 --- a/pages/market/clue/edit_clues.vue +++ b/pages/market/clue/edit_clues.vue @@ -134,13 +134,13 @@ - +