Browse Source

修改 bug

master
王泽彦 9 months ago
parent
commit
ce7a849217
  1. 5
      niucloud/app/adminapi/controller/course_schedule/CourseSchedule.php
  2. 15
      niucloud/app/api/controller/member/Member.php
  3. 24
      niucloud/app/api/route/route.php
  4. 1
      niucloud/app/common.php
  5. 4
      niucloud/app/service/admin/course_schedule/CourseScheduleService.php
  6. 2
      niucloud/app/service/api/apiService/CourseService.php
  7. 2
      niucloud/app/service/api/apiService/ResourceSharingService.php
  8. 53
      niucloud/app/service/api/member/MemberService.php
  9. 102
      uniapp/pages/market/clue/add_clues.vue
  10. 229
      uniapp/pages/market/clue/class_arrangement_detail.vue
  11. 163
      uniapp/pages/market/clue/clue_info.vue
  12. 190
      uniapp/pages/market/clue/edit_clues.vue

5
niucloud/app/adminapi/controller/course_schedule/CourseSchedule.php

@ -164,7 +164,10 @@ class CourseSchedule extends BaseAdminController
["person_type",''], ["person_type",''],
["schedule_id",''], ["schedule_id",''],
["course_date",''], ["course_date",''],
["time_slot",''] ["time_slot",''],
["schedule_type", 1], // 1=正式位, 2=等待位
["course_type", 1], // 1=正式课, 2=体验课, 3=补课, 4=试听课
["position", ''] // 位置信息
]); ]);
return (new CourseScheduleService())->addSchedule($data); return (new CourseScheduleService())->addSchedule($data);
} }

15
niucloud/app/api/controller/member/Member.php

@ -18,6 +18,7 @@ use app\service\api\member\MemberLogService;
use app\service\api\member\MemberService; use app\service\api\member\MemberService;
use core\base\BaseApiController; use core\base\BaseApiController;
use think\facade\Db; use think\facade\Db;
use think\facade\Log;
use think\Response; use think\Response;
class Member extends BaseApiController class Member extends BaseApiController
@ -127,9 +128,19 @@ class Member extends BaseApiController
public function list_call_up() public function list_call_up()
{ {
$data = $this->request->params([ $data = $this->request->params([
['sales_id', ''], ['resource_id', ''],
['sales_id', ''], // 保留旧参数名称以保持兼容性
]); ]);
return success((new MemberService())->list_call_up($data['sales_id']));
// 优先使用resource_id,如果不存在则使用sales_id
$resource_id = !empty($data['resource_id']) ? $data['resource_id'] : $data['sales_id'];
// 记录日志
Log::debug("Member/list_call_up - 请求参数: resource_id={$resource_id}");
$result = (new MemberService())->list_call_up($resource_id);
return success($result);
} }
public function update_call_up() public function update_call_up()

24
niucloud/app/api/route/route.php

@ -127,18 +127,6 @@ Route::group(function () {
// 通过经纬度查询地址 // 通过经纬度查询地址
Route::get('area/address_by_latlng', 'sys.Area/getAddressByLatlng'); Route::get('area/address_by_latlng', 'sys.Area/getAddressByLatlng');
/***************************************************** 字典批量获取 ****************************************************/
// 批量获取字典数据
Route::get('dict/batch', 'common.Dict/getBatchDict');
// 根据业务场景获取字典数据
Route::get('dict/scene/:scene', 'common.Dict/getDictByScene');
// 获取单个字典数据
Route::get('dict/single/:key', 'common.Dict/getDict');
// 获取字典映射关系
Route::get('dict/mapping', 'common.Dict/getDictMapping');
// 清除字典缓存
Route::post('dict/clear_cache', 'common.Dict/clearDictCache');
/***************************************************** 海报管理 ****************************************************/ /***************************************************** 海报管理 ****************************************************/
//获取海报 //获取海报
Route::get('poster', 'poster.Poster/poster'); Route::get('poster', 'poster.Poster/poster');
@ -465,7 +453,17 @@ Route::group(function () {
Route::post('xy/orderTable/add', 'apiController.OrderTable/add'); Route::post('xy/orderTable/add', 'apiController.OrderTable/add');
/***************************************************** 字典批量获取 ****************************************************/
// 批量获取字典数据
Route::get('dict/batch', 'common.Dict/getBatchDict');
// 根据业务场景获取字典数据
Route::get('dict/scene/:scene', 'common.Dict/getDictByScene');
// 获取单个字典数据
Route::get('dict/single/:key', 'common.Dict/getDict');
// 获取字典映射关系
Route::get('dict/mapping', 'common.Dict/getDictMapping');
// 清除字典缓存
Route::post('dict/clear_cache', 'common.Dict/clearDictCache');
})->middleware(ApiChannel::class) })->middleware(ApiChannel::class)
->middleware(ApiPersonnelCheckToken::class, true) ->middleware(ApiPersonnelCheckToken::class, true)

1
niucloud/app/common.php

@ -1229,6 +1229,7 @@ function get_dict_value($key, $value)
$field = 'id,name,key,dictionary,memo,create_time,update_time'; $field = 'id,name,key,dictionary,memo,create_time,update_time';
$info = $dict->field($field)->where([['key', '=', $key]])->findOrEmpty()->toArray(); $info = $dict->field($field)->where([['key', '=', $key]])->findOrEmpty()->toArray();
if ($info['dictionary'] == null) { if ($info['dictionary'] == null) {
$info['dictionary'] = []; $info['dictionary'] = [];
} }

