Browse Source

修改 bug

master
王泽彦 11 months ago
parent
commit
2324c32458
  1. 45
      niucloud/app/api/controller/apiController/StudentManager.php
  2. 1
      niucloud/app/api/route/route.php
  3. 27
      niucloud/app/service/api/apiService/StudentService.php
  4. 1
      niucloud/app/service/api/student/StudentService.php
  5. 1614
      uniapp/api/apiRoute.js
  6. 46
      uniapp/components/client-info-card/client-info-card.vue
  7. 911
      uniapp/components/order-list-card/index.vue
  8. 207
      uniapp/components/order-list-card/qrcode-payment-dialog.vue
  9. 274
      uniapp/components/schedule/ScheduleDetail.vue
  10. 49
      uniapp/components/student-edit-popup/student-edit-popup.less
  11. 70
      uniapp/components/student-edit-popup/student-edit-popup.vue
  12. 24
      uniapp/components/student-info-card/student-info-card.vue
  13. 62
      uniapp/pages-coach/coach/schedule/schedule_table.vue
  14. 1
      uniapp/pages-market/clue/class_arrangement_detail.vue
  15. 718
      uniapp/pages-market/clue/clue_info.vue
  16. 201
      uniapp/pages-market/clue/edit_clues.vue

45
niucloud/app/api/controller/apiController/StudentManager.php

@ -147,4 +147,47 @@ class StudentManager extends BaseApiService
return fail('添加学员失败:' . $e->getMessage()); return fail('添加学员失败:' . $e->getMessage());
} }
} }
}
/**
* 获取学员基本信息(员工端)
* @param int $id 学员ID
* @return \think\Response
*/
public function info($id)
{
// 验证参数
if (empty($id) || !is_numeric($id)) {
return fail('学员ID无效');
}
try {
// 直接查询学员基本信息,不需要权限验证
$student = \think\facade\Db::table('school_student')
->where('id', $id)
->where('deleted_at', 0)
->find();
if (!$student) {
return fail('学员信息不存在');
}
// 返回学员基本信息
$studentInfo = [
'id' => $student['id'],
'name' => $student['name'],
'gender' => $student['gender'],
'gender_text' => $student['gender'] == 1 ? '男' : '女',
'birthday' => $student['birthday'],
'campus_id' => $student['campus_id'] ?? null,
'headimg' => $student['headimg'] ? get_image_url($student['headimg']) : '',
'emergency_contact' => $student['emergency_contact'],
'contact_phone' => $student['contact_phone']
];
return success($studentInfo, '获取学员信息成功');
} catch (\Exception $e) {
return fail('获取学员信息失败:' . $e->getMessage());
}
}
}

1
niucloud/app/api/route/route.php

@ -277,6 +277,7 @@ Route::group(function () {
Route::post('student/edit', 'apiController.StudentManager/edit'); Route::post('student/edit', 'apiController.StudentManager/edit');
//销售端-学员-列表 //销售端-学员-列表
Route::get('student/list', 'apiController.StudentManager/list'); Route::get('student/list', 'apiController.StudentManager/list');
Route::get('student/info/:id', 'apiController.StudentManager/info');
//教练端-学员-我的学员列表 //教练端-学员-我的学员列表
Route::get('coach/students/my', 'apiController.CoachStudent/getMyStudents'); Route::get('coach/students/my', 'apiController.CoachStudent/getMyStudents');

27
niucloud/app/service/api/apiService/StudentService.php

@ -64,7 +64,7 @@ class StudentService extends BaseApiService
// 插入数据库 // 插入数据库
$id = Db::table('school_student')->insertGetId($insertData); $id = Db::table('school_student')->insertGetId($insertData);
if ($id) { if ($id) {
$res['code'] = 1; $res['code'] = 1;
$res['msg'] = '添加成功'; $res['msg'] = '添加成功';
@ -115,7 +115,7 @@ class StudentService extends BaseApiService
if (!empty($data['name'])) { if (!empty($data['name'])) {
$where[] = ['s.name', 'like', '%' . $data['name'] . '%']; $where[] = ['s.name', 'like', '%' . $data['name'] . '%'];
} }
if (!empty($data['phone'])) { if (!empty($data['phone'])) {
$where[] = ['s.contact_phone', 'like', '%' . $data['phone'] . '%']; $where[] = ['s.contact_phone', 'like', '%' . $data['phone'] . '%'];
} }
@ -146,8 +146,7 @@ class StudentService extends BaseApiService
// 查询该学员的课程安排记录,按日期排序获取一访和二访信息 // 查询该学员的课程安排记录,按日期排序获取一访和二访信息
$visitRecords = Db::table('school_person_course_schedule') $visitRecords = Db::table('school_person_course_schedule')
->where([ ->where([
['person_id', '=', $student['id']], ['student_id', '=', $student['id']]
['person_type', '=', 'student']
]) ])
->order('course_date', 'asc') ->order('course_date', 'asc')
->select() ->select()
@ -197,27 +196,27 @@ class StudentService extends BaseApiService
// 如果指定了parent_resource_id,则查询该资源下的学生 // 如果指定了parent_resource_id,则查询该资源下的学生
if (!empty($data['parent_resource_id'])) { if (!empty($data['parent_resource_id'])) {
$resourceId = $data['parent_resource_id']; $resourceId = $data['parent_resource_id'];
// 验证当前用户是否有权限访问该资源 // 验证当前用户是否有权限访问该资源
$resource = Db::table('school_customer_resources') $resource = Db::table('school_customer_resources')
->where('id', $resourceId) ->where('id', $resourceId)
->where('consultant', $userId) ->where('consultant', $userId)
->where('deleted_at', 0) ->where('deleted_at', 0)
->find(); ->find();
if (empty($resource)) { if (empty($resource)) {
// 用户无权限访问该资源 // 用户无权限访问该资源
return []; return [];
} }
// 查询该资源关联的学生ID // 查询该资源关联的学生ID
$studentIds = Db::table('school_student_courses') $studentIds = Db::table('school_student_courses')
->where('resource_id', $resourceId) ->where('resource_id', $resourceId)
->column('student_id'); ->column('student_id');
return array_values(array_unique($studentIds)); return array_values(array_unique($studentIds));
} }
// 如果没有指定资源ID,则查询当前用户(教练)负责的所有学生 // 如果没有指定资源ID,则查询当前用户(教练)负责的所有学生
return $this->getCoachStudentIds($userId, $data); return $this->getCoachStudentIds($userId, $data);
} }
@ -305,13 +304,13 @@ class StudentService extends BaseApiService
if (empty($token)) { if (empty($token)) {
return 0; return 0;
} }
// 去掉Bearer前缀 // 去掉Bearer前缀
$token = str_replace('Bearer ', '', $token); $token = str_replace('Bearer ', '', $token);
// 使用项目的TokenAuth类解析token,类型为personnel(员工端) // 使用项目的TokenAuth类解析token,类型为personnel(员工端)
$tokenInfo = \core\util\TokenAuth::parseToken($token, 'personnel'); $tokenInfo = \core\util\TokenAuth::parseToken($token, 'personnel');
if (!empty($tokenInfo)) { if (!empty($tokenInfo)) {
// 从jti中提取用户ID(格式:用户ID_类型) // 从jti中提取用户ID(格式:用户ID_类型)
$jti = $tokenInfo['jti'] ?? ''; $jti = $tokenInfo['jti'] ?? '';
@ -322,7 +321,7 @@ class StudentService extends BaseApiService
} }
} }
} }
return 0; return 0;
} catch (\Exception $e) { } catch (\Exception $e) {
return 0; return 0;
@ -363,4 +362,4 @@ class StudentService extends BaseApiService
return $res; return $res;
} }
} }

1
niucloud/app/service/api/student/StudentService.php

@ -184,6 +184,7 @@ class StudentService extends BaseService
'contact_phone' => $student['contact_phone'], 'contact_phone' => $student['contact_phone'],
'note' => $student['note'], 'note' => $student['note'],
'headimg' => $student['headimg'] ? get_image_url($student['headimg']) : '', 'headimg' => $student['headimg'] ? get_image_url($student['headimg']) : '',
'campus_id' => $student['campus_id'] ?? null, // 添加校区ID
]; ];
// 处理体测信息 // 处理体测信息

1614
uniapp/api/apiRoute.js

File diff suppressed because it is too large

46
uniapp/components/client-info-card/client-info-card.vue

@ -22,7 +22,7 @@
</view> </view>
</view> </view>
</view> </view>
<!-- 客户详细信息 --> <!-- 客户详细信息 -->
<view class="customer-details"> <view class="customer-details">
<view class="detail-row"> <view class="detail-row">
@ -35,19 +35,19 @@
</view> </view>
<view class="detail-row"> <view class="detail-row">
<text class="info-label">分配顾问:</text> <text class="info-label">分配顾问:</text>
<text class="info-value">{{ $util.safeGet(clientInfo, 'customerResource.consultant_name', '未知顾问') }}</text> <text class="info-value">{{ $util.safeGet(clientInfo, 'customerResource.consultant_name', '---') }}</text>
</view> </view>
<view class="detail-row"> <view class="detail-row">
<text class="info-label">性别:</text> <text class="info-label">性别:</text>
<text class="info-value">{{ $util.safeGet(clientInfo, 'customerResource.gender_name', '未知性别') }}</text> <text class="info-value">{{ $util.safeGet(clientInfo, 'customerResource.gender_name', '未知性别') }}</text>
</view> </view>
</view> </view>
<!-- 操作按钮区域 --> <!-- 操作按钮区域 -->
<view class="action-panel" v-if="actionsExpanded && actions && actions.length > 0"> <view class="action-panel" v-if="actionsExpanded && actions && actions.length > 0">
<view <view
class="action-btn" class="action-btn"
v-for="action in actions" v-for="action in actions"
:key="action.key" :key="action.key"
@click="handleAction(action)" @click="handleAction(action)"
> >
@ -82,11 +82,11 @@ export default {
this.$util.makePhoneCall(phoneNumber) this.$util.makePhoneCall(phoneNumber)
this.$emit('call', phoneNumber) this.$emit('call', phoneNumber)
}, },
toggleActions() { toggleActions() {
this.actionsExpanded = !this.actionsExpanded this.actionsExpanded = !this.actionsExpanded
}, },
handleAction(action) { handleAction(action) {
this.$emit('action', { action, client: this.clientInfo }) this.$emit('action', { action, client: this.clientInfo })
}, },
@ -114,7 +114,7 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 30rpx; margin-bottom: 30rpx;
.customer-avatar { .customer-avatar {
width: 80rpx; width: 80rpx;
height: 80rpx; height: 80rpx;
@ -124,24 +124,24 @@ export default {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-right: 20rpx; margin-right: 20rpx;
text { text {
color: white; color: white;
font-size: 32rpx; font-size: 32rpx;
font-weight: bold; font-weight: bold;
} }
} }
.customer-info { .customer-info {
flex: 1; flex: 1;
.customer-name { .customer-name {
color: white; color: white;
font-size: 32rpx; font-size: 32rpx;
font-weight: bold; font-weight: bold;
margin-bottom: 8rpx; margin-bottom: 8rpx;
} }
.customer-meta { .customer-meta {
.customer-phone { .customer-phone {
color: #999; color: #999;
@ -149,12 +149,12 @@ export default {
} }
} }
} }
.contact-actions { .contact-actions {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 15rpx; gap: 15rpx;
.contact-btn { .contact-btn {
width: 60rpx; width: 60rpx;
height: 60rpx; height: 60rpx;
@ -163,15 +163,15 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.contact-icon { .contact-icon {
font-size: 28rpx; font-size: 28rpx;
} }
} }
.action-toggle { .action-toggle {
padding: 10rpx; padding: 10rpx;
.toggle-icon { .toggle-icon {
color: #29d3b4; color: #29d3b4;
font-size: 24rpx; font-size: 24rpx;
@ -186,13 +186,13 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 20rpx; margin-bottom: 20rpx;
.info-label { .info-label {
color: #999; color: #999;
font-size: 24rpx; font-size: 24rpx;
width: 150rpx; width: 150rpx;
} }
.info-value { .info-value {
color: white; color: white;
font-size: 24rpx; font-size: 24rpx;
@ -208,12 +208,12 @@ export default {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 15rpx; gap: 15rpx;
.action-btn { .action-btn {
padding: 15rpx 25rpx; padding: 15rpx 25rpx;
background-color: #29d3b4; background-color: #29d3b4;
border-radius: 25rpx; border-radius: 25rpx;
.action-text { .action-text {
color: white; color: white;
font-size: 22rpx; font-size: 22rpx;
@ -221,4 +221,4 @@ export default {
} }
} }
} }
</style> </style>

911
uniapp/components/order-list-card/index.vue

File diff suppressed because it is too large

207
uniapp/components/order-list-card/qrcode-payment-dialog.vue

@ -0,0 +1,207 @@
<!--二维码支付弹窗组件-->
<template>
<view class="qrcode-payment-modal">
<!-- 弹窗头部 -->
<view class="modal-header">
<text class="modal-title">扫码支付</text>
<view class="close-btn" @click="handleClose">
<text>✕</text>
</view>
</view>
<!-- 订单信息 -->
<view class="order-info">
<view class="info-row">
<text class="label">订单号:</text>
<text class="value">{{ paymentData.order.order_no }}</text>
</view>
<view class="info-row">
<text class="label">支付金额:</text>
<text class="amount">¥{{ paymentData.order.total_amount }}</text>
</view>
</view>
<!-- 二维码区域 -->
<view class="qrcode-container">
<image
v-if="paymentData.qrcodeImage"
:src="paymentData.qrcodeImage"
class="qrcode-image"
mode="aspectFit"
/>
<text v-else class="qrcode-placeholder">二维码加载中...</text>
<text class="qrcode-tip">请使用微信扫码完成支付</text>
</view>
<!-- 操作按钮 -->
<view class="modal-buttons">
<view class="btn secondary" @click="handleClose">取消支付</view>
<view class="btn primary" @click="handleConfirm">确认已支付</view>
</view>
</view>
</template>
<script>
export default {
name: 'QRCodePaymentDialog',
props: {
paymentData: {
type: Object,
required: true
}
},
methods: {
handleClose() {
this.$emit('close')
},
handleConfirm() {
this.$emit('confirm')
}
}
}
</script>
<style lang="scss" scoped>
.qrcode-payment-modal {
background: #2A2A2A;
border-radius: 24rpx;
padding: 48rpx;
width: 600rpx;
max-width: 90vw;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40rpx;
}
.modal-title {
font-size: 36rpx;
font-weight: 600;
color: #ffffff;
}
.close-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
font-size: 32rpx;
color: #ffffff;
transition: all 0.3s ease;
&:active {
background: rgba(255, 255, 255, 0.2);
}
}
.order-info {
background: rgba(255, 255, 255, 0.05);
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 40rpx;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
}
.label {
font-size: 28rpx;
color: #999999;
}
.value {
font-size: 28rpx;
color: #ffffff;
}
.amount {
font-size: 40rpx;
font-weight: 600;
color: #FFC107;
}
.qrcode-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40rpx;
background: #ffffff;
border-radius: 16rpx;
margin-bottom: 40rpx;
}
.qrcode-image {
width: 400rpx;
height: 400rpx;
margin-bottom: 24rpx;
}
.qrcode-placeholder {
width: 400rpx;
height: 400rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #999999;
margin-bottom: 24rpx;
}
.qrcode-tip {
font-size: 26rpx;
color: #666666;
text-align: center;
}
.modal-buttons {
display: flex;
gap: 24rpx;
}
.btn {
flex: 1;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 44rpx;
font-size: 30rpx;
font-weight: 500;
transition: all 0.3s ease;
&.secondary {
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
&:active {
background: rgba(255, 255, 255, 0.15);
}
}
&.primary {
background: #29D3B4;
color: #ffffff;
&:active {
background: #1fb396;
}
}
}
</style>

274
uniapp/components/schedule/ScheduleDetail.vue

@ -1,15 +1,20 @@
<template> <template>
<fui-modal :show="visible" width="700" @cancel="closePopup" :buttons="[{text: '关闭', type: 'default'}]" :showClose="true" @close="closePopup" @click="handleModalClick"> <!-- 组件根容器 - Vue2要求只有一个根元素 -->
<!-- 自定义关闭按钮 --> <view>
<template #header> <!-- 自定义弹窗遮罩 -->
<view class="modal-mask" v-if="visible" @click="closePopup">
<!-- 弹窗内容 -->
<view class="modal-container" @click.stop>
<!-- 自定义头部 -->
<view class="custom-header"> <view class="custom-header">
<text class="modal-title">课程安排详情</text> <text class="modal-title">课程安排详情</text>
<view class="close-btn" @click="closePopup"> <view class="close-btn" @click="closePopup">
<text class="close-icon">✕</text> <text class="close-icon">✕</text>
</view> </view>
</view> </view>
</template>
<view class="schedule-detail" v-if="scheduleInfo"> <!-- 弹窗主体内容 -->
<view class="schedule-detail" v-if="scheduleInfo">
<!-- 课程基本信息 --> <!-- 课程基本信息 -->
<view class="section basic-info"> <view class="section basic-info">
<view class="section-title">基本信息</view> <view class="section-title">基本信息</view>
@ -186,22 +191,33 @@
</view> </view>
</view> </view>
</view> </view>
<view class="loading" v-if="loading && !scheduleInfo"> <!-- 加载状态 -->
<fui-loading></fui-loading> <view class="loading" v-if="loading && !scheduleInfo">
<text class="loading-text">加载中...</text> <fui-loading></fui-loading>
</view> <text class="loading-text">加载中...</text>
</view>
<view class="error-message" v-if="error && !scheduleInfo"> <!-- 错误信息 -->
<text>{{ errorMessage }}</text> <view class="error-message" v-if="error && !scheduleInfo">
<view class="retry-btn" @click="fetchScheduleDetail"> <text>{{ errorMessage }}</text>
<text>重试</text> <view class="retry-btn" @click="fetchScheduleDetail">
<text>重试</text>
</view>
</view>
<!-- 底部关闭按钮 -->
<view class="modal-footer">
<view class="footer-btn close-footer-btn" @click="closePopup">
<text class="btn-text">关闭</text>
</view>
</view> </view>
</view> </view>
</view>
<!-- 学员点名底部弹窗 --> <!-- 学员点名底部弹窗 -->
<fui-modal :show="showAttendanceModal" title="学员点名" @cancel="closeAttendanceModal" :buttons="[]"> <fui-modal :show="showAttendanceModal" title="学员点名" @cancel="closeAttendanceModal" :buttons="[]" :zIndex="10001">
<view class="attendance-modal" v-if="selectedStudent"> <view class="attendance-modal" v-if="selectedStudent">
<view class="student-info"> <view class="student-info">
<view class="student-avatar-large"> <view class="student-avatar-large">
@ -226,7 +242,7 @@
</fui-modal> </fui-modal>
<!-- 升级确认弹窗 --> <!-- 升级确认弹窗 -->
<fui-modal :show="showUpgradeConfirm" title="升级确认" @cancel="cancelUpgrade" :buttons="[]" :zIndex="10000"> <fui-modal :show="showUpgradeConfirm" title="升级确认" @cancel="cancelUpgrade" :buttons="[]" :zIndex="10002">
<view class="upgrade-confirm-modal" v-if="upgradeStudent"> <view class="upgrade-confirm-modal" v-if="upgradeStudent">
<view class="confirm-content"> <view class="confirm-content">
<view class="upgrade-icon">⚡</view> <view class="upgrade-icon">⚡</view>
@ -250,7 +266,7 @@
</fui-modal> </fui-modal>
<!-- 删除课程安排确认弹窗 --> <!-- 删除课程安排确认弹窗 -->
<fui-modal :show="showDeleteSchedulesModal" title="删除确认" @cancel="closeDeleteSchedulesModal" :buttons="[]" :zIndex="10001"> <fui-modal :show="showDeleteSchedulesModal" title="删除确认" @cancel="closeDeleteSchedulesModal" :buttons="[]" :zIndex="10003">
<view class="delete-confirm-modal" v-if="studentToDelete"> <view class="delete-confirm-modal" v-if="studentToDelete">
<view class="confirm-content"> <view class="confirm-content">
<view class="error-icon">⚠️</view> <view class="error-icon">⚠️</view>
@ -275,7 +291,7 @@
</view> </view>
</view> </view>
</fui-modal> </fui-modal>
</fui-modal> </view>
</template> </template>
<script> <script>
@ -296,12 +312,20 @@
computed: { computed: {
// 分离正式学员和等待位学员 // 分离正式学员和等待位学员
formalStudents() { formalStudents() {
if (!this.scheduleInfo || !this.scheduleInfo.students) return []; if (!this.scheduleInfo) return [];
return this.scheduleInfo.students.filter(student => student.schedule_type === 1 || student.schedule_type === null); const students = this.scheduleInfo.formal_students;
if (!students || !Array.isArray(students)) {
return [];
}
return students;
}, },
waitingStudents() { waitingStudents() {
if (!this.scheduleInfo || !this.scheduleInfo.students) return []; if (!this.scheduleInfo) return [];
return this.scheduleInfo.students.filter(student => student.schedule_type === 2); const students = this.scheduleInfo.waiting_students;
if (!students || !Array.isArray(students)) {
return [];
}
return students;
}, },
statusClass() { statusClass() {
const statusMap = { const statusMap = {
@ -369,24 +393,12 @@
if (newVal && this.scheduleId) { if (newVal && this.scheduleId) {
this.fetchScheduleDetail(); this.fetchScheduleDetail();
} }
},
scheduleId(newVal, oldVal) {
// 只有在弹窗可见且scheduleId真正发生变化时才重新获取数据
if (newVal && this.visible && newVal !== oldVal) {
this.fetchScheduleDetail();
}
} }
// 注意: 已移除scheduleId的watch监听,避免与visible的watch重复触发
// 父组件总是先设置scheduleId再设置visible=true,所以只需监听visible即可
}, },
methods: { methods: {
// 处理弹窗按钮点击 // 获取课程安排详情(使用统一API - 对接admin端)
handleModalClick(e) {
// 如果点击的是关闭按钮,关闭弹窗
if (e.index === 0) {
this.closePopup();
}
},
// 获取课程安排详情(使用统一API - 对接admin端)
async fetchScheduleDetail() { async fetchScheduleDetail() {
if (!this.scheduleId) { if (!this.scheduleId) {
this.error = true; this.error = true;
@ -410,8 +422,33 @@
// 使用新的统一API数据结构,与admin端保持一致 // 使用新的统一API数据结构,与admin端保持一致
if (data.schedule_info) { if (data.schedule_info) {
// 处理新的统一数据结构(包含schedule_info、formal_students、waiting_students) // 处理正式学员数据
const allStudents = [...(data.formal_students || []), ...(data.waiting_students || [])]; const processStudents = (students) => {
return (students || []).map(student => ({
...student,
status_text: this.getStatusText(student.status || 0),
// 确保包含课程进度数据
course_progress: student.course_progress || {
total: student.totalHours || 0,
used: student.usedHours || 0,
remaining: student.remainingHours || 0,
percentage: student.totalHours > 0 ? Math.round((student.usedHours / student.totalHours) * 100) : 0
},
// 确保包含续费和体验课标识
needsRenewal: student.needsRenewal || false,
isTrialStudent: student.isTrialStudent || student.person_type !== 'student',
// 确保包含课程状态和类型
courseStatus: student.courseStatus || (student.person_type === 'student' ? '正式课' : '体验课'),
courseType: student.schedule_type === 2 ? 'waiting' : 'formal',
// 确保包含年龄信息
age: student.age || 0,
// 确保包含体验课时信息
trialClassCount: student.trialClassCount || 0,
// 确保包含剩余课时和到期时间
remainingHours: student.remainingHours || student.course_progress?.remaining || 0,
expiryDate: student.expiryDate || ''
}));
};
this.scheduleInfo = { this.scheduleInfo = {
// 基本课程信息从schedule_info获取 // 基本课程信息从schedule_info获取
@ -433,8 +470,20 @@
time_info: data.schedule_info.time_info || null, time_info: data.schedule_info.time_info || null,
// 课程时长 // 课程时长
course_duration: data.schedule_info.time_info?.duration || data.schedule_info.course_duration || 60, course_duration: data.schedule_info.time_info?.duration || data.schedule_info.course_duration || 60,
// 合并正式学员和等待位学员数据 // 分别存储正式学员和等待位学员
students: allStudents.map(student => ({ formal_students: processStudents(data.formal_students),
waiting_students: processStudents(data.waiting_students),
// 其他信息
available_capacity: data.schedule_info.available_capacity || 0,
enrolled_count: (data.formal_students?.length || 0) + (data.waiting_students?.length || 0),
remaining_capacity: data.schedule_info.remaining_capacity || 0
};
console.log('课程安排详情加载成功:', this.scheduleInfo);
} else {
// 兼容旧的数据结构
const processStudents = (students) => {
return (students || []).map(student => ({
...student, ...student,
status_text: this.getStatusText(student.status || 0), status_text: this.getStatusText(student.status || 0),
// 确保包含课程进度数据 // 确保包含课程进度数据
@ -446,7 +495,7 @@
}, },
// 确保包含续费和体验课标识 // 确保包含续费和体验课标识
needsRenewal: student.needsRenewal || false, needsRenewal: student.needsRenewal || false,
isTrialStudent: student.isTrialStudent || student.person_type !== 'student', isTrialStudent: student.isTrialStudent || false,
// 确保包含课程状态和类型 // 确保包含课程状态和类型
courseStatus: student.courseStatus || (student.person_type === 'student' ? '正式课' : '体验课'), courseStatus: student.courseStatus || (student.person_type === 'student' ? '正式课' : '体验课'),
courseType: student.schedule_type === 2 ? 'waiting' : 'formal', courseType: student.schedule_type === 2 ? 'waiting' : 'formal',
@ -455,16 +504,16 @@
// 确保包含体验课时信息 // 确保包含体验课时信息
trialClassCount: student.trialClassCount || 0, trialClassCount: student.trialClassCount || 0,
// 确保包含剩余课时和到期时间 // 确保包含剩余课时和到期时间
remainingHours: student.remainingHours || student.course_progress?.remaining || 0, remainingHours: student.remainingHours || 0,
expiryDate: student.expiryDate || '' expiryDate: student.expiryDate || ''
})), }));
// 其他信息
available_capacity: data.available_capacity || 0,
enrolled_count: allStudents.length,
remaining_capacity: data.remaining_capacity || 0
}; };
} else {
// 兼容旧的数据结构 // 如果是旧结构但有students数组,需要分离正式学员和等待位学员
const allStudents = data.students || [];
const formalStudents = allStudents.filter(s => s.schedule_type === 1 || s.schedule_type === null);
const waitingStudents = allStudents.filter(s => s.schedule_type === 2);
this.scheduleInfo = { this.scheduleInfo = {
// 基本课程信息 // 基本课程信息
id: data.id, id: data.id,
@ -485,34 +534,12 @@
time_info: data.time_info || null, time_info: data.time_info || null,
// 课程时长 // 课程时长
course_duration: data.time_info?.duration || data.course_duration || 60, course_duration: data.time_info?.duration || data.course_duration || 60,
// 学员数据 // 分别存储正式学员和等待位学员
students: (data.students || []).map(student => ({ formal_students: processStudents(formalStudents),
...student, waiting_students: processStudents(waitingStudents),
status_text: this.getStatusText(student.status || 0),
// 确保包含课程进度数据
course_progress: student.course_progress || {
total: student.totalHours || 0,
used: student.usedHours || 0,
remaining: student.remainingHours || 0,
percentage: student.totalHours > 0 ? Math.round((student.usedHours / student.totalHours) * 100) : 0
},
// 确保包含续费和体验课标识
needsRenewal: student.needsRenewal || false,
isTrialStudent: student.isTrialStudent || false,
// 确保包含课程状态和类型
courseStatus: student.courseStatus || (student.person_type === 'student' ? '正式课' : '体验课'),
courseType: student.schedule_type === 2 ? 'waiting' : 'formal',
// 确保包含年龄信息
age: student.age || 0,
// 确保包含体验课时信息
trialClassCount: student.trialClassCount || 0,
// 确保包含剩余课时和到期时间
remainingHours: student.remainingHours || 0,
expiryDate: student.expiryDate || ''
})),
// 其他信息 // 其他信息
available_capacity: data.available_capacity || 0, available_capacity: data.available_capacity || 0,
enrolled_count: data.enrolled_count || 0, enrolled_count: allStudents.length,
remaining_capacity: data.remaining_capacity || 0 remaining_capacity: data.remaining_capacity || 0
}; };
} }
@ -562,7 +589,11 @@
// 学员点击处理 // 学员点击处理
handleStudentClick(student, index) { handleStudentClick(student, index) {
console.log('点击了学员:', student) console.log('=== 学员点击事件触发 ===');
console.log('学员信息:', student);
console.log('学员索引:', index);
console.log('学员状态:', student.status);
// 检查学员签到状态,只有状态为0(待上课)才能进行签到操作 // 检查学员签到状态,只有状态为0(待上课)才能进行签到操作
if (student.status !== 0) { if (student.status !== 0) {
uni.showToast({ uni.showToast({
@ -575,13 +606,18 @@
// 检查是否是等待位学员 // 检查是否是等待位学员
if (student.schedule_type === 2) { if (student.schedule_type === 2) {
console.log('等待位学员,弹出升级确认');
// 等待位学员 - 询问是否转为正式课 // 等待位学员 - 询问是否转为正式课
this.handleWaitingStudentClick(student, index); this.handleWaitingStudentClick(student, index);
} else { } else {
console.log('正式学员,弹出签到弹窗');
console.log('设置 selectedStudent:', student);
console.log('设置 selectedStudentIndex:', index);
// 正式学员 - 进行签到/请假操作 // 正式学员 - 进行签到/请假操作
this.selectedStudent = student; this.selectedStudent = student;
this.selectedStudentIndex = index; this.selectedStudentIndex = index;
this.showAttendanceModal = true; this.showAttendanceModal = true;
console.log('showAttendanceModal 已设置为:', this.showAttendanceModal);
} }
}, },
@ -796,8 +832,14 @@
this.selectedStudent.statusClass = this.getStudentStatusClass(actionMap[action].status); this.selectedStudent.statusClass = this.getStudentStatusClass(actionMap[action].status);
// 更新scheduleInfo中的学员信息 // 更新scheduleInfo中的学员信息
if (this.scheduleInfo && this.scheduleInfo.students && this.selectedStudentIndex >= 0) { if (this.scheduleInfo) {
this.$set(this.scheduleInfo.students, this.selectedStudentIndex, this.selectedStudent); // 判断是正式学员还是等待位学员
const isFormal = this.selectedStudent.schedule_type === 1 || this.selectedStudent.schedule_type === null;
const targetArray = isFormal ? this.scheduleInfo.formal_students : this.scheduleInfo.waiting_students;
if (targetArray && this.selectedStudentIndex >= 0) {
this.$set(targetArray, this.selectedStudentIndex, this.selectedStudent);
}
} }
// 发射事件给父组件 // 发射事件给父组件
@ -949,6 +991,38 @@
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 自定义弹窗样式 */
.modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
padding: 20rpx;
}
.modal-container {
width: 100%;
max-width: 700rpx;
max-height: 90vh;
background-color: #1a1a1a;
border-radius: 16rpx;
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.5);
}
/* 移除 fui-modal 的默认 padding */
::v-deep .fui-modal__body {
padding: 0 !important;
}
/* 自定义头部样式 */ /* 自定义头部样式 */
.custom-header { .custom-header {
display: flex; display: flex;
@ -957,6 +1031,7 @@
padding: 20rpx 30rpx; padding: 20rpx 30rpx;
background: #2a2a2a; background: #2a2a2a;
border-bottom: 1px solid #3a3a3a; border-bottom: 1px solid #3a3a3a;
flex-shrink: 0;
} }
.modal-title { .modal-title {
@ -994,7 +1069,7 @@
.schedule-detail { .schedule-detail {
padding: 20rpx; padding: 20rpx;
max-height: 80vh; flex: 1;
overflow-y: auto; overflow-y: auto;
position: relative; position: relative;
} }
@ -1724,4 +1799,43 @@
box-shadow: 0 2rpx 8rpx rgba(239, 68, 68, 0.4); box-shadow: 0 2rpx 8rpx rgba(239, 68, 68, 0.4);
} }
} }
/* 弹窗底部样式 */
.modal-footer {
padding: 20rpx 30rpx;
background: #2a2a2a;
border-top: 1px solid #3a3a3a;
flex-shrink: 0;
display: flex;
justify-content: center;
}
.footer-btn {
padding: 16rpx 60rpx;
border-radius: 8rpx;
cursor: pointer;
transition: all 0.3s ease;
.btn-text {
font-size: 28rpx;
font-weight: 600;
}
&:active {
transform: scale(0.98);
}
}
.close-footer-btn {
background: #4a4a4a;
border: 1px solid #666;
.btn-text {
color: #ccc;
}
&:hover {
background: #5a5a5a;
}
}
</style> </style>

49
uniapp/components/student-edit-popup/student-edit-popup.less

@ -81,7 +81,54 @@
.form-input { .form-input {
flex: 1; flex: 1;
// 头像上传样式
.avatar-upload {
width: 160rpx;
height: 160rpx;
border-radius: 50%;
overflow: hidden;
border: 2rpx solid #e9ecef;
background: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
&:active {
background: #e9ecef;
transform: scale(0.95);
}
.avatar-preview {
width: 100%;
height: 100%;
border-radius: 50%;
}
.avatar-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
.avatar-icon {
font-size: 48rpx;
color: #29d3b4;
margin-bottom: 8rpx;
font-weight: 300;
}
.avatar-text {
font-size: 22rpx;
color: #999;
}
}
}
input { input {
width: 100%; width: 100%;
height: 80rpx; height: 80rpx;

70
uniapp/components/student-edit-popup/student-edit-popup.vue

@ -13,7 +13,20 @@
<!-- 基本信息 --> <!-- 基本信息 -->
<view class="form-group"> <view class="form-group">
<view class="form-group-title">基本信息</view> <view class="form-group-title">基本信息</view>
<view class="form-item">
<view class="form-label">头像</view>
<view class="form-input">
<view class="avatar-upload" @click="chooseAvatar">
<image v-if="studentData.headimg" :src="studentData.headimg" class="avatar-preview" mode="aspectFill"></image>
<view v-else class="avatar-placeholder">
<text class="avatar-icon">+</text>
<text class="avatar-text">上传头像</text>
</view>
</view>
</view>
</view>
<view class="form-item"> <view class="form-item">
<view class="form-label required">姓名</view> <view class="form-label required">姓名</view>
<view class="form-input"> <view class="form-input">
@ -151,6 +164,7 @@
<script> <script>
import apiRoute from '@/api/apiRoute.js' import apiRoute from '@/api/apiRoute.js'
import { uploadFile } from '@/common/util.js'
export default { export default {
name: 'StudentEditPopup', name: 'StudentEditPopup',
@ -183,7 +197,8 @@ export default {
consultant_id: null, consultant_id: null,
coach_id: null, coach_id: null,
trial_class_count: 2, // 体验课次数|默认2(新增学员赠送) trial_class_count: 2, // 体验课次数|默认2(新增学员赠送)
actionsExpanded: false // 操作面板展开状态 actionsExpanded: false, // 操作面板展开状态
headimg: '' // 学生头像URL
}, },
// 选项数据 // 选项数据
@ -258,7 +273,8 @@ export default {
consultant_id: student.consultant_id, consultant_id: student.consultant_id,
coach_id: student.coach_id, coach_id: student.coach_id,
trial_class_count: student.trial_class_count, trial_class_count: student.trial_class_count,
actionsExpanded: student.actionsExpanded || false actionsExpanded: student.actionsExpanded || false,
headimg: student.headimg || '' // 头像URL
} }
// 解析已有的标签数据 // 解析已有的标签数据
this.parseExistingTags() this.parseExistingTags()
@ -291,7 +307,8 @@ export default {
consultant_id: null, consultant_id: null,
coach_id: null, coach_id: null,
trial_class_count: 2, trial_class_count: 2,
actionsExpanded: false actionsExpanded: false,
headimg: '' // 头像URL
} }
// 重置标签选择状态 // 重置标签选择状态
this.selectedTagIds = [] this.selectedTagIds = []
@ -378,7 +395,8 @@ export default {
member_label: this.studentData.member_label, member_label: this.studentData.member_label,
consultant_id: this.studentData.consultant_id, consultant_id: this.studentData.consultant_id,
coach_id: this.studentData.coach_id, coach_id: this.studentData.coach_id,
trial_class_count: this.studentData.trial_class_count trial_class_count: this.studentData.trial_class_count,
headimg: this.studentData.headimg // 头像URL
} }
if (this.isEditing) { if (this.isEditing) {
@ -466,7 +484,7 @@ export default {
this.selectedTagIds = this.studentData.member_label.split(',') this.selectedTagIds = this.studentData.member_label.split(',')
.map(id => parseInt(id.trim())) .map(id => parseInt(id.trim()))
.filter(id => !isNaN(id)) .filter(id => !isNaN(id))
// 更新选中的标签名称数组 // 更新选中的标签名称数组
this.selectedTagNames = this.selectedTagIds.map(tagId => { this.selectedTagNames = this.selectedTagIds.map(tagId => {
const tag = this.studentTagOptions.find(t => t.label_id === tagId) const tag = this.studentTagOptions.find(t => t.label_id === tagId)
@ -476,6 +494,46 @@ export default {
this.selectedTagIds = [] this.selectedTagIds = []
this.selectedTagNames = [] this.selectedTagNames = []
} }
},
// 选择头像
chooseAvatar() {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0]
// 显示上传中提示
uni.showLoading({
title: '上传中...',
mask: true
})
// 调用uploadFile方法上传图片
uploadFile(
tempFilePath,
(fileData) => {
// 上传成功,fileData包含url、extname、name等信息
this.studentData.headimg = fileData.url
uni.hideLoading()
uni.showToast({
title: '头像上传成功',
icon: 'success'
})
},
(error) => {
// 上传失败
uni.hideLoading()
console.error('头像上传失败:', error)
}
)
},
fail: (err) => {
console.error('选择图片失败:', err)
}
})
} }
} }
} }

24
uniapp/components/student-info-card/student-info-card.vue

@ -93,18 +93,18 @@ export default {
// 计算年龄(x岁x月) // 计算年龄(x岁x月)
calculateAge(birthday) { calculateAge(birthday) {
if (!birthday) return '' if (!birthday) return ''
const birthDate = new Date(birthday) const birthDate = new Date(birthday)
const now = new Date() const now = new Date()
let years = now.getFullYear() - birthDate.getFullYear() let years = now.getFullYear() - birthDate.getFullYear()
let months = now.getMonth() - birthDate.getMonth() let months = now.getMonth() - birthDate.getMonth()
if (months < 0) { if (months < 0) {
years-- years--
months += 12 months += 12
} }
// 如果当前日期小于生日的日期,月份减1 // 如果当前日期小于生日的日期,月份减1
if (now.getDate() < birthDate.getDate()) { if (now.getDate() < birthDate.getDate()) {
months-- months--
@ -113,7 +113,7 @@ export default {
months += 12 months += 12
} }
} }
if (years > 0 && months > 0) { if (years > 0 && months > 0) {
return `${years}岁${months}月` return `${years}岁${months}月`
} else if (years > 0) { } else if (years > 0) {
@ -242,19 +242,19 @@ export default {
margin-top: 20rpx; margin-top: 20rpx;
padding-top: 20rpx; padding-top: 20rpx;
border-top: 1rpx solid #333; border-top: 1rpx solid #333;
.info-row { .info-row {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
margin-bottom: 15rpx; margin-bottom: 15rpx;
.info-label { .info-label {
color: #999; color: #999;
font-size: 22rpx; font-size: 22rpx;
width: 150rpx; width: 165rpx;
flex-shrink: 0; flex-shrink: 0;
} }
.info-value { .info-value {
color: white; color: white;
font-size: 22rpx; font-size: 22rpx;
@ -271,12 +271,12 @@ export default {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 15rpx; gap: 15rpx;
.action-btn { .action-btn {
padding: 15rpx 25rpx; padding: 15rpx 25rpx;
background-color: #29d3b4; background-color: #29d3b4;
border-radius: 25rpx; border-radius: 25rpx;
.action-text { .action-text {
color: white; color: white;
font-size: 22rpx; font-size: 22rpx;
@ -284,4 +284,4 @@ export default {
} }
} }
} }
</style> </style>

62
uniapp/pages-coach/coach/schedule/schedule_table.vue

@ -1431,61 +1431,19 @@ export default {
}, },
// 查看课程安排详情(使用统一API) // 查看课程安排详情(使用统一API)
async viewScheduleDetail(scheduleId) { viewScheduleDetail(scheduleId) {
try { if (!scheduleId) {
if (!scheduleId) {
uni.showToast({
title: '课程ID不能为空',
icon: 'none'
});
return;
}
// 显示课程详情弹窗
this.selectedScheduleId = scheduleId;
this.showScheduleDetail = true;
// 使用新的统一API获取课程安排详情,对接admin端功能
console.log('调用统一API获取课程安排详情:', scheduleId);
// 注意:具体的API调用逻辑在ScheduleDetail组件的fetchScheduleDetail方法中实现
// ScheduleDetail组件会调用api.getCourseArrangementDetail()方法,与admin端保持一致
// 这确保了移动端和管理端使用相同的数据结构和业务逻辑
// 预先验证API是否可用(可选的预检查)
try {
const testResponse = await api.getCourseArrangementDetail({
schedule_id: scheduleId
});
if (testResponse.code !== 1) {
console.warn('API预检查警告:', testResponse.msg);
}
} catch (preCheckError) {
console.warn('API预检查失败,将在ScheduleDetail组件中处理:', preCheckError);
}
} catch (error) {
console.error('获取课程安排详情失败:', error);
let errorMessage = '获取课程详情失败';
if (error.message) {
if (error.message.includes('timeout')) {
errorMessage = '请求超时,请重试';
} else if (error.message.includes('Network')) {
errorMessage = '网络连接失败';
}
}
uni.showToast({ uni.showToast({
title: errorMessage, title: '课程ID不能为空',
icon: 'none', icon: 'none'
duration: 3000
}); });
return;
// 即使出错也要显示弹窗,让ScheduleDetail组件处理错误
this.selectedScheduleId = scheduleId;
this.showScheduleDetail = true;
} }
// 直接设置props,让子组件ScheduleDetail负责获取数据
// ScheduleDetail组件会在watch中调用api.getCourseArrangementDetail()
this.selectedScheduleId = scheduleId;
this.showScheduleDetail = true;
}, },
// 处理编辑课程事件 // 处理编辑课程事件

1
uniapp/pages-market/clue/class_arrangement_detail.vue

@ -29,6 +29,7 @@
<view class="student-info"> <view class="student-info">
<view class="student-name">{{ stu.name }}</view> <view class="student-name">{{ stu.name }}</view>
<view class="student-age">年龄:{{ stu.age || '未知' }}岁</view> <view class="student-age">年龄:{{ stu.age || '未知' }}岁</view>
<view class="course-status">课程类型:{{ stu.person_type == 'customer_resource' ? '体验课' : '正式课'}}</view>
<view class="course-status">课程状态:{{ stu.courseStatus }}</view> <view class="course-status">课程状态:{{ stu.courseStatus }}</view>
<view class="course-status">上课情况:{{ stu.course_progress.used }}/{{ stu.course_progress.total }}节</view> <view class="course-status">上课情况:{{ stu.course_progress.used }}/{{ stu.course_progress.total }}节</view>
<view class="expiry-date" v-if="stu.student_course_info">到期时间:{{ stu.student_course_info.end_date || '未设置' }}</view> <view class="expiry-date" v-if="stu.student_course_info">到期时间:{{ stu.student_course_info.end_date || '未设置' }}</view>

718
uniapp/pages-market/clue/clue_info.vue

@ -138,8 +138,12 @@
<StudyPlanCard v-if="currentPopup === 'study_plan'" :plan-list="studyPlanList" @edit="openEditStudyPlan" /> <StudyPlanCard v-if="currentPopup === 'study_plan'" :plan-list="studyPlanList" @edit="openEditStudyPlan" />
<!-- 订单列表弹窗 --> <!-- 订单列表弹窗 -->
<OrderListCard v-if="currentPopup === 'order_list'" :order-list="orderList" @add-order="openAddOrderDialog" <OrderListCard
@pay-order="handlePayOrder" @view-detail="viewOrderDetail" /> v-if="currentPopup === 'order_list'"
:student-id="currentStudent && currentStudent.id"
:resource-id="clientInfo.resource_id"
@payment-success="handlePaymentSuccess"
/>
<!-- 服务列表弹窗 --> <!-- 服务列表弹窗 -->
<ServiceListCard v-if="currentPopup === 'service_list'" :service-list="serviceList" /> <ServiceListCard v-if="currentPopup === 'service_list'" :service-list="serviceList" />
@ -170,57 +174,11 @@
@confirm="handleStudentEditConfirm" /> @confirm="handleStudentEditConfirm" />
<StudyPlanPopup ref="studyPlanPopup" :student-id="currentStudent && currentStudent.id" <StudyPlanPopup ref="studyPlanPopup" :student-id="currentStudent && currentStudent.id"
@confirm="handleStudyPlanConfirm" /> @confirm="handleStudyPlanConfirm" />
<!-- 新增订单弹窗 -->
<uni-popup ref="orderFormPopup" type="bottom">
<OrderFormPopup :visible="showOrderForm" :student-info="currentStudent"
:resource-id="clientInfo.resource_id" @cancel="closeOrderForm" @confirm="handleOrderFormConfirm" />
</uni-popup>
<!-- 二维码支付弹窗 -->
<uni-popup ref="qrCodePopup" type="center" @close="closeQRCodeModal">
<view class="qrcode-payment-modal" v-if="qrCodePaymentData">
<!-- 弹窗头部 -->
<view class="modal-header">
<text class="modal-title">扫码支付</text>
<view class="close-btn" @click="closeQRCodeModal">
<text>✕</text>
</view>
</view>
<!-- 订单信息 -->
<view class="order-info">
<view class="info-row">
<text class="label">订单号:</text>
<text class="value">{{ qrCodePaymentData.order.order_no }}</text>
</view>
<view class="info-row">
<text class="label">支付金额:</text>
<text class="amount">¥{{ qrCodePaymentData.order.total_amount }}</text>
</view>
</view>
<!-- 二维码区域 -->
<view class="qrcode-container">
<image v-if="qrCodePaymentData.qrcodeImage" :src="qrCodePaymentData.qrcodeImage"
class="qrcode-image" mode="aspectFit" />
<text v-else class="qrcode-placeholder">二维码加载中...</text>
<text class="qrcode-tip">请使用微信扫码完成支付</text>
</view>
<!-- 操作按钮 -->
<view class="modal-buttons">
<view class="btn secondary" @click="closeQRCodeModal">取消支付</view>
<view class="btn primary" @click="confirmQRCodePayment">发送二维码给客户</view>
</view>
</view>
</uni-popup>
</view> </view>
</template> </template>
<script> <script>
import apiRoute from '@/api/apiRoute.js' import apiRoute from '@/api/apiRoute.js'
import dictUtilSimple from '@/common/dictUtilSimple.js'
// 组件导入 // 组件导入
import ClientInfoCard from '@/components/client-info-card/client-info-card.vue' import ClientInfoCard from '@/components/client-info-card/client-info-card.vue'
import StudentInfoCard from '@/components/student-info-card/student-info-card.vue' import StudentInfoCard from '@/components/student-info-card/student-info-card.vue'
@ -234,7 +192,6 @@
import CourseInfoCard from '@/components/course-info-card/index.vue' import CourseInfoCard from '@/components/course-info-card/index.vue'
import OrderListCard from '@/components/order-list-card/index.vue' import OrderListCard from '@/components/order-list-card/index.vue'
import ServiceListCard from '@/components/service-list-card/index.vue' import ServiceListCard from '@/components/service-list-card/index.vue'
import OrderFormPopup from '@/components/order-form-popup/index.vue'
// 编辑弹窗 // 编辑弹窗
import StudentEditPopup from '@/components/student-edit-popup/student-edit-popup.vue' import StudentEditPopup from '@/components/student-edit-popup/student-edit-popup.vue'
import FitnessRecordPopup from '@/components/fitness-record-popup/fitness-record-popup.vue' import FitnessRecordPopup from '@/components/fitness-record-popup/fitness-record-popup.vue'
@ -253,7 +210,6 @@
CourseInfoCard, CourseInfoCard,
OrderListCard, OrderListCard,
ServiceListCard, ServiceListCard,
OrderFormPopup,
StudentEditPopup, StudentEditPopup,
FitnessRecordPopup, FitnessRecordPopup,
StudyPlanPopup StudyPlanPopup
@ -281,19 +237,8 @@
// 底部弹窗相关 // 底部弹窗相关
currentPopup: null, currentPopup: null,
studyPlanList: [], studyPlanList: [],
orderList: [],
serviceList: [], serviceList: [],
// 订单表单弹窗
showOrderForm: false,
// 二维码支付弹窗
showQRCodeModal: false,
qrCodePaymentData: null,
// 字典数据
paymentTypeDict: [], // 支付方式字典
// 编辑相关 // 编辑相关
remark_content: '', remark_content: '',
currentRecord: null, currentRecord: null,
@ -412,82 +357,8 @@
}, },
methods: { methods: {
/**
* 加载字典数据
*/
async loadDictData() {
try {
console.log('开始加载支付方式字典数据')
const dictResult = await dictUtilSimple.getBatchDict(['payment_type'])
if (dictResult.payment_type && Array.isArray(dictResult.payment_type)) {
this.paymentTypeDict = dictResult.payment_type
console.log('支付方式字典加载成功:', this.paymentTypeDict)
} else {
console.warn('支付方式字典数据格式不正确:', dictResult.payment_type)
// 使用备用数据
this.paymentTypeDict = [{
name: '现金支付',
value: 'cash'
},
{
name: '扫码支付',
value: 'scan_code'
},
{
name: '订阅支付',
value: 'subscription'
},
{
name: '微信在线代付',
value: 'wxpay_online'
},
{
name: '客户自行付款',
value: 'client_wxpay'
},
{
name: '定金',
value: 'deposit'
}
]
}
} catch (error) {
console.error('加载支付方式字典失败:', error)
// 使用备用数据
this.paymentTypeDict = [{
name: '现金支付',
value: 'cash'
},
{
name: '扫码支付',
value: 'scan_code'
},
{
name: '订阅支付',
value: 'subscription'
},
{
name: '微信在线代付',
value: 'wxpay_online'
},
{
name: '客户自行付款',
value: 'client_wxpay'
},
{
name: '定金',
value: 'deposit'
}
]
}
},
async init() { async init() {
try { try {
// 加载字典数据
await this.loadDictData()
await this.getInfo() await this.getInfo()
await Promise.all([ await Promise.all([
this.getUserInfo(), this.getUserInfo(),
@ -673,7 +544,7 @@
this.currentPopup = 'study_plan' this.currentPopup = 'study_plan'
break break
case 'order_list': case 'order_list':
await this.getOrderList(student.id) // 订单列表由组件内部自动加载,只需打开弹窗
this.currentPopup = 'order_list' this.currentPopup = 'order_list'
break break
case 'service_list': case 'service_list':
@ -688,7 +559,6 @@
this.currentPopup = null this.currentPopup = null
// 重置相关数据 // 重置相关数据
this.studyPlanList = [] this.studyPlanList = []
this.orderList = []
this.serviceList = [] this.serviceList = []
this.courseInfo = [] this.courseInfo = []
this.fitnessRecords = [] this.fitnessRecords = []
@ -1295,574 +1165,12 @@
} }
}, },
// 获取订单列表 /**
async getOrderList(studentId = null) { * 订单支付成功回调
if (!this.clientInfo?.resource_id) return */
handlePaymentSuccess(order) {
try { console.log('订单支付成功:', order)
const targetStudentId = studentId || this.currentStudent?.id // 可以在这里添加支付成功后的业务逻辑,例如刷新其他相关数据
// 构建查询参数,优先使用student_id
const params = {}
if (targetStudentId) {
params.student_id = targetStudentId
} else if (this.clientInfo.resource_id) {
params.resource_id = this.clientInfo.resource_id
} else {
console.warn('缺少查询参数')
this.orderList = []
return
}
const res = await apiRoute.xs_orderTableList(params)
if (res.code === 1) {
// 处理API返回的数据格式
this.orderList = this.processOrderData(res.data?.data || [])
} else {
console.error('获取订单列表失败:', res.msg)
this.orderList = []
}
} catch (error) {
console.error('获取订单列表异常:', error)
this.orderList = []
}
},
// 处理订单数据格式,适配前端组件
processOrderData(orders) {
if (!Array.isArray(orders)) return []
return orders.map(order => ({
id: order.id,
student_id: order.student_id,
order_no: order.payment_id || `ORD${order.id}`, // 使用payment_id作为订单号
product_name: order.course_id_name || '课程',
total_amount: order.order_amount || '0.00',
paid_amount: order.order_status === 'paid' ? order.order_amount : '0.00',
unpaid_amount: order.order_status === 'paid' ? '0.00' : order.order_amount,
payment_method: this.formatPaymentType(order.payment_type),
salesperson_name: order.staff_id_name || '未指定',
course_count: order.total_hours || 0, // 使用课时数
status: this.mapOrderStatus(order.order_status),
create_time: order.created_at,
// 新增合同相关字段
contract_id: order.contract_id,
contract_sign_id: order.contract_sign_id,
// 保留原始数据
_raw: order
}))
},
// 格式化支付类型
formatPaymentType(paymentType) {
if (!paymentType) return '未知'
// 从字典数据中查找对应的支付方式名称
const paymentItem = this.paymentTypeDict.find(item => item.value === paymentType)
if (paymentItem) {
return paymentItem.name
}
// 如果字典数据未加载,使用备用映射
const fallbackMap = {
'cash': '现金支付',
'scan_code': '扫码支付',
'subscription': '订阅支付',
'wxpay_online': '微信在线代付',
'client_wxpay': '客户自行付款',
'deposit': '定金'
}
return fallbackMap[paymentType] || paymentType || '未知'
},
// 映射订单状态
mapOrderStatus(orderStatus) {
const statusMap = {
'pending': 'pending',
'paid': 'paid',
'signed': 'completed',
'completed': 'completed',
'transfer': 'partial'
}
return statusMap[orderStatus] || 'pending'
},
// 获取服务列表
async getServiceList(studentId = null) {
if (!this.clientInfo?.resource_id) return
const targetStudentId = studentId || this.currentStudent?.id
if (!targetStudentId) {
uni.showToast({
title: '请先选择学生',
icon: 'none'
})
return
}
try {
const response = await apiRoute.getStudentServiceList({
student_id: targetStudentId
})
if (response.code === 1) {
this.serviceList = response.data || []
} else {
console.error('获取服务记录失败:', response.msg)
uni.showToast({
title: response.msg || '获取服务记录失败',
icon: 'none'
})
this.serviceList = []
}
} catch (error) {
console.error('获取服务记录异常:', error)
uni.showToast({
title: '网络请求失败',
icon: 'none'
})
this.serviceList = []
}
},
// 打开新增订单弹窗
openAddOrderDialog() {
if (!this.currentStudent) {
uni.showToast({
title: '请先选择学生',
icon: 'none'
})
return
}
// 先关闭订单列表弹窗,避免遮挡新增订单弹窗
this.closePopup()
this.showOrderForm = true
this.$refs.orderFormPopup.open()
},
// 关闭新增订单弹窗
closeOrderForm() {
this.showOrderForm = false
this.$refs.orderFormPopup.close()
},
// 订单表单确认处理
async handleOrderFormConfirm() {
try {
// 关闽弹窗
this.closeOrderForm()
// 刷新订单列表
await this.getOrderList()
uni.showToast({
title: '订单创建成功',
icon: 'success'
})
} catch (error) {
console.error('处理订单确认失败:', error)
}
},
// 处理订单支付
handlePayOrder(order) {
// 关闭当前弹窗
this.closePopup()
// 根据支付类型处理不同的支付流程
this.processPayment(order)
},
// 处理支付流程
async processPayment(order) {
const paymentType = order._raw?.payment_type || order.payment_type
console.log('paymentType', paymentType)
try {
switch (paymentType) {
case 'cash':
// 现金支付 - 直接标记为已支付
await this.confirmCashPayment(order)
break
case 'scan_code':
// 扫码支付 - 显示二维码
this.showQRCodePayment(order)
break
case 'subscription':
// 订阅支付 - 分期支付流程
this.showSubscriptionPayment(order)
break
case 'wxpay_online':
// 微信在线代付 - 调用微信支付
this.showWechatPayment(order)
break
default:
uni.showToast({
title: '不支持的支付方式',
icon: 'none'
})
}
} catch (error) {
console.error('支付处理失败:', error)
uni.showToast({
title: '支付处理失败',
icon: 'none'
})
}
},
// 现金支付确认
async confirmCashPayment(order) {
uni.showModal({
title: '现金支付确认',
content: `确认已收到现金支付 ¥${order.total_amount}?`,
success: async (res) => {
if (res.confirm) {
try {
// 调用API更新订单状态为已支付
const updateData = {
order_id: order._raw?.id || order.id,
order_status: 'paid',
payment_id: `CASH${Date.now()}` // 生成现金支付单号
}
const result = await apiRoute.xs_orderTableUpdatePaymentStatus(updateData)
if (result.code === 1) {
uni.showToast({
title: '支付确认成功',
icon: 'success'
})
// 刷新订单列表
await this.getOrderList()
} else {
uni.showToast({
title: result.msg || '支付确认失败',
icon: 'none'
})
}
} catch (error) {
console.error('现金支付确认失败:', error)
uni.showToast({
title: '支付确认失败',
icon: 'none'
})
}
}
}
})
},
// 扫码支付
async showQRCodePayment(order) {
console.log('扫码支付:', order)
try {
uni.showLoading({
title: '生成支付二维码...'
})
// 调用接口获取支付二维码
const res = await apiRoute.getOrderPayQrcode({
order_id: order._raw?.id || order.id
})
uni.hideLoading()
if (res.code === 1 && res.data) {
// 显示二维码支付弹窗
this.openQRCodeModal({
order: order,
qrcode: res.data.code_url,
// 优先使用base64编码的二维码(解决微信小程序无法访问localhost的问题)
qrcodeImage: res.data.qrcode_base64 || res.data.qrcode_url
})
} else {
uni.showToast({
title: res.msg || '获取支付二维码失败',
icon: 'none'
})
}
} catch (error) {
uni.hideLoading()
console.error('获取支付二维码失败:', error)
uni.showToast({
title: '获取支付二维码失败',
icon: 'none'
})
}
},
// 打开二维码支付弹窗
openQRCodeModal(paymentData) {
this.qrCodePaymentData = paymentData
this.showQRCodeModal = true
this.$refs.qrCodePopup.open()
},
// 关闭二维码支付弹窗
closeQRCodeModal() {
this.showQRCodeModal = false
this.qrCodePaymentData = null
this.$refs.qrCodePopup.close()
},
// 确认二维码支付完成
async confirmQRCodePayment() {
if (!this.qrCodePaymentData?.order) return
const order = this.qrCodePaymentData.order
uni.showModal({
title: '支付确认',
content: '请确认是否已完成扫码支付?',
success: async (res) => {
if (res.confirm) {
try {
// 更新订单状态为已支付
await this.updateOrderStatus(order, 'paid', `QR${Date.now()}`)
this.closeQRCodeModal()
} catch (error) {
console.error('支付确认失败:', error)
uni.showToast({
title: '支付确认失败',
icon: 'none'
})
}
}
}
})
},
// 订阅支付
showSubscriptionPayment(order) {
uni.showActionSheet({
itemList: ['确认分期支付方案', '取消支付'],
success: async (res) => {
if (res.tapIndex === 0) {
uni.showModal({
title: '分期支付确认',
content: `确认使用分期支付方式支付 ¥${order.total_amount}?`,
success: async (modalRes) => {
if (modalRes.confirm) {
// 这里可以设置为部分支付状态
await this.updateOrderStatus(order, 'partial',
`SUB${Date.now()}`)
}
}
})
}
}
})
},
// 微信支付
showWechatPayment(order) {
uni.showModal({
title: '微信支付',
content: `将调用微信支付 ¥${order.total_amount}`,
confirmText: '确认支付',
cancelText: '取消',
success: async (res) => {
if (res.confirm) {
// 模拟微信支付流程
uni.showLoading({
title: '正在调用微信支付...'
})
// 模拟支付延时
setTimeout(async () => {
uni.hideLoading()
// 模拟支付结果(实际应该调用微信支付API)
uni.showModal({
title: '支付结果',
content: '微信支付完成,请确认是否已收到款项?',
success: async (confirmRes) => {
if (confirmRes.confirm) {
await this.updateOrderStatus(order, 'paid',
`WX${Date.now()}`)
}
}
})
}, 1500)
}
}
})
},
// 统一的订单状态更新方法
async updateOrderStatus(order, status, paymentId = '') {
try {
const updateData = {
order_id: order._raw?.id || order.id,
order_status: status,
payment_id: paymentId
}
const result = await apiRoute.xs_orderTableUpdatePaymentStatus(updateData)
if (result.code === 1) {
const statusText = {
'paid': '支付成功',
'partial': '分期支付确认成功',
'cancelled': '支付已取消'
}
uni.showToast({
title: statusText[status] || '状态更新成功',
icon: 'success'
})
// 刷新订单列表
await this.getOrderList()
} else {
uni.showToast({
title: result.msg || '状态更新失败',
icon: 'none'
})
}
} catch (error) {
console.error('更新订单状态失败:', error)
uni.showToast({
title: '状态更新失败',
icon: 'none'
})
}
},
// 查看订单详情
viewOrderDetail(order) {
const orderInfo = order._raw || order
const detailText = `
订单号:${order.order_no}
课程:${order.product_name}
金额:¥${order.total_amount}
已付:¥${order.paid_amount}
未付:¥${order.unpaid_amount}
支付方式:${order.payment_method}
销售顾问:${order.salesperson_name}
课时数:${order.course_count}节
状态:${this.getOrderStatusText(order.status)}
创建时间:${this.formatOrderTime(order.create_time)}
${orderInfo.paid_at ? '支付时间:' + this.formatOrderTime(orderInfo.paid_at) : ''}
`.trim()
// 检查是否为已支付订单,显示不同的按钮
const isOrderPaid = order.status === 'paid'
const buttons = isOrderPaid ? ['知道了', '合同签署'] : ['知道了']
console.log('订单数据', order)
uni.showModal({
title: '订单详情',
content: detailText,
showCancel: isOrderPaid,
cancelText: isOrderPaid ? '知道了' : '',
confirmText: isOrderPaid ? '合同签署' : '知道了',
success: (res) => {
if (res.confirm && isOrderPaid) {
// 点击了合同签署按钮
this.goToContractSign(order)
}
}
})
},
// 跳转到合同签署页面
goToContractSign(order) {
// 检查必要参数
const studentId = order.student_id || this.currentStudent?.id
const contractId = order.contract_id || order._raw?.contract_id
const contractSignId = order.contract_sign_id || order._raw?.contract_sign_id
if (!studentId) {
uni.showToast({
title: '缺少学生信息',
icon: 'none'
})
return
}
if (!contractId) {
// 如果订单中没有合同模板ID,尝试从课程信息获取
this.getContractByOrder(order, studentId)
return
}
// 构建跳转参数
let url =
`/pages-student/contracts/sign?contract_id=${contractId}&student_id=${studentId}&contract_name=${encodeURIComponent(order.product_name + '合同')}&user_role=staff`
// 如果有合同签署记录ID,也传递过去(用于已存在的签署记录)
if (contractSignId) {
url += `&contract_sign_id=${contractSignId}`
}
// 跳转到学员端合同签署页面
uni.navigateTo({
url: url
})
},
// 根据订单获取合同信息
async getContractByOrder(order, studentId) {
try {
uni.showLoading({
title: '获取合同信息...'
})
// 调用API获取订单对应的合同
const res = await apiRoute.getContractByOrder({
order_id: order._raw?.id || order.id,
student_id: studentId
})
uni.hideLoading()
if (res.code === 1 && res.data) {
const contractInfo = res.data
// 跳转到合同签署页面
uni.navigateTo({
url: `/pages-student/contracts/sign?contract_id=${contractInfo.contract_id}&student_id=${studentId}&contract_name=${encodeURIComponent(contractInfo.contract_name || order.product_name + '合同')}&user_role=staff`
})
} else {
uni.showToast({
title: res.msg || '未找到相关合同',
icon: 'none'
})
}
} catch (error) {
uni.hideLoading()
console.error('获取合同信息失败:', error)
uni.showToast({
title: '获取合同信息失败',
icon: 'none'
})
}
},
// 获取订单状态文本
getOrderStatusText(status) {
const statusMap = {
'pending': '待支付',
'paid': '已支付',
'partial': '部分支付',
'cancelled': '已取消',
'completed': '已完成',
'refunded': '已退款'
}
return statusMap[status] || '未知状态'
},
// 格式化订单时间
formatOrderTime(timeStr) {
if (!timeStr) return '未知'
try {
const date = new Date(timeStr)
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
} catch (e) {
return timeStr
}
}, },
// 获取赠品记录列表 // 获取赠品记录列表

201
uniapp/pages-market/clue/edit_clues.vue

@ -45,13 +45,13 @@
</view> </view>
</fui-form-item> </fui-form-item>
<!-- 来源渠道 --> <!-- 来源渠道 -->
<fui-form-item <fui-form-item
v-if="formData.source == 1" v-if="formData.source == 1"
label="来源渠道" label="来源渠道"
labelSize='26' labelSize='26'
prop="source_channel" prop="source_channel"
background='#434544' background='#434544'
labelColor='#fff' labelColor='#fff'
:bottomBorder='false'> :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="openCicker('source_channel')"> <view class="input-title" style="margin-right:14rpx;" @click="openCicker('source_channel')">
@ -172,7 +172,7 @@
</view> </view>
</view> </view>
</fui-form-item> --> </fui-form-item> -->
<fui-form-item labelWidth="240" label="2、承诺到访时间" labelSize='26' prop="promised_visit_time" background='#434544' labelColor='#fff' :bottomBorder='false'> <fui-form-item labelWidth="240" label="2、承诺到访时间" 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;">
<fui-input :borderBottom="false" :padding="[0]" placeholder="填写承诺到访时间" v-model="formData.promised_visit_time" backgroundColor="#434544" size="26" color="#fff" ></fui-input> <fui-input :borderBottom="false" :padding="[0]" placeholder="填写承诺到访时间" v-model="formData.promised_visit_time" backgroundColor="#434544" size="26" color="#fff" ></fui-input>
@ -223,7 +223,7 @@
</fui-radio-group> </fui-radio-group>
</view> </view>
</fui-form-item> </fui-form-item>
<!-- 沟通备注 --> <!-- 沟通备注 -->
<fui-form-item label="沟通备注" labelSize='26' prop="communication" background='#434544' labelColor='#fff' :bottomBorder='false'> <fui-form-item label="沟通备注" labelSize='26' prop="communication" background='#434544' labelColor='#fff' :bottomBorder='false'>
<view class="input-title" style="margin-right:14rpx;"> <view class="input-title" style="margin-right:14rpx;">
@ -255,7 +255,7 @@
<fui-textarea v-model="formData.first_visit_status" placeholder="点击填写" backgroundColor="#434544" size="26" color="#fff" :textareaBorder="false" :isCounter="true" :maxlength="500" :minHeight="120" :isAutoHeight="true" :borderTop="false"></fui-textarea> <fui-textarea v-model="formData.first_visit_status" placeholder="点击填写" backgroundColor="#434544" size="26" color="#fff" :textareaBorder="false" :isCounter="true" :maxlength="500" :minHeight="120" :isAutoHeight="true" :borderTop="false"></fui-textarea>
</view> </view>
</fui-form-item> </fui-form-item>
<!-- 二访时间 --> <!-- 二访时间 -->
<fui-form-item label="二访时间" labelSize='26' prop="second_visit_time" background='#434544' labelColor='#fff' :bottomBorder='false'> <fui-form-item label="二访时间" labelSize='26' prop="second_visit_time" background='#434544' labelColor='#fff' :bottomBorder='false'>
<view class="input-title" style="margin-right:14rpx;" @click="openDate('second_visit_time')"> <view class="input-title" style="margin-right:14rpx;" @click="openDate('second_visit_time')">
@ -268,13 +268,13 @@
<fui-textarea v-model="formData.second_visit_status" placeholder="点击填写" backgroundColor="#434544" size="26" color="#fff" :textareaBorder="false" :isCounter="true" :maxlength="500" :minHeight="120" :isAutoHeight="true" :borderTop="false"></fui-textarea> <fui-textarea v-model="formData.second_visit_status" placeholder="点击填写" backgroundColor="#434544" size="26" color="#fff" :textareaBorder="false" :isCounter="true" :maxlength="500" :minHeight="120" :isAutoHeight="true" :borderTop="false"></fui-textarea>
</view> </view>
</fui-form-item> </fui-form-item>
<fui-form-item label="追单标注" labelSize='26' prop="chasing_orders" background='#434544' labelColor='#fff' :bottomBorder='false'> <fui-form-item label="追单标注" labelSize='26' prop="chasing_orders" background='#434544' labelColor='#fff' :bottomBorder='false'>
<view class="textarea-container"> <view class="textarea-container">
<fui-textarea v-model="formData.chasing_orders" placeholder="点击填写" backgroundColor="#434544" size="26" color="#fff" :textareaBorder="false" :isCounter="true" :maxlength="500" :minHeight="120" :isAutoHeight="true" :borderTop="false"></fui-textarea> <fui-textarea v-model="formData.chasing_orders" placeholder="点击填写" backgroundColor="#434544" size="26" color="#fff" :textareaBorder="false" :isCounter="true" :maxlength="500" :minHeight="120" :isAutoHeight="true" :borderTop="false"></fui-textarea>
</view> </view>
</fui-form-item> </fui-form-item>
<fui-form-item label="是否报名" labelSize='26' prop="is_bm" background='#434544' labelColor='#fff' :bottomBorder='false'> <fui-form-item label="是否报名" labelSize='26' prop="is_bm" background='#434544' labelColor='#fff' :bottomBorder='false'>
<view class="input-title" style="margin-right:14rpx;"> <view class="input-title" style="margin-right:14rpx;">
<fui-radio-group name="radio" v-model="formData.is_bm" :disabled="true"> <fui-radio-group name="radio" v-model="formData.is_bm" :disabled="true">
@ -295,7 +295,7 @@
</fui-radio-group> </fui-radio-group>
</view> </view>
</fui-form-item> </fui-form-item>
<!-- 面咨备注 --> <!-- 面咨备注 -->
<fui-form-item label="面咨备注" labelSize='26' prop="consultation_remark" background='#434544' labelColor='#fff' :bottomBorder='false'> <fui-form-item label="面咨备注" labelSize='26' prop="consultation_remark" background='#434544' labelColor='#fff' :bottomBorder='false'>
<view class="textarea-container"> <view class="textarea-container">
@ -313,10 +313,10 @@
<!-- 选择器、日期选择等控件保留原有 --> <!-- 选择器、日期选择等控件保留原有 -->
<fui-date-picker :show="date_picker_show" type="5" @change="change_date" @cancel="cancel_date" :value="default_date_value"></fui-date-picker> <fui-date-picker :show="date_picker_show" type="5" @change="change_date" @cancel="cancel_date" :value="default_date_value"></fui-date-picker>
<!-- 日期时间选择器 --> <!-- 日期时间选择器 -->
<fui-date-picker :show="datetime_picker_show" type="5" @change="change_datetime" @cancel="cancel_datetime" :value="default_datetime_value"></fui-date-picker> <fui-date-picker :show="datetime_picker_show" type="5" @change="change_datetime" @cancel="cancel_datetime" :value="default_datetime_value"></fui-date-picker>
<fui-picker :linkage='picker_linkage' :options="picker_options" :layer="1" :show="picker_show" @change="changeCicker" @cancel="cancelCicker"></fui-picker> <fui-picker :linkage='picker_linkage' :options="picker_options" :layer="1" :show="picker_show" @change="changeCicker" @cancel="cancelCicker"></fui-picker>
<!-- 快速填写弹窗 --> <!-- 快速填写弹窗 -->
@ -333,8 +333,8 @@
<text>请粘贴包含客户信息的文本,支持格式:</text> <text>请粘贴包含客户信息的文本,支持格式:</text>
<text>姓名:张三,电话:13800138000,校区:测试校区</text> <text>姓名:张三,电话:13800138000,校区:测试校区</text>
</view> </view>
<textarea <textarea
class="quick-fill-textarea" class="quick-fill-textarea"
placeholder="请粘贴客户信息文本..." placeholder="请粘贴客户信息文本..."
v-model="quickFillText" v-model="quickFillText"
:maxlength="1000" :maxlength="1000"
@ -362,11 +362,11 @@
is_submit: true, //是否提交(防止重复提交)|true=可提交,false=不可提交 is_submit: true, //是否提交(防止重复提交)|true=可提交,false=不可提交
resource_sharing_id: '', //resource_sharing_id(资源共享表id) resource_sharing_id: '', //resource_sharing_id(资源共享表id)
// 快速填写相关 // 快速填写相关
showQuickFill: false, // 是否显示快速填写弹窗 showQuickFill: false, // 是否显示快速填写弹窗
quickFillText: '', // 快速填写文本内容 quickFillText: '', // 快速填写文本内容
//表单 //表单
formData: { formData: {
// 客户基础信息 // 客户基础信息
@ -489,7 +489,7 @@
data_picker_input_name: '', //时间组件的input_name data_picker_input_name: '', //时间组件的input_name
date_picker_show: false, //时间选择器是否展示 date_picker_show: false, //时间选择器是否展示
default_date_value: '', // 添加默认日期值 default_date_value: '', // 添加默认日期值
// 日期时间选择组件 // 日期时间选择组件
datetime_picker_input_name: '', //日期时间组件的input_name datetime_picker_input_name: '', //日期时间组件的input_name
datetime_picker_show: false, //日期时间选择器是否展示 datetime_picker_show: false, //日期时间选择器是否展示
@ -497,7 +497,7 @@
// 查重相关 // 查重相关
clientUserList: [], //查重用户列表 clientUserList: [], //查重用户列表
showDuplicateCheck: false, //是否显示查重弹出层 showDuplicateCheck: false, //是否显示查重弹出层
@ -520,14 +520,14 @@
}, },
onLoad(options) { onLoad(options) {
console.log('onLoad - 接收到参数:', options); console.log('onLoad - 接收到参数:', options);
// 检查参数是否存在 // 检查参数是否存在
if (!options) { if (!options) {
console.error('onLoad - 未接收到任何参数'); console.error('onLoad - 未接收到任何参数');
this.showParameterError(); this.showParameterError();
return; return;
} }
// 检查 resource_sharing_id 参数,支持多种参数名称 // 检查 resource_sharing_id 参数,支持多种参数名称
const resourceSharingId = options.resource_sharing_id || options.id || options.resourceSharingId; const resourceSharingId = options.resource_sharing_id || options.id || options.resourceSharingId;
if (!resourceSharingId) { if (!resourceSharingId) {
@ -536,7 +536,7 @@
this.showParameterError(); this.showParameterError();
return; return;
} }
this.resource_sharing_id = String(resourceSharingId); this.resource_sharing_id = String(resourceSharingId);
console.log('onLoad - 成功设置 resource_sharing_id:', this.resource_sharing_id); console.log('onLoad - 成功设置 resource_sharing_id:', this.resource_sharing_id);
}, },
@ -556,39 +556,39 @@
uni.navigateBack(); uni.navigateBack();
}, 2000); }, 2000);
}, },
//初始化 //初始化
async init() { async init() {
try { try {
console.log('init - 开始初始化流程'); console.log('init - 开始初始化流程');
if (!this.resource_sharing_id) { if (!this.resource_sharing_id) {
console.error('init - resource_sharing_id 为空,无法初始化'); console.error('init - resource_sharing_id 为空,无法初始化');
this.showParameterError(); this.showParameterError();
return; return;
} }
uni.showLoading({ uni.showLoading({
title: '加载中...', title: '加载中...',
mask: true mask: true
}); });
console.log('init - 开始加载字典数据'); console.log('init - 开始加载字典数据');
// 批量加载所有字典数据 // 批量加载所有字典数据
await this.getBatchDictData(); await this.getBatchDictData();
console.log('init - 字典数据加载完成'); console.log('init - 字典数据加载完成');
// 加载校区列表 // 加载校区列表
console.log('init - 开始加载校区列表'); console.log('init - 开始加载校区列表');
await this.get_campus_list(); await this.get_campus_list();
console.log('init - 校区列表加载完成'); console.log('init - 校区列表加载完成');
// 获取资源共享详情并回显数据 // 获取资源共享详情并回显数据
console.log('init - 开始获取客户详情'); console.log('init - 开始获取客户详情');
await this.getInfo(); await this.getInfo();
console.log('init - 客户详情获取完成'); console.log('init - 客户详情获取完成');
} catch (error) { } catch (error) {
console.error('初始化失败:', error); console.error('初始化失败:', error);
uni.showToast({ uni.showToast({
@ -599,7 +599,7 @@
uni.hideLoading(); uni.hideLoading();
} }
}, },
// 批量获取字典数据 // 批量获取字典数据
async getBatchDictData() { async getBatchDictData() {
const dictMapping = { const dictMapping = {
@ -613,7 +613,7 @@
'distance': 'distance', 'distance': 'distance',
'emotional_stickiness_score': 'emotional_stickiness_score' 'emotional_stickiness_score': 'emotional_stickiness_score'
}; };
try { try {
const batchResult = await apiRoute.common_getBatchDict(Object.keys(dictMapping)); const batchResult = await apiRoute.common_getBatchDict(Object.keys(dictMapping));
if (batchResult?.code === 1 && batchResult.data) { if (batchResult?.code === 1 && batchResult.data) {
@ -627,11 +627,11 @@
} catch (error) { } catch (error) {
console.error('批量字典接口失败:', error); console.error('批量字典接口失败:', error);
} }
// 回退到单个获取 // 回退到单个获取
const priorityKeys = ['source_channel', 'source']; const priorityKeys = ['source_channel', 'source'];
const otherKeys = ['purchasing_power', 'initial_intent', 'cognitive_idea', 'status', 'decision_maker', 'distance', 'emotional_stickiness_score']; const otherKeys = ['purchasing_power', 'initial_intent', 'cognitive_idea', 'status', 'decision_maker', 'distance', 'emotional_stickiness_score'];
try { try {
await Promise.all(priorityKeys.map(key => this.getDict(key))); await Promise.all(priorityKeys.map(key => this.getDict(key)));
setTimeout(() => { setTimeout(() => {
@ -645,18 +645,18 @@
// 处理字典数据 // 处理字典数据
processDictData(localKey, dictData) { processDictData(localKey, dictData) {
if (!Array.isArray(dictData) || !dictData.length) return; if (!Array.isArray(dictData) || !dictData.length) return;
let options = dictData.map(item => ({ text: item.name || '', value: item.value || '' })); let options = dictData.map(item => ({ text: item.name || '', value: item.value || '' }));
if (localKey === 'source_channel') { if (localKey === 'source_channel') {
options.unshift({ text: '线下', value: '0' }); options.unshift({ text: '线下', value: '0' });
} }
this.picker_config[localKey] = this.picker_config[localKey] || { text: '点击选择' }; this.picker_config[localKey] = this.picker_config[localKey] || { text: '点击选择' };
this.picker_config[localKey].options = options; this.picker_config[localKey].options = options;
}, },
async get_campus_list(){ async get_campus_list(){
let res = await apiRoute.common_getCampusesList({}) let res = await apiRoute.common_getCampusesList({})
if (res.code != 1) { if (res.code != 1) {
uni.showToast({ uni.showToast({
@ -665,10 +665,10 @@
}) })
return return
} }
this.campus_list = res.data this.campus_list = res.data
let arr = [] let arr = []
this.campus_list.forEach((v,k)=>{ this.campus_list.forEach((v,k)=>{
arr.push({ arr.push({
@ -676,29 +676,25 @@
value: v.id, value: v.id,
}) })
}) })
this.picker_config['campus'].options = arr this.picker_config['campus'].options = arr
}, },
//获取资源共享-详情(客户资源详情) //获取资源共享-详情(客户资源详情)
async getInfo() { async getInfo() {
try { try {
console.log('getInfo - 开始获取客户详情, resource_sharing_id:', this.resource_sharing_id);
if (!this.resource_sharing_id) { if (!this.resource_sharing_id) {
console.error('getInfo - resource_sharing_id 为空,无法获取客户详情'); console.error('getInfo - resource_sharing_id 为空,无法获取客户详情');
return; return;
} }
let params = { let params = {
resource_sharing_id: this.resource_sharing_id resource_sharing_id: this.resource_sharing_id
}; };
console.log('getInfo - 发起请求:', params);
let res = await apiRoute.xs_resourceSharingInfo(params); //资源共享-详情(客户资源详情) let res = await apiRoute.xs_resourceSharingInfo(params); //资源共享-详情(客户资源详情)
console.log('getInfo - 请求响应:', res);
if (res.code != 1) { if (res.code != 1) {
console.error('getInfo - 请求失败:', res.msg); console.error('getInfo - 请求失败:', res.msg);
uni.showToast({ uni.showToast({
@ -710,8 +706,6 @@
let customerResource = res.data.customerResource || {}; //客户资源详情 let customerResource = res.data.customerResource || {}; //客户资源详情
let sixSpeed = res.data.customerResource.sixSpeed || {}; //六要素详情 let sixSpeed = res.data.customerResource.sixSpeed || {}; //六要素详情
console.log('getInfo - 客户资源详情:', customerResource);
console.log('getInfo - 六要素详情:', sixSpeed);
// 存储原始数据,用于后续回显 // 存储原始数据,用于后续回显
this._resourceDetail = res.data; this._resourceDetail = res.data;
@ -754,39 +748,40 @@
call_intent: sixSpeed.call_intent || '2', // 是否加微信 call_intent: sixSpeed.call_intent || '2', // 是否加微信
emotional_stickiness_score: sixSpeed.emotional_stickiness_score || '', // 情感粘度 emotional_stickiness_score: sixSpeed.emotional_stickiness_score || '', // 情感粘度
}; };
console.log('getInfo - 表单数据设置完成:', this.formData); console.log('getInfo - 表单数据设置完成:',sixSpeed.promised_visit_time);
// 格式化日期时间 // 格式化日期时间
if (sixSpeed.promised_visit_time) { // if (sixSpeed.promised_visit_time) {
// console.log('getInfo - 开始格式化日期时间',sixSpeed.promised_visit_time.includes(' '));
// 如果包含时间部分,保持原格式;否则只格式化日期部分 // 如果包含时间部分,保持原格式;否则只格式化日期部分
if (sixSpeed.promised_visit_time.includes(' ')) { // if (sixSpeed.promised_visit_time.includes(' ')) {
this.formData.promised_visit_time = sixSpeed.promised_visit_time; // this.formData.promised_visit_time = sixSpeed.promised_visit_time;
} else { // } else {
this.formData.promised_visit_time = this.$util.formatToDateTime(sixSpeed.promised_visit_time, 'Y-m-d'); // this.formData.promised_visit_time = this.$util.formatToDateTime(sixSpeed.promised_visit_time, 'Y-m-d');
} // }
} // }
// 可选上课时间保持原始文本格式,不做日期格式化 // 可选上课时间保持原始文本格式,不做日期格式化
if (sixSpeed.preferred_class_time) { if (sixSpeed.preferred_class_time) {
this.formData.optional_class_time = sixSpeed.preferred_class_time; this.formData.optional_class_time = sixSpeed.preferred_class_time;
} }
if (sixSpeed.first_visit_time) { if (sixSpeed.first_visit_time) {
this.formData.first_visit_time = this.$util.formatToDateTime(sixSpeed.first_visit_time, 'Y-m-d'); this.formData.first_visit_time = this.$util.formatToDateTime(sixSpeed.first_visit_time, 'Y-m-d');
} }
if (sixSpeed.second_visit_time) { if (sixSpeed.second_visit_time) {
this.formData.second_visit_time = this.$util.formatToDateTime(sixSpeed.second_visit_time, 'Y-m-d'); this.formData.second_visit_time = this.$util.formatToDateTime(sixSpeed.second_visit_time, 'Y-m-d');
} }
console.log('getInfo - 日期格式化完成'); console.log('getInfo - 日期格式化完成');
// 设置选择器文本回显 // 设置选择器文本回显
console.log('getInfo - 开始设置选择器文本回显'); console.log('getInfo - 开始设置选择器文本回显');
await this.setPickerText(); await this.setPickerText();
console.log('getInfo - 选择器文本回显完成'); console.log('getInfo - 选择器文本回显完成');
} catch (error) { } catch (error) {
console.error('获取客户详情失败:', error); console.error('获取客户详情失败:', error);
uni.showToast({ uni.showToast({
@ -800,7 +795,7 @@
async setPickerText() { async setPickerText() {
try { try {
const { customerResource = {}, sixSpeed = {} } = await this.getResourceDetail() || {}; const { customerResource = {}, sixSpeed = {} } = await this.getResourceDetail() || {};
// 设置选择器文本回显 // 设置选择器文本回显
this.setPickerTextByValue('source_channel', this.formData.source_channel, customerResource.source_channel_name); this.setPickerTextByValue('source_channel', this.formData.source_channel, customerResource.source_channel_name);
this.setPickerTextByValue('source', this.formData.source, customerResource.source_name); this.setPickerTextByValue('source', this.formData.source, customerResource.source_name);
@ -810,14 +805,14 @@
this.setPickerTextByValue('decision_maker', this.formData.decision_maker, customerResource.decision_maker_name); this.setPickerTextByValue('decision_maker', this.formData.decision_maker, customerResource.decision_maker_name);
this.setPickerTextByValue('campus', this.formData.campus, customerResource.campus_name); this.setPickerTextByValue('campus', this.formData.campus, customerResource.campus_name);
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.purchase_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);
this.setPickerTextByValue('emotional_stickiness_score', this.formData.emotional_stickiness_score, sixSpeed.emotional_stickiness_score_name); this.setPickerTextByValue('emotional_stickiness_score', this.formData.emotional_stickiness_score, sixSpeed.emotional_stickiness_score_name);
// 不再需要设置call_intent的选择器文本,因为已改为单选组件 // 不再需要设置call_intent的选择器文本,因为已改为单选组件
console.log('选择器文本回显完成'); console.log('选择器文本回显完成');
} catch (error) { } catch (error) {
console.error('设置选择器文本回显失败:', error); console.error('设置选择器文本回显失败:', error);
@ -832,12 +827,12 @@
this.picker_config[pickerName].text = '点击选择'; this.picker_config[pickerName].text = '点击选择';
return; return;
} }
// 创建映射缓存,避免重复查找 // 创建映射缓存,避免重复查找
if (!this._valueTextMapping) { if (!this._valueTextMapping) {
this._valueTextMapping = {}; this._valueTextMapping = {};
} }
// 检查缓存 // 检查缓存
const cacheKey = `${pickerName}_${value}`; const cacheKey = `${pickerName}_${value}`;
if (this._valueTextMapping[cacheKey]) { if (this._valueTextMapping[cacheKey]) {
@ -845,16 +840,16 @@
this.picker_config[pickerName].text = this._valueTextMapping[cacheKey]; this.picker_config[pickerName].text = this._valueTextMapping[cacheKey];
return; 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: [] };
} }
// 先尝试从选项中找到匹配的文本 // 先尝试从选项中找到匹配的文本
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; let textValue;
if (option) { if (option) {
textValue = option.text; textValue = option.text;
@ -864,7 +859,7 @@
} else { } else {
textValue = '点击选择'; textValue = '点击选择';
} }
// 保存到缓存 // 保存到缓存
this._valueTextMapping[cacheKey] = textValue; this._valueTextMapping[cacheKey] = textValue;
this.picker_config[pickerName].text = textValue; this.picker_config[pickerName].text = textValue;
@ -875,11 +870,11 @@
if (this._resourceDetail) { if (this._resourceDetail) {
return this._resourceDetail return this._resourceDetail
} }
let params = { let params = {
resource_sharing_id: this.resource_sharing_id resource_sharing_id: this.resource_sharing_id
} }
let res = await apiRoute.xs_resourceSharingInfo(params) let res = await apiRoute.xs_resourceSharingInfo(params)
if (res.code == 1) { if (res.code == 1) {
this._resourceDetail = res.data this._resourceDetail = res.data
@ -902,14 +897,14 @@
customer_type: 'customer_type', customer_type: 'customer_type',
emotional_stickiness_score: 'emotional_stickiness_score' emotional_stickiness_score: 'emotional_stickiness_score'
}; };
const key = keyMap[inputName]; const key = keyMap[inputName];
if (!key) return; if (!key) return;
try { try {
const dictionary = await this.$util.getDict(key); const dictionary = await this.$util.getDict(key);
if (!dictionary?.length) return; if (!dictionary?.length) return;
this.processDictData(inputName, dictionary); this.processDictData(inputName, dictionary);
} catch (error) { } catch (error) {
console.error(`获取字典 ${inputName} 失败:`, error); console.error(`获取字典 ${inputName} 失败:`, error);
@ -1000,7 +995,7 @@
this.showQuickFill = true this.showQuickFill = true
this.quickFillText = '' this.quickFillText = ''
}, },
// 解析快速填写文本 // 解析快速填写文本
parseQuickFillText() { parseQuickFillText() {
if (!this.quickFillText.trim()) { if (!this.quickFillText.trim()) {
@ -1010,7 +1005,7 @@
}) })
return return
} }
try { try {
// 定义字段映射规则 // 定义字段映射规则
const fieldRules = [ const fieldRules = [
@ -1020,11 +1015,11 @@
{ key: 'age', patterns: ['年龄'] }, { key: 'age', patterns: ['年龄'] },
{ key: 'birthday', patterns: ['生日', '出生日期', '生日日期'] } { key: 'birthday', patterns: ['生日', '出生日期', '生日日期'] }
] ]
// 用于存储解析结果 // 用于存储解析结果
const parsedData = {} const parsedData = {}
const text = this.quickFillText.trim() const text = this.quickFillText.trim()
// 对每个字段规则进行匹配 // 对每个字段规则进行匹配
fieldRules.forEach(rule => { fieldRules.forEach(rule => {
rule.patterns.forEach(pattern => { rule.patterns.forEach(pattern => {
@ -1036,9 +1031,9 @@
} }
}) })
}) })
console.log('解析结果:', parsedData) console.log('解析结果:', parsedData)
// 填写到表单中 // 填写到表单中
let fillCount = 0 let fillCount = 0
if (parsedData.name) { if (parsedData.name) {
@ -1065,7 +1060,7 @@
this.formData.birthday = parsedData.birthday this.formData.birthday = parsedData.birthday
fillCount++ fillCount++
} }
if (fillCount > 0) { if (fillCount > 0) {
uni.showToast({ uni.showToast({
title: `成功填写${fillCount}个字段`, title: `成功填写${fillCount}个字段`,
@ -1078,7 +1073,7 @@
icon: 'none' icon: 'none'
}) })
} }
} catch (error) { } catch (error) {
console.error('解析失败:', error) console.error('解析失败:', error)
uni.showToast({ uni.showToast({
@ -1087,14 +1082,14 @@
}) })
} }
}, },
// 查找并设置校区 // 查找并设置校区
findAndSetCampus(campusText) { findAndSetCampus(campusText) {
const campusOptions = this.picker_config.campus?.options || [] const campusOptions = this.picker_config.campus?.options || []
const matchedCampus = campusOptions.find(option => const matchedCampus = campusOptions.find(option =>
option.text.includes(campusText) || campusText.includes(option.text) option.text.includes(campusText) || campusText.includes(option.text)
) )
if (matchedCampus) { if (matchedCampus) {
this.formData.campus = matchedCampus.value this.formData.campus = matchedCampus.value
this.picker_config.campus.text = matchedCampus.text this.picker_config.campus.text = matchedCampus.text
@ -1106,7 +1101,7 @@
// 打开选择器 // 打开选择器
openCicker(inputName, linkage = true) { openCicker(inputName, linkage = true) {
const options = this.picker_config[inputName]?.options || []; const options = this.picker_config[inputName]?.options || [];
if (!options.length) { if (!options.length) {
uni.showToast({ title: '暂无选项', icon: 'none' }); uni.showToast({ title: '暂无选项', icon: 'none' });
return; return;
@ -1115,7 +1110,7 @@
this.picker_input_name = inputName; this.picker_input_name = inputName;
this.picker_options = options; this.picker_options = options;
this.picker_linkage = linkage; this.picker_linkage = linkage;
this.$nextTick(() => { this.$nextTick(() => {
this.picker_show = true; this.picker_show = true;
}); });
@ -1124,7 +1119,7 @@
// 处理选择器选择 // 处理选择器选择
changeCicker(e) { changeCicker(e) {
const inputName = this.picker_input_name; const inputName = this.picker_input_name;
this.updateFormField(inputName, e.value, e.text); this.updateFormField(inputName, e.value, e.text);
this.handleSpecialSelectionLogic(inputName, e.value, e.text); this.handleSpecialSelectionLogic(inputName, e.value, e.text);
this.cancelCicker(); this.cancelCicker();
@ -1221,7 +1216,7 @@
change_date(e) { change_date(e) {
const val = e.result || ''; const val = e.result || '';
const inputName = this.data_picker_input_name; const inputName = this.data_picker_input_name;
this.formData[inputName] = val; this.formData[inputName] = val;
this.cancel_date(); this.cancel_date();
}, },
@ -1234,9 +1229,9 @@
// 处理日期时间选择 // 处理日期时间选择
change_datetime(e) { change_datetime(e) {
console.log('日期时间选择器返回数据:', e); console.log('日期时间选择器返回数据:', e);
let val = e.result || e.value || ''; let val = e.result || e.value || '';
// 确保日期时间格式正确 // 确保日期时间格式正确
if (val && typeof val === 'string') { if (val && typeof val === 'string') {
// 如果包含时间部分但格式不对,进行格式化 // 如果包含时间部分但格式不对,进行格式化
@ -1251,7 +1246,7 @@
} }
} }
} }
const inputName = this.datetime_picker_input_name; const inputName = this.datetime_picker_input_name;
this.formData[inputName] = val; this.formData[inputName] = val;
this.cancel_datetime(); this.cancel_datetime();
@ -1313,13 +1308,13 @@
//提交 //提交
async submit() { async submit() {
const data = { ...this.formData }; const data = { ...this.formData };
// 表单验证 // 表单验证
if (!await this.validatorForm(data)) return; if (!await this.validatorForm(data)) return;
// 防止重复提交 // 防止重复提交
if (!this.canSubmit()) return; if (!this.canSubmit()) return;
this.is_submit = false; this.is_submit = false;
try { try {
const res = await apiRoute.xs_editCustomerResources(data); const res = await apiRoute.xs_editCustomerResources(data);
@ -1340,4 +1335,4 @@
<style lang="less" scoped> <style lang="less" scoped>
@import './edit_clues.less'; @import './edit_clues.less';
</style> </style>

Loading…
Cancel
Save