4
niucloud/app/service/admin/course_schedule/CourseScheduleService.php

@ -299,7 +299,7 @@ class CourseScheduleService extends BaseAdminService
->alias('a') ->alias('a')
->join(['school_customer_resources' => 'b'],'a.resources_id = b.id','left') ->join(['school_customer_resources' => 'b'],'a.resources_id = b.id','left')
->where('a.schedule_id',$data['schedule_id']) ->where('a.schedule_id',$data['schedule_id'])
->field("b.name,a.status") ->field("a.id,a.resources_id,a.person_id,a.student_id,a.person_type,a.schedule_id,a.course_date,a.schedule_type,a.course_type,a.time_slot,a.status,a.remark,b.name")
->select()->toArray(); ->select()->toArray();
return $list; return $list;
@ -342,6 +342,8 @@ class CourseScheduleService extends BaseAdminService
'schedule_id' => $data['schedule_id'], 'schedule_id' => $data['schedule_id'],
'course_date' => $data['course_date'], 'course_date' => $data['course_date'],
'time_slot' => $data['time_slot'], 'time_slot' => $data['time_slot'],
'schedule_type' => $data['schedule_type'] ?? 1, // 1=正式位, 2=等待位
'course_type' => $data['course_type'] ?? 1, // 1=正式课, 2=体验课, 3=补课, 4=试听课
]); ]);
$CourseSchedule->where(['id' => $data['schedule_id']])->dec("available_capacity")->update(); $CourseSchedule->where(['id' => $data['schedule_id']])->dec("available_capacity")->update();
return success("添加成功"); return success("添加成功");

2
niucloud/app/service/api/apiService/CourseService.php

@ -327,7 +327,7 @@ class CourseService extends BaseApiService
->join(['school_customer_resources' => 'b'],'a.resources_id = b.id','left') ->join(['school_customer_resources' => 'b'],'a.resources_id = b.id','left')
->join(['school_course_schedule' => 'c'],'c.id = a.schedule_id','left') ->join(['school_course_schedule' => 'c'],'c.id = a.schedule_id','left')
->where('a.schedule_id',$data['schedule_id']) ->where('a.schedule_id',$data['schedule_id'])
->field("b.name,a.status,a.person_type,c.campus_id,b.id as resources_id") ->field("b.name,a.status,a.person_type,c.campus_id,b.id as resources_id,a.schedule_type,a.course_type")
->select() ->select()
->toArray(); ->toArray();

2
niucloud/app/service/api/apiService/ResourceSharingService.php

@ -300,7 +300,7 @@ class ResourceSharingService extends BaseApiService
if (!empty($item['customerResource'])) { if (!empty($item['customerResource'])) {
// 设置来源和渠道名称 // 设置来源和渠道名称
$item['customerResource']['source'] = get_dict_value('source', $item['customerResource']['source']); $item['customerResource']['source'] = get_dict_value('source', $item['customerResource']['source']);
$item['customerResource']['source_channel'] = get_dict_value('source', $item['customerResource']['source_channel']); $item['customerResource']['source_channel'] = get_dict_value('SourceChannel', $item['customerResource']['source_channel']);
$item['customerResource']['campus_name'] = $campus_name[$item['customerResource']['campus']] ?? ''; $item['customerResource']['campus_name'] = $campus_name[$item['customerResource']['campus']] ?? '';
$item['customerResource']['communication_time'] = $resultdata[$item['resource_id']] ?? ''; $item['customerResource']['communication_time'] = $resultdata[$item['resource_id']] ?? '';
} }

53
niucloud/app/service/api/member/MemberService.php

@ -26,6 +26,7 @@ use core\base\BaseApiService;
use core\exception\ApiException; use core\exception\ApiException;
use core\util\Barcode; use core\util\Barcode;
use think\facade\Db; use think\facade\Db;
use think\facade\Log;
use think\Model; use think\Model;
/** /**
@ -181,10 +182,58 @@ class MemberService extends BaseApiService
} }
/**
* 日志记录工具方法
* @param string $level 日志级别
* @param string $message 日志信息
* @return void
*/
private function log($level, $message) {
Log::$level('MemberService: ' . $message);
}
public function list_call_up($resource_id) public function list_call_up($resource_id)
{ {
$campus = new CommunicationRecords(); $communication = new CommunicationRecords();
return $campus->where('resource_id', $resource_id)->select()->toArray(); // 添加日志记录以便调试
$this->log('debug', "list_call_up请求参数: resource_id={$resource_id}");
try {
// 检查resource_id是否有效
if (empty($resource_id)) {
$this->log('warning', "list_call_up: resource_id为空");
return [];
}
// 查询前打印SQL查询条件
$sqlDebug = "SELECT * FROM school_communication_records WHERE resource_id = '{$resource_id}' ORDER BY communication_time DESC";
$this->log('debug', "list_call_up对应SQL: {$sqlDebug}");
// 执行查询
$result = $communication->where('resource_id', $resource_id)
->order('communication_time DESC')
->select()->toArray();
// 如果没有结果,尝试使用原生 SQL 查询检查记录存在性
if (empty($result)) {
$this->log('debug', "list_call_up: 主要查询没有结果,尝试直接查询表");
$rawResult = $communication->query("SELECT COUNT(*) as count FROM school_communication_records WHERE resource_id = '{$resource_id}'");
$count = $rawResult[0]['count'] ?? 0;
$this->log('debug', "list_call_up: 原生SQL查询结果数量: {$count}");
// 如果原生查询有结果但模型查询没结果,尝试直接使用原生查询
if ($count > 0) {
$this->log('debug', "list_call_up: 检测到数据存在,使用原生查询获取");
$result = $communication->query("SELECT * FROM school_communication_records WHERE resource_id = '{$resource_id}' ORDER BY communication_time DESC");
}
}
$this->log('debug', "list_call_up查询结果数量: " . count($result));
return $result;
} catch (\Exception $e) {
$this->log('error', "list_call_up查询异常: " . $e->getMessage());
return [];
}
} }
public function update_call_up($resource_id, $remarks) public function update_call_up($resource_id, $remarks)

102
uniapp/pages/market/clue/add_clues.vue

@ -428,7 +428,15 @@
<!-- 年月日-选择时间 --> <!-- 年月日-选择时间 -->
<fui-date-picker :show="date_picker_show" type="3" @change="change_date" @cancel="cancel_date"></fui-date-picker> <fui-date-picker
:show="date_picker_show"
type="3"
:startYear="2020"
:endYear="2030"
:value="getCurrentDate()"
@change="change_date"
@cancel="cancel_date">
</fui-date-picker>
<!-- 选择器 --> <!-- 选择器 -->
<fui-picker <fui-picker
@ -764,10 +772,56 @@ export default {
// 使 // 使
await this.getBatchDictData() await this.getBatchDictData()
//
this.setDefaultTimes()
// this.getStaffList()// // this.getStaffList()//
// this.getAreaTree()// // this.getAreaTree()//
}, },
//
setDefaultTimes() {
//
this.formData.optional_class_time = ''
// 访
this.formData.promised_visit_time = ''
console.log('时间默认值已清空,用户需手动选择')
},
//
formatDate(date) {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
// FirstUI
//
// :
const formats = {
standard: `${year}-${month}-${day}`, // YYYY-MM-DD
chinese: `${year}${month}${day}`, // YYYYMMDD
slash: `${year}/${month}/${day}`, // YYYY/MM/DD
dot: `${year}.${month}.${day}`, // YYYY.MM.DD
space: `${year} ${month} ${day}` // YYYY MM DD
}
// 使
return formats.standard
// :
// return formats.chinese //
// return formats.slash //
},
//
getCurrentDate() {
const today = new Date()
return this.formatDate(today)
},
// //
async getBatchDictData() { async getBatchDictData() {
try { try {
@ -1267,22 +1321,56 @@ export default {
//######----------###### //######----------######
// //
openDate(input_name) { openDate(input_name) {
this.date_picker_show = true console.log('打开日期选择器:', input_name)
this.data_picker_input_name = input_name this.data_picker_input_name = input_name
//
this.$nextTick(() => {
this.date_picker_show = true
})
}, },
// //
change_date(e) { change_date(e) {
// console.log('日期选择器返回数据:', e)
let val = (e.result ?? '')
if(val){ //
val = val let val = ''
if (e.result) {
val = e.result
} else if (e.value) {
val = e.value
} else if (e.detail && e.detail.result) {
val = e.detail.result
} else if (e.detail && e.detail.value) {
val = e.detail.value
}
// YYYY-MM-DD
if (val && typeof val === 'string') {
//
if (/^\d+$/.test(val)) {
const date = new Date(parseInt(val))
val = this.formatDate(date)
}
//
else if (val.includes(' ')) {
val = val.split(' ')[0]
}
// YYYY-MM-DD
if (val.includes('/')) {
const parts = val.split('/')
if (parts.length === 3) {
val = `${parts[0]}-${parts[1].padStart(2, '0')}-${parts[2].padStart(2, '0')}`
}
}
} }
let input_name = this.data_picker_input_name let input_name = this.data_picker_input_name
this.formData[input_name] = val this.formData[input_name] = val
console.log(`设置${input_name}为:`, val)
this.cancel_date() this.cancel_date()
}, },
// //

229
uniapp/pages/market/clue/class_arrangement_detail.vue

@ -4,30 +4,24 @@
<view class="title">课程安排详情</view> <view class="title">课程安排详情</view>
<view class="date">日期{{ course_info.course_date }} {{course_info.time_slot}}</view> <view class="date">日期{{ course_info.course_date }} {{course_info.time_slot}}</view>
</view> </view>
<!-- 正式学员列表 -->
<view class="section"> <view class="section">
<view class="section-title">学员列表</view> <view class="section-title">正式学员</view>
<view class="student-list" v-if="course_info && course_info.available_capacity"> <view class="student-list" v-if="course_info">
<!-- 显示已安排的学员 --> <!-- 显示已安排的正式学员 -->
<view <view v-for="(stu, idx) in formalStudents" :key="idx" class="student-item"
v-for="(stu, idx) in students" @tap="viewStudent(stu)">
:key="idx"
class="student-item"
@tap="viewStudent(stu)"
>
<view class="avatar">{{ stu.name && stu.name.charAt(0) }}</view> <view class="avatar">{{ stu.name && stu.name.charAt(0) }}</view>
<view class="info"> <view class="info">
<view class="name">{{ stu.name }}</view> <view class="name">{{ stu.name }}</view>
<view class="desc">{{ getStatusText(stu.status) }}</view> <view class="desc">{{ getCourseTypeText(stu.course_type) }} | {{ getStatusText(stu.status) }}
</view>
</view> </view>
</view> </view>
<!-- 显示空位 --> <!-- 显示正式学员空位 -->
<view <view v-for="index in formalEmptySeats" :key="index" class="student-item empty"
v-for="index in emptySeats" @tap="addStudent($event, index, 'formal')">
:key="index"
class="student-item empty"
@tap="addStudent($event, index)"
>
<view class="avatar empty-avatar">+</view> <view class="avatar empty-avatar">+</view>
<view class="info"> <view class="info">
<view class="name">空位</view> <view class="name">空位</view>
@ -42,16 +36,46 @@
</view> </view>
</view> </view>
<!-- 等待位学员列表 -->
<view class="section">
<view class="section-title waiting-title">等待位</view>
<view class="student-list">
<!-- 显示等待位学员 -->
<view v-for="(stu, idx) in waitingStudents" :key="idx" class="student-item waiting"
@tap="viewStudent(stu)">
<view class="avatar waiting-avatar">{{ stu.name && stu.name.charAt(0) }}</view>
<view class="info">
<view class="name">{{ stu.name }}</view>
<view class="desc">{{ getCourseTypeText(stu.course_type) }} | {{ getStatusText(stu.status) }}
</view>
</view>
</view>
<!-- 等待位空位固定2个 -->
<view v-for="index in waitingEmptySeats" :key="index"
class="student-item empty waiting" @tap="addStudent($event, index, 'waiting')">
<view class="avatar empty-avatar waiting-avatar">+</view>
<view class="info">
<view class="name">等待位</view>
<view class="desc">点击添加学员</view>
</view>
</view>
</view>
</view>
<!-- 请假原因弹窗 --> <!-- 请假原因弹窗 -->
<fui-modal ref="leaveReasonModal" :buttons="[]" width="600" title="请假申请"> <fui-modal ref="leaveReasonModal" :buttons="[]" width="600" title="请假申请">
<view class="leave-form"> <view class="leave-form">
<view class="leave-label">请假原因</view> <view class="leave-label">请假原因</view>
<view class="leave-input"> <view class="leave-input">
<fui-textarea v-model="leaveReason" placeholder="请输入请假原因" :isCounter="true" :maxlength="200" :minHeight="200" :isAutoHeight="true"></fui-textarea> <fui-textarea v-model="leaveReason" placeholder="请输入请假原因" :isCounter="true" :maxlength="200"
:minHeight="200" :isAutoHeight="true"></fui-textarea>
</view> </view>
<view class="leave-buttons"> <view class="leave-buttons">
<fui-button background="#434544" color="#fff" borderColor="#666" btnSize="medium" @tap="$refs.leaveReasonModal.close()">取消</fui-button> <fui-button background="#434544" color="#fff" borderColor="#666" btnSize="medium"
<fui-button background="#29d3b4" color="#fff" btnSize="medium" @tap="submitLeaveRequest">提交</fui-button> @tap="$refs.leaveReasonModal.close()">取消</fui-button>
<fui-button background="#29d3b4" color="#fff" btnSize="medium"
@tap="submitLeaveRequest">提交</fui-button>
</view> </view>
</view> </view>
</fui-modal> </fui-modal>
@ -63,23 +87,17 @@
export default { export default {
data() { data() {
return { return {
course_id:'', course_id: '',
course_info:[], course_info: [],
date: '', date: '',
students: [ students: [], //
// { formalStudents: [], //
// name: '', waitingStudents: [], //
// desc: '' resource_id: '',
// },
// {
// name: '',
// desc: ''
// },
],
resource_id:'',
leaveReason: '', // leaveReason: '', //
currentStudent: null, // currentStudent: null, //
emptySeats: [] // formalEmptySeats: [], //
waitingEmptySeats: [1, 2] // 2
}; };
}, },
onLoad(query) { onLoad(query) {
@ -90,9 +108,15 @@
this.courseInfo(); this.courseInfo();
}, },
methods: { methods: {
viewStudent(stu){ viewStudent(stu) {
console.log(stu, this.course_info); console.log(stu, this.course_info);
//
uni.showActionSheet({
title: `学员: ${stu.name}`,
itemList: stu.person_type === 'customer_resource' ? ['取消课程'] : ['申请请假'],
success: (res) => {
if (res.tapIndex === 0) {
// //
if (stu.person_type === 'customer_resource') { if (stu.person_type === 'customer_resource') {
// //
@ -146,6 +170,9 @@
this.$refs.leaveReasonModal.open(); this.$refs.leaveReasonModal.open();
this.currentStudent = stu; // this.currentStudent = stu; //
} }
}
}
});
}, },
getStatusText(status) { getStatusText(status) {
const statusMap = { const statusMap = {
@ -155,6 +182,36 @@
}; };
return statusMap[status] || status; return statusMap[status] || status;
}, },
//
getCourseTypeText(courseType) {
const courseTypeMap = {
1: '正式课',
2: '体验课',
3: '补课',
4: '试听课'
};
return courseTypeMap[courseType] || '未知';
},
//
categorizeStudents() {
// schedule_type
// schedule_type: 1=, 2=
this.formalStudents = this.students.filter(stu => stu.schedule_type === 1 || stu.schedule_type === null);
this.waitingStudents = this.students.filter(stu => stu.schedule_type === 2);
//
this.formalStudents.forEach(stu => {
if (!stu.name) stu.name = '未知学员';
});
this.waitingStudents.forEach(stu => {
if (!stu.name) stu.name = '未知学员';
});
console.log('学员分类完成:', {
formal: this.formalStudents.length,
waiting: this.waitingStudents.length
});
},
async scheduleList() { async scheduleList() {
try { try {
console.log('开始获取学员列表, schedule_id:', this.course_id); console.log('开始获取学员列表, schedule_id:', this.course_id);
@ -174,8 +231,13 @@
// ID // ID
return a.id - b.id; return a.id - b.id;
}); });
//
this.categorizeStudents();
} else { } else {
this.students = []; this.students = [];
this.formalStudents = [];
this.waitingStudents = [];
} }
// //
@ -185,6 +247,8 @@
} catch (error) { } catch (error) {
console.error('获取学员列表失败:', error); console.error('获取学员列表失败:', error);
this.students = []; this.students = [];
this.formalStudents = [];
this.waitingStudents = [];
return []; return [];
} }
}, },
@ -221,30 +285,39 @@
}, },
// //
updateAvailableCapacity() { updateAvailableCapacity() {
// course_infostudents // course_info
if (!this.course_info || !this.students) return; if (!this.course_info) return;
console.log('更新可用容量 - 课程总容量:', this.course_info.available_capacity, '学员数量:', this.students.length); console.log('更新可用容量 - 课程总容量:', this.course_info.available_capacity, '正式学员数量:', this.formalStudents.length);
// //
const occupiedSeats = this.students.length; const occupiedFormalSeats = this.formalStudents.length;
// //
const totalCapacity = parseInt(this.course_info.available_capacity) || 0; const totalCapacity = parseInt(this.course_info.available_capacity) || 0;
// //
this.course_info.available_capacity = Math.max(0, totalCapacity - occupiedSeats); const remainingFormalSeats = Math.max(0, totalCapacity - occupiedFormalSeats);
console.log('计算后的正式位剩余容量:', remainingFormalSeats);
console.log('计算后的可用容量:', this.course_info.available_capacity); //
this.formalEmptySeats = [];
for (let i = 1; i <= remainingFormalSeats; i++) {
this.formalEmptySeats.push(i);
}
// //
this.emptySeats = []; const remainingWaitingSeats = Math.max(0, 2 - this.waitingStudents.length);
for (let i = 1; i <= this.course_info.available_capacity; i++) { this.waitingEmptySeats = [];
this.emptySeats.push(i); for (let i = 1; i <= remainingWaitingSeats; i++) {
this.waitingEmptySeats.push(i);
} }
console.log('等待位剩余空位:', remainingWaitingSeats);
}, },
async addStudent(e, index) { async addStudent(e, index, type = 'formal') {
console.log('添加学员到位置:', index); console.log('添加学员到位置:', index, '类型:', type);
const data = { const data = {
'resources_id': this.resource_id, 'resources_id': this.resource_id,
@ -252,7 +325,9 @@
'schedule_id': this.course_id, 'schedule_id': this.course_id,
'course_date': this.course_info.course_date, 'course_date': this.course_info.course_date,
'time_slot': this.course_info.time_slot, 'time_slot': this.course_info.time_slot,
'position': index // 'position': index, //
'schedule_type': type === 'waiting' ? 2 : 1, // 1=, 2=
'course_type': type === 'waiting' ? 2 : 1 // 1=, 2=
}; };
try { try {
@ -264,7 +339,7 @@
uni.hideLoading(); uni.hideLoading();
if(res.code == 1){ if (res.code == 1) {
uni.showToast({ uni.showToast({
title: '添加成功', title: '添加成功',
icon: 'success' icon: 'success'
@ -272,7 +347,7 @@
// //
await this.courseInfo(); await this.courseInfo();
}else{ } else {
uni.showToast({ uni.showToast({
title: res.msg || '添加失败', title: res.msg || '添加失败',
icon: 'none' icon: 'none'
@ -395,7 +470,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
padding: 18rpx 24rpx; padding: 18rpx 24rpx;
min-width: 260rpx; width: calc(50% - 10rpx);
/* 一行两个,减去gap的一半 */
box-sizing: border-box;
.avatar { .avatar {
width: 60rpx; width: 60rpx;
@ -411,15 +488,24 @@
} }
.info { .info {
flex: 1;
overflow: hidden;
.name { .name {
color: #fff; color: #fff;
font-size: 28rpx; font-size: 28rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.desc { .desc {
color: #bdbdbd; color: #bdbdbd;
font-size: 22rpx; font-size: 22rpx;
margin-top: 4rpx; margin-top: 4rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
} }
@ -441,6 +527,41 @@
color: #ffd86b; color: #ffd86b;
} }
} }
//
&.waiting {
background: #2a2a3a; //
border: 1rpx solid #8a7fff;
.avatar,
.avatar.waiting-avatar {
background: #8a7fff;
color: #fff;
}
&.empty {
border: 2rpx dashed #8a7fff;
background: #1a1a2a;
.avatar.empty-avatar {
background: #8a7fff;
color: #1a1a2a;
}
.info .name {
color: #8a7fff;
}
.info .desc {
color: #8a7fff;
}
}
}
}
//
.waiting-title {
color: #8a7fff !important;
} }
// //

163
uniapp/pages/market/clue/clue_info.vue

@ -49,7 +49,8 @@
<view :class="{'selected-text': switch_tags_type === 1, 'text': switch_tags_type !== 1}" <view :class="{'selected-text': switch_tags_type === 1, 'text': switch_tags_type !== 1}"
@click="switch_tags(1)">基本资料 @click="switch_tags(1)">基本资料
</view> </view>
<view :class="{'selected-text': switch_tags_type === 2, 'text': switch_tags_type !== 2}" <view v-if="hasCourseInfo"
:class="{'selected-text': switch_tags_type === 2, 'text': switch_tags_type !== 2}"
@click="switch_tags(2)">课程信息 @click="switch_tags(2)">课程信息
</view> </view>
<view :class="{'selected-text': switch_tags_type === 3, 'text': switch_tags_type !== 3}" <view :class="{'selected-text': switch_tags_type === 3, 'text': switch_tags_type !== 3}"
@ -346,6 +347,12 @@
selectedAssistants: [], // ID selectedAssistants: [], // ID
} }
}, },
computed: {
//
hasCourseInfo() {
return this.courseInfo && Array.isArray(this.courseInfo) && this.courseInfo.length > 0;
}
},
onLoad(options) { onLoad(options) {
console.log('onLoad - 接收到参数:', options); console.log('onLoad - 接收到参数:', options);
@ -375,33 +382,6 @@
async init(){ async init(){
console.log('init - 开始初始化流程'); 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 { try {
// //
@ -409,14 +389,15 @@
await this.getInfo(); await this.getInfo();
console.log('init - 客户详情获取完成'); console.log('init - 客户详情获取完成');
// //
console.log('init - 开始获取员工信息、通话记录和教练列表'); console.log('init - 开始获取员工信息、通话记录、教练列表和课程信息');
await Promise.all([ await Promise.all([
this.getUserInfo(), this.getUserInfo(),
this.getListCallUp(), this.getListCallUp(),
this.getPersonnelList() this.getPersonnelList(),
this.getCourseInfo() //
]); ]);
console.log('init - 员工信息、通话记录和教练列表获取完成'); console.log('init - 员工信息、通话记录、教练列表和课程信息获取完成');
} catch (error) { } catch (error) {
console.error('init - 数据加载出错:', error); console.error('init - 数据加载出错:', error);
} }
@ -502,25 +483,27 @@
async getListCallUp(){ async getListCallUp(){
console.log('getListCallUp - 开始获取通话记录'); console.log('getListCallUp - 开始获取通话记录');
try { try {
// resource_sharing_id // clientInfo.resource_id
if (!this.resource_sharing_id) { if (!this.clientInfo || !this.clientInfo.resource_id) {
console.error('getListCallUp - resource_sharing_id为空,无法获取通话记录'); console.error('getListCallUp - resource_id为空,无法获取通话记录');
return false; return false;
} }
let data = { let data = {
sales_id:this.resource_sharing_id// resource_id: this.clientInfo.resource_id // 使ID
} }
console.log('getListCallUp - 请求参数:', data);
let res = await apiRoute.listCallUp(data) let res = await apiRoute.listCallUp(data)
console.log('getListCallUp - 响应:', res);
if(res.code != 1){ if(res.code != 1){
uni.showToast({ uni.showToast({
title: res.msg, title: res.msg || '获取通话记录失败',
icon: 'none' icon: 'none'
}) })
return false; return false;
} }
this.listCallUp = res.data this.listCallUp = res.data || []
console.log('getListCallUp - 通话记录获取成功'); console.log('getListCallUp - 通话记录获取成功, 数量:', this.listCallUp.length);
return true; return true;
} catch (error) { } catch (error) {
console.error('getListCallUp - 获取通话记录失败:', error); console.error('getListCallUp - 获取通话记录失败:', error);
@ -738,11 +721,27 @@
// //
async switch_tags(type){ async switch_tags(type){
//
if (type === 2 && !this.hasCourseInfo) {
uni.showToast({
title: '暂无课程信息',
icon: 'none'
});
return;
}
this.switch_tags_type = type this.switch_tags_type = type
//
//
if (type === 2) { if (type === 2) {
await this.getCourseInfo(); await this.getCourseInfo();
} }
//
if (type === 3) {
await this.getListCallUp();
console.log('刷新通话记录数据,当前记录数:', this.listCallUp.length);
}
}, },
getSelect(type){ getSelect(type){
this.select_type = type this.select_type = type
@ -753,37 +752,40 @@
try { try {
if (!this.clientInfo.resource_id) { if (!this.clientInfo.resource_id) {
console.error('getCourseInfo - resource_id为空,无法获取课程信息'); console.error('getCourseInfo - resource_id为空,无法获取课程信息');
this.courseInfo = [];
return false; return false;
} }
// 使 // 使
const params = { const params = {
resource_id: this.clientInfo.resource_id, resource_id: this.clientInfo.resource_id,
member_id: this.clientInfo.customerResource.member_id || '' member_id: this.clientInfo.customerResource.member_id || ''
}; };
// API console.log('getCourseInfo - 请求参数:', params);
try { try {
const res = await apiRoute.getStudentCourseInfo(params); const res = await apiRoute.getStudentCourseInfo(params);
if (res.code === 1 && res.data) { console.log('getCourseInfo - API响应:', res);
this.courseInfo = res.data;
if (res.code === 1) {
//
this.courseInfo = this.formatCourseData(res.data || []);
console.log('getCourseInfo - 课程信息获取成功:', this.courseInfo); console.log('getCourseInfo - 课程信息获取成功:', this.courseInfo);
return true; return true;
} else { } else {
console.warn('API返回错误:', res.msg); console.warn('API返回错误:', res.msg);
throw new Error(res.msg); this.courseInfo = [];
return false;
} }
} catch (apiError) { } catch (apiError) {
console.warn('使用API获取课程信息失败,使用模拟数据:', apiError); console.warn('获取课程信息API调用失败:', apiError);
// API使 this.courseInfo = [];
this.courseInfo = this.getMockCourseData(); return false;
console.log('getCourseInfo - 使用模拟课程数据');
return true;
} }
} catch (error) { } catch (error) {
console.error('getCourseInfo - 获取课程信息异常:', error); console.error('getCourseInfo - 获取课程信息异常:', error);
// this.courseInfo = [];
this.courseInfo = this.getMockCourseData();
return false; return false;
} }
}, },
@ -811,41 +813,6 @@
})); }));
}, },
//
getMockCourseData() {
return [
{
id: 1,
course_name: '篮球基础课程',
total_count: 20,
used_count: 8,
leave_count: 2,
expiry_date: '2024-12-31',
status: 'active',
main_coach_id: 1,
main_coach_name: '张教练',
education_id: 2,
education_name: '李教务',
assistant_ids: '3,4',
assistant_names: '王助教, 赵助教'
},
{
id: 2,
course_name: '足球进阶训练',
total_count: 15,
used_count: 15,
leave_count: 1,
expiry_date: '2024-10-31',
status: 'completed',
main_coach_id: 5,
main_coach_name: '陈教练',
education_id: 2,
education_name: '李教务',
assistant_ids: '6',
assistant_names: '孙助教'
}
];
},
// //
async getPersonnelList() { async getPersonnelList() {
@ -1058,17 +1025,33 @@
}, },
// 访 // 访
safeGet(obj, path, defaultValue = '') { safeGet(obj, path, defaultValue = '') {
if (!obj) return defaultValue; if (!obj) return defaultValue;
const keys = path.split('.');
// 使
if (!this._pathCache) this._pathCache = {};
// 使
const cacheKey = path;
//
if (!this._pathCache[cacheKey]) {
this._pathCache[cacheKey] = path.split('.');
}
const keys = this._pathCache[cacheKey];
let result = obj; let result = obj;
for (const key of keys) {
// 使forfor...of
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (result === null || result === undefined || !result.hasOwnProperty(key)) { if (result === null || result === undefined || !result.hasOwnProperty(key)) {
return defaultValue; return defaultValue;
} }
result = result[key]; result = result[key];
} }
return result || defaultValue; return result || defaultValue;
}, },
} }

190
uniapp/pages/market/clue/edit_clues.vue

@ -140,14 +140,22 @@
</view> </view>
</view> </view>
</fui-form-item> </fui-form-item>
<!-- 上课时间 --> <!-- 可选上课时间 -->
<fui-form-item label="上课时间" labelSize='26' prop="optional_class_time" background='#434544' labelColor='#fff' :bottomBorder='false'> <fui-form-item label="可选上课时间" labelSize='26' prop="optional_class_time" background='#434544' labelColor='#fff' :bottomBorder='false'>
<view class="input-title" style="margin-right:14rpx;"> <view class="input-title" style="margin-right:14rpx;">
<view class="input-title" style="margin-right:14rpx;" @click="openDate('optional_class_time')"> <view class="input-title" style="margin-right:14rpx;" @click="openDate('optional_class_time')">
{{ formData.optional_class_time ? formData.optional_class_time : '点击选择' }} {{ formData.optional_class_time ? formData.optional_class_time : '点击选择' }}
</view> </view>
</view> </view>
</fui-form-item> </fui-form-item>
<!-- 承诺到访时间 -->
<fui-form-item label="承诺到访时间" labelSize='26' prop="promised_visit_time" background='#434544' labelColor='#fff' :bottomBorder='false'>
<view class="input-title" style="margin-right:14rpx;">
<view class="input-title" style="margin-right:14rpx;" @click="openDate('promised_visit_time')">
{{ formData.promised_visit_time ? formData.promised_visit_time : '点击选择' }}
</view>
</view>
</fui-form-item>
<!-- 距离 --> <!-- 距离 -->
<fui-form-item label="距离" labelSize='26' prop="distance" background='#434544' labelColor='#fff' :bottomBorder='false'> <fui-form-item label="距离" labelSize='26' prop="distance" background='#434544' labelColor='#fff' :bottomBorder='false'>
<view class="input-title" style="margin-right:14rpx;"> <view class="input-title" style="margin-right:14rpx;">
@ -577,20 +585,8 @@
console.log('init - 开始加载字典数据'); console.log('init - 开始加载字典数据');
// //
const dictPromises = [ await this.getBatchDictData();
this.getDict('source_channel'), //-
this.getDict('source'), //-
this.getDict('purchasing_power'), //-
this.getDict('initial_intent'), //-
this.getDict('cognitive_idea'), //-
this.getDict('status'), //-
this.getDict('decision_maker'), //-
this.getDict('distance'), //-
];
await Promise.all(dictPromises);
console.log('init - 字典数据加载完成'); console.log('init - 字典数据加载完成');
// //
@ -614,6 +610,139 @@
} }
}, },
//
async getBatchDictData() {
try {
// keys
const dictMapping = {
'SourceChannel': 'source_channel',
'source': 'source',
'customer_purchasing_power': 'purchasing_power',
'preliminarycustomerintention': 'initial_intent',
'cognitive_concept': 'cognitive_idea',
'kh_status': 'status',
'decision_maker': 'decision_maker',
'distance': 'distance'
};
//
if (!window._dictCache) {
window._dictCache = {};
}
//
const criticalDicts = ['source', 'source_channel'];
const regularDicts = Object.keys(dictMapping).filter(key => !criticalDicts.includes(dictMapping[key]));
//
for (const dictKey of criticalDicts) {
const key = Object.keys(dictMapping).find(k => dictMapping[k] === dictKey);
if (key) {
await this.loadDictData(key, dictMapping[key]);
}
}
//
setTimeout(() => {
regularDicts.forEach(async (key) => {
const localKey = dictMapping[key];
if (!window._dictCache[key]) {
await this.loadDictData(key, localKey);
}
});
}, 100);
console.log('优化的批量字典数据加载初始化完成');
} catch (error) {
console.error('批量获取字典数据失败:', error);
// 退
await this.fallbackGetDict();
}
},
//
async loadDictData(key, localKey) {
try {
//
if (window._dictCache[key]) {
// 使
const dictData = window._dictCache[key];
this.processDictData(localKey, dictData);
return dictData;
}
//
const dictData = await this.$util.getDict(key);
//
if (Array.isArray(dictData) && dictData.length > 0) {
window._dictCache[key] = dictData;
this.processDictData(localKey, dictData);
}
return dictData;
} catch (error) {
console.error(`加载字典 ${key} 失败:`, error);
return [];
}
},
//
processDictData(localKey, dictData) {
if (!Array.isArray(dictData) || dictData.length === 0) return;
let formattedOptions = dictData.map(item => ({
text: item.name || '',
value: item.value || ''
}));
// 线
if (localKey === 'source_channel') {
formattedOptions.unshift({
text: '线下',
value: '0'
});
}
// picker_config
if (!this.picker_config[localKey]) {
this.picker_config[localKey] = { options: [], text: '点击选择' };
}
this.picker_config[localKey].options = formattedOptions;
},
// 退
async fallbackGetDict() {
console.log('使用回退方案获取字典数据');
try {
//
await Promise.all([
this.getDict('source_channel'),
this.getDict('source')
]);
//
setTimeout(async () => {
try {
await Promise.all([
this.getDict('purchasing_power'),
this.getDict('initial_intent'),
this.getDict('cognitive_idea'),
this.getDict('status'),
this.getDict('decision_maker'),
this.getDict('distance')
]);
} catch (error) {
console.error('回退方案第二阶段失败:', error);
}
}, 100);
} catch (error) {
console.error('回退方案也失败了:', error);
}
},
async get_campus_list(){ async get_campus_list(){
let res = await apiRoute.common_getCampusesList({}) let res = await apiRoute.common_getCampusesList({})
@ -693,7 +822,7 @@
customer_type: customerResource.customer_type || '', // customer_type: customerResource.customer_type || '', //
// //
purchasing_power: sixSpeed.purchase_power || '', // purchasing_power: sixSpeed.purchasing_power || '', //
cognitive_idea: sixSpeed.concept_awareness || '', // cognitive_idea: sixSpeed.concept_awareness || '', //
communication: sixSpeed.communication || '', // communication: sixSpeed.communication || '', //
staff_id: sixSpeed.staff_id || '', //ID staff_id: sixSpeed.staff_id || '', //ID
@ -763,7 +892,7 @@
this.setPickerTextByValue('customer_type', this.formData.customer_type, customerResource.customer_type_name); this.setPickerTextByValue('customer_type', this.formData.customer_type, customerResource.customer_type_name);
// //
this.setPickerTextByValue('purchasing_power', this.formData.purchasing_power, sixSpeed.purchase_power_name); this.setPickerTextByValue('purchasing_power', this.formData.purchasing_power, sixSpeed.purchasing_power_name);
this.setPickerTextByValue('cognitive_idea', this.formData.cognitive_idea, sixSpeed.concept_awareness_name); this.setPickerTextByValue('cognitive_idea', this.formData.cognitive_idea, sixSpeed.concept_awareness_name);
this.setPickerTextByValue('distance', this.formData.distance, sixSpeed.distance_name); this.setPickerTextByValue('distance', this.formData.distance, sixSpeed.distance_name);
// call_intent // call_intent
@ -776,12 +905,26 @@
// //
setPickerTextByValue(pickerName, value, defaultText) { setPickerTextByValue(pickerName, value, defaultText) {
// 使
if (!value) { if (!value) {
this.picker_config[pickerName] = this.picker_config[pickerName] || {}; this.picker_config[pickerName] = this.picker_config[pickerName] || {};
this.picker_config[pickerName].text = '点击选择'; this.picker_config[pickerName].text = '点击选择';
return; return;
} }
//
if (!this._valueTextMapping) {
this._valueTextMapping = {};
}
//
const cacheKey = `${pickerName}_${value}`;
if (this._valueTextMapping[cacheKey]) {
this.picker_config[pickerName] = this.picker_config[pickerName] || {};
this.picker_config[pickerName].text = this._valueTextMapping[cacheKey];
return;
}
// picker_config[pickerName] // picker_config[pickerName]
if (!this.picker_config[pickerName]) { if (!this.picker_config[pickerName]) {
this.picker_config[pickerName] = { options: [] }; this.picker_config[pickerName] = { options: [] };
@ -791,14 +934,19 @@
const options = this.picker_config[pickerName].options || []; const options = this.picker_config[pickerName].options || [];
const option = options.find(opt => String(opt.value) === String(value)); const option = options.find(opt => String(opt.value) === String(value));
let textValue;
if (option) { if (option) {
this.picker_config[pickerName].text = option.text; textValue = option.text;
} else if (defaultText) { } else if (defaultText) {
// 使 // 使
this.picker_config[pickerName].text = defaultText; textValue = defaultText;
} else { } else {
this.picker_config[pickerName].text = '点击选择'; textValue = '点击选择';
} }
//
this._valueTextMapping[cacheKey] = textValue;
this.picker_config[pickerName].text = textValue;
}, },
// //

Loading…
Cancel
Save