智慧教务系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1213 lines
37 KiB

<!--客户详情 - 重构版本-->
<template>
<view class="assemble">
<!-- 主要内容区域 -->
<view class="content">
<!-- 客户信息卡片 -->
<ClientInfoCard
:client-info="clientInfo"
:actions="[]"
@call="handleMakeCall"
@message="handleSendMessage"
/>
<!-- 标签切换器 -->
<view class="tab-switcher-container">
<TabSwitcher
:tabs="tabs"
:active-tab-id="switch_tags_type"
@tab-change="handleTabChange"
/>
</view>
<!-- 学生信息卡片区域 -->
<view class="student-section" v-if="switch_tags_type == 1">
<view class="section-header">
<text class="section-title">学生信息</text>
<view class="add-student-btn" @click="openAddStudentDialog">
<text class="add-icon">+</text>
<text class="add-text">添加学生</text>
</view>
</view>
<!-- 学生信息滑动卡片 -->
<view class="student-cards">
<swiper
class="student-swiper"
:indicator-dots="studentList.length > 1"
:circular="studentList.length > 1"
indicator-color="rgba(255, 255, 255, 0.3)"
indicator-active-color="#29D3B4"
previous-margin="20"
next-margin="20"
@change="onStudentSwiperChange">
<swiper-item v-for="(student, index) in studentList" :key="student.id">
<view class="student-swiper-content">
<StudentInfoCard
:student="student"
:actions="[]"
:show-details="true"
@action="handleStudentAction"
/>
<!-- 操作按钮区域 - 每个学生卡片都有自己的按钮 -->
<view class="action-buttons-section">
<view
class="action-item"
v-for="action in actionButtons"
:key="action.key"
@click="handleStudentActionClick(action, student)"
>
<view class="action-icon">
<text>{{ action.icon }}</text>
</view>
<text class="action-text">{{ action.text }}</text>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
<!-- 空状态 -->
<view v-if="studentList.length === 0" class="empty-state">
<text class="empty-icon">👤</text>
<text class="empty-text">暂无学生信息</text>
<view class="empty-add-btn" @click="openAddStudentDialog">
<text>添加第一个学生</text>
</view>
</view>
</view>
<!-- 课程信息标签内容 -->
<view class="course-section" v-if="switch_tags_type == 2">
<CourseInfoCard
v-if="courseInfo && courseInfo.length > 0"
:course-list="courseInfo"
@view-detail="viewCourseDetail"
/>
<view v-else class="empty-state">
<text class="empty-icon">📚</text>
<text class="empty-text">暂无课程信息</text>
</view>
</view>
<!-- 通话记录标签内容 -->
<view class="call-section" v-if="switch_tags_type == 3">
<view v-if="listCallUp.length === 0" class="empty-state">
<text class="empty-icon">📞</text>
<text class="empty-text">暂无通话记录</text>
</view>
<CallRecordCard
v-for="record in listCallUp"
:key="record.id"
:record="record"
@remark="openAddRemark"
/>
</view>
<!-- 体测记录标签内容 -->
<view class="fitness-section" v-if="switch_tags_type == 4">
<view class="section-header" v-if="currentStudent">
<text class="context-title">{{ currentStudent.name }}的体测记录</text>
<view class="add-record-btn" @click="openAddFitnessRecord">
<text class="add-icon">+</text>
<text class="add-text">新增记录</text>
</view>
</view>
<view v-if="currentStudentFitnessRecords.length === 0" class="empty-state">
<text class="empty-icon">📊</text>
<text class="empty-text">暂无体测记录</text>
</view>
<FitnessRecordCard
v-for="record in currentStudentFitnessRecords"
:key="record.id"
:record="record"
@edit="openEditFitnessRecord"
/>
</view>
<!-- 学习计划标签内容 -->
<view class="study-plan-section" v-if="switch_tags_type == 5">
<view v-if="!studyPlanList || studyPlanList.length === 0" class="empty-state">
<text class="empty-icon">📚</text>
<text class="empty-text">暂无学习计划</text>
</view>
</view>
</view>
<!-- 底部弹窗组件 -->
<BottomPopup
:visible="currentPopup !== null"
:title="popupTitle"
:has-footer="needsFooter"
@close="closePopup"
>
<!-- 课程信息弹窗 -->
<CourseInfoCard
v-if="currentPopup === 'course_info'"
:course-list="courseInfo"
@view-detail="viewCourseDetail"
/>
<!-- 体测记录弹窗 -->
<FitnessRecordCard
v-if="currentPopup === 'fitness_record'"
v-for="record in currentStudentFitnessRecords"
:key="record.id"
:record="record"
@edit="openEditFitnessRecord"
/>
<!-- 学习计划弹窗 -->
<StudyPlanCard
v-if="currentPopup === 'study_plan'"
:plan-list="studyPlanList"
/>
<!-- 订单列表弹窗 -->
<OrderListCard
v-if="currentPopup === 'order_list'"
:order-list="orderList"
@add-order="openAddOrderDialog"
@pay-order="handlePayOrder"
@view-detail="viewOrderDetail"
/>
<!-- 服务列表弹窗 -->
<ServiceListCard
v-if="currentPopup === 'service_list'"
:service-list="serviceList"
/>
<!-- 底部操作按钮 -->
<template #footer v-if="needsFooter">
<view class="popup-footer-btns">
<view class="footer-btn secondary" @click="closePopup">关闭</view>
<view
class="footer-btn primary"
v-if="showAddButton"
@click="handleAddAction"
>新增</view>
</view>
</template>
</BottomPopup>
<!-- 保留原有的编辑弹窗 -->
<uni-popup ref="remarkPopup" type="dialog">
<view class="remark-dialog">
<textarea
v-model="remark_content"
placeholder="请输入备注内容"
maxlength="200"
></textarea>
<view class="dialog-btns">
<view class="btn cancel" @click="closeRemark">取消</view>
<view class="btn confirm" @click="confirmRemark">确定</view>
</view>
</view>
</uni-popup>
<FitnessRecordPopup ref="fitnessRecordPopup" @confirm="handleFitnessRecordConfirm" />
<CourseEditPopup ref="courseEditPopup" @confirm="handleCourseEditConfirm" />
<StudentEditPopup ref="studentEditPopup" :resource-id="clientInfo.resource_id" @confirm="handleStudentEditConfirm" />
<!-- 新增订单弹窗 -->
<uni-popup ref="orderFormPopup" type="bottom">
<OrderFormPopup
:visible="showOrderForm"
:student-info="currentStudent"
:resource-id="clientInfo.resource_id"
@cancel="closeOrderForm"
@confirm="handleOrderFormConfirm"
/>
</uni-popup>
</view>
</template>
<script>
import apiRoute from '@/api/apiRoute.js'
// 组件导入
import ClientInfoCard from '@/components/client-info-card/client-info-card.vue'
import StudentInfoCard from '@/components/student-info-card/student-info-card.vue'
import TabSwitcher from '@/components/tab-switcher/tab-switcher.vue'
import CallRecordCard from '@/components/call-record-card/call-record-card.vue'
import FitnessRecordCard from '@/components/fitness-record-card/fitness-record-card.vue'
// 新的底部弹窗组件
import BottomPopup from '@/components/bottom-popup/index.vue'
import StudyPlanCard from '@/components/study-plan-card/index.vue'
import CourseInfoCard from '@/components/course-info-card/index.vue'
import OrderListCard from '@/components/order-list-card/index.vue'
import ServiceListCard from '@/components/service-list-card/index.vue'
import OrderFormPopup from '@/components/order-form-popup/index.vue'
// 编辑弹窗
import CourseEditPopup from '@/components/course-edit-popup/course-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'
export default {
components: {
ClientInfoCard,
StudentInfoCard,
TabSwitcher,
CallRecordCard,
FitnessRecordCard,
BottomPopup,
StudyPlanCard,
CourseInfoCard,
OrderListCard,
ServiceListCard,
OrderFormPopup,
CourseEditPopup,
StudentEditPopup,
FitnessRecordPopup
},
data() {
return {
switch_tags_type: 1,
resource_sharing_id: '',
// 屏幕信息(保留以备其他地方使用)
screenHeight: 0,
// 基本数据
clientInfo: { id: '', resource_id: '', customerResource: {} },
userInfo: {},
listCallUp: [],
courseInfo: [],
fitnessRecords: [],
studentList: [],
currentStudentIndex: 0,
// 底部弹窗相关
currentPopup: null,
studyPlanList: [],
orderList: [],
serviceList: [],
// 订单表单弹窗
showOrderForm: false,
// 编辑相关
remark_content: '',
currentRecord: null,
// 配置数据
tabs: [
{ id: 1, name: '基本资料' },
// { id: 2, name: '课程信息' },
{ id: 3, name: '通话记录' },
// { id: 4, name: '体测记录' },
// { id: 5, name: '学习计划' }
{ id: 6, name: '修改记录' }
],
actionButtons: [
{ key: 'course_arrangement', text: '课程安排', icon: '📅' },
{ key: 'order_list', text: '订单列表', icon: '📋' },
{ key: 'service_list', text: '服务列表', icon: '🔧' },
{ key: 'course_info', text: '课程信息', icon: '📚' },
{ key: 'fitness_record', text: '体测记录', icon: '📊' },
{ key: 'study_plan', text: '学习计划', icon: '📝' }
]
}
},
computed: {
currentStudent() {
return this.studentList[this.currentStudentIndex] || null
},
currentStudentFitnessRecords() {
if (!this.currentStudent) return []
return this.fitnessRecords.filter(record => record.student_id === this.currentStudent.id)
},
popupTitle() {
const titles = {
'course_info': '课程信息',
'fitness_record': '体测记录',
'study_plan': '学习计划',
'order_list': '订单列表',
'service_list': '服务列表'
}
return titles[this.currentPopup] || ''
},
needsFooter() {
// 所有弹窗都显示底部按钮区域
return this.currentPopup !== null
},
showAddButton() {
// 只有体测记录和学习计划弹窗显示新增按钮
return ['fitness_record', 'study_plan'].includes(this.currentPopup)
},
// 计算年龄(x岁x月)
calculateAge(birthday) {
if (!birthday) return ''
const birthDate = new Date(birthday)
const now = new Date()
let years = now.getFullYear() - birthDate.getFullYear()
let months = now.getMonth() - birthDate.getMonth()
if (months < 0) {
years--
months += 12
}
// 如果当前日期小于生日的日期,月份减1
if (now.getDate() < birthDate.getDate()) {
months--
if (months < 0) {
years--
months += 12
}
}
if (years > 0 && months > 0) {
return `${years}${months}`
} else if (years > 0) {
return `${years}`
} else if (months > 0) {
return `${months}`
} else {
return '新生儿'
}
},
// 格式化性别
formatGender(gender) {
return gender === 1 ? '男' : gender === 2 ? '女' : ''
}
},
onLoad(options) {
if (!options?.resource_sharing_id) {
uni.showToast({ title: '缺少必要参数', icon: 'none' })
setTimeout(() => uni.navigateBack(), 1500)
return
}
this.resource_sharing_id = options.resource_sharing_id
},
onShow() {
this.init()
},
methods: {
async init() {
try {
await this.getInfo()
await Promise.all([
this.getUserInfo(),
this.getListCallUp(),
this.getCourseInfo(),
this.getFitnessRecords(),
this.getStudentList()
])
} catch (error) {
console.error('数据加载失败:', error)
}
},
async getUserInfo() {
try {
const res = await apiRoute.getPersonnelInfo({})
if (res.code === 1) {
this.userInfo = res.data
}
} catch (error) {
console.error('获取员工信息失败:', error)
}
},
async getInfo() {
if (!this.resource_sharing_id) return
try {
const res = await apiRoute.xs_resourceSharingInfo({ resource_sharing_id: this.resource_sharing_id })
if (res.code === 1) {
this.clientInfo = res.data
}
} catch (error) {
console.error('获取客户详情失败:', error)
}
},
async getListCallUp() {
if (!this.clientInfo?.resource_id) return
try {
const res = await apiRoute.listCallUp({ resource_id: this.clientInfo.resource_id })
if (res.code === 1) {
this.listCallUp = res.data || []
}
} catch (error) {
console.error('获取通话记录失败:', error)
}
},
handleMakeCall() {
const phone = this.clientInfo?.customerResource?.phone_number
if (phone) {
uni.makePhoneCall({ phoneNumber: phone })
}
},
handleSendMessage() {
uni.showToast({ title: '发送消息功能待实现', icon: 'none' })
},
openAddRemark(record) {
this.remark_content = record.remarks || ''
this.currentRecord = record
this.$refs.remarkPopup.open()
},
async confirmRemark() {
if (!this.remark_content.trim() || !this.currentRecord?.id) {
uni.showToast({ title: '请输入备注内容', icon: 'none' })
return
}
try {
const res = await apiRoute.xs_communicationRecordsEdit({
id: this.currentRecord.id,
remarks: this.remark_content
})
if (res.code === 1) {
uni.showToast({ title: '备注更新成功', icon: 'success' })
await this.getListCallUp()
}
} catch (error) {
uni.showToast({ title: '更新失败', icon: 'none' })
}
this.closeRemark()
},
closeRemark() {
this.$refs.remarkPopup.close()
this.remark_content = ''
this.currentRecord = null
},
async getCourseInfo(studentId = null) {
if (!this.clientInfo?.resource_id) return
try {
const targetStudentId = studentId || this.currentStudent?.id
const res = await apiRoute.getStudentCourseInfo({
resource_id: this.clientInfo.resource_id,
member_id: this.clientInfo.customerResource?.member_id || '',
student_id: targetStudentId
})
if (res.code === 1) {
this.courseInfo = res.data || []
}
} catch (error) {
console.error('获取课程信息失败:', error)
}
},
// 弹窗交互逻辑
async handleActionClick(action) {
switch (action.key) {
case 'course_arrangement':
this.$navigateToPage(`/pages/market/clue/class_arrangement`, {
resource_id: this.clientInfo.resource_id,
student_id: this.currentStudent?.id
})
break
case 'course_info':
await this.getCourseInfo()
this.currentPopup = 'course_info'
break
case 'fitness_record':
await this.getFitnessRecords()
this.currentPopup = 'fitness_record'
break
case 'study_plan':
await this.getStudyPlanList()
this.currentPopup = 'study_plan'
break
case 'order_list':
await this.getOrderList()
this.currentPopup = 'order_list'
break
case 'service_list':
await this.getServiceList()
this.currentPopup = 'service_list'
break
}
},
// 学生卡片操作按钮点击处理 - 传递具体学生信息
async handleStudentActionClick(action, student) {
// 设置当前操作的学生
this.setCurrentStudent(student)
switch (action.key) {
case 'course_arrangement':
this.$navigateToPage(`/pages/market/clue/class_arrangement`, {
resource_id: this.clientInfo.resource_id,
student_id: student.id
})
break
case 'course_info':
await this.getCourseInfo(student.id)
this.currentPopup = 'course_info'
break
case 'fitness_record':
await this.getFitnessRecords(student.id)
this.currentPopup = 'fitness_record'
break
case 'study_plan':
await this.getStudyPlanList(student.id)
this.currentPopup = 'study_plan'
break
case 'order_list':
await this.getOrderList(student.id)
this.currentPopup = 'order_list'
break
case 'service_list':
await this.getServiceList(student.id)
this.currentPopup = 'service_list'
break
}
},
closePopup() {
this.currentPopup = null
},
handleAddAction() {
if (this.currentPopup === 'fitness_record') {
this.openAddFitnessRecord()
} else if (this.currentPopup === 'study_plan') {
// TODO: 添加学习计划
uni.showToast({ title: '添加学习计划功能待实现', icon: 'none' })
}
},
handleCourseEditConfirm(result) {
uni.showToast({ title: '保存成功', icon: 'success' })
},
viewCourseDetail(course) {
if (!course?.id) {
uni.showToast({ title: '课程信息不完整', icon: 'none' })
return
}
this.$navigateToPage(`/pages/market/course/course_detail`, {
id: course.id,
resource_id: this.clientInfo.resource_id
})
},
async handleTabChange({ tabId }) {
this.switch_tags_type = tabId
if (tabId === 2) await this.getCourseInfo()
if (tabId === 3) await this.getListCallUp()
if (tabId === 4) await this.getFitnessRecords()
if (tabId === 6) {
this.$navigateToPage(`/pages/market/clue/edit_clues_log`, {
resource_id: this.clientInfo.resource_id
})
}
},
handleStudentAction({ action, student }) {
this.setCurrentStudent(student)
switch (action.key) {
case 'course_info':
this.switch_tags_type = 2
break
case 'fitness_record':
this.switch_tags_type = 4
break
case 'study_plan':
this.switch_tags_type = 5
break
default:
this.handleActionClick(action)
break
}
},
setCurrentStudent(student) {
const index = this.studentList.findIndex(s => s.id === student.id)
if (index !== -1) this.currentStudentIndex = index
},
onStudentSwiperChange(e) {
this.currentStudentIndex = e.detail.current
},
async getFitnessRecords(studentId = null) {
if (!this.clientInfo?.resource_id) return
// 如果指定了学生ID,则获取该学生的体测记录
const targetStudentId = studentId || this.currentStudent?.id
// 使用模拟数据
this.fitnessRecords = [
{
id: 1,
student_id: targetStudentId || 1,
test_date: '2024-01-15',
height: '165',
weight: '55'
}
]
},
openAddFitnessRecord() {
this.$refs.fitnessRecordPopup.openAdd()
},
openEditFitnessRecord(record) {
this.$refs.fitnessRecordPopup.openEdit(record)
},
handleFitnessRecordConfirm(result) {
uni.showToast({ title: '保存成功', icon: 'success' })
},
async getStudentList() {
try {
if (this.clientInfo?.resource_id) {
const res = await apiRoute.xs_getStudentList({ parent_resource_id: this.clientInfo.resource_id })
if (res.code === 1) {
// 处理接口返回的学生数据,转换字段
this.studentList = await this.processStudentData(res.data || [])
return
}
}
// 使用模拟数据(匹配真实接口字段结构)
const mockData = [
{
id: 1,
name: '张小明',
gender: 1, // 1=男, 2=女
birthday: '2015-05-10',
remark: '活泼开朗,学习能力强',
member_label: 1, // student_label表的ID,需要转换为标签名称
coach_id: 1, // personnel表的ID,需要转换为教练姓名
consultant_id: 2, // personnel表的ID,需要转换为顾问姓名
trial_class_count: 3,
// 其他原有字段
age: 9
}
]
// 处理模拟数据
this.studentList = await this.processStudentData(mockData)
} catch (error) {
console.error('获取学生列表失败:', error)
}
},
// 处理学生数据,转换字段为显示用的格式
async processStudentData(students) {
if (!students || students.length === 0) return []
const processedStudents = []
for (const student of students) {
const processedStudent = {
...student,
// 转换字段名称和处理逻辑
student_tags: await this.getStudentLabel(student.member_label),
class_teacher: await this.getPersonnelName(student.coach_id),
academic_affairs: await this.getPersonnelName(student.consultant_id),
trial_course_count: student.trial_class_count || 0,
// 使用后端API返回的真实到访状态数据
course_visit_status: this.formatVisitStatus(student.first_visit_status, student.second_visit_status)
}
processedStudents.push(processedStudent)
}
return processedStudents
},
// 获取学员标签名称
async getStudentLabel(labelId) {
if (!labelId) return []
// TODO: 调用接口获取student_label表中的标签名称
// const res = await apiRoute.getStudentLabel({ id: labelId })
// 模拟数据
const labelMap = {
1: '优秀学员',
2: '积极参与',
3: '需要关注'
}
return labelMap[labelId] ? [labelMap[labelId]] : []
},
// 获取人员姓名(教练或顾问)
async getPersonnelName(personnelId) {
if (!personnelId) return ''
// TODO: 调用接口获取personnel表中的人员姓名
// const res = await apiRoute.getPersonnelInfo({ id: personnelId })
// 模拟数据
const personnelMap = {
1: '李老师',
2: '王主任',
3: '张教练'
}
return personnelMap[personnelId] || ''
},
// 格式化到访状态 - 使用后端API返回的真实数据
formatVisitStatus(firstVisitStatus, secondVisitStatus) {
// 如果都未到访
if (firstVisitStatus === '未到访' && secondVisitStatus === '未到访') {
return '未到访'
}
// 如果一访已到,二访未到
else if (firstVisitStatus === '已到访' && secondVisitStatus === '未到访') {
return '一访已到'
}
// 如果一访和二访都已到
else if (firstVisitStatus === '已到访' && secondVisitStatus === '已到访') {
return '二访已到'
}
// 默认情况
else {
return '未到访'
}
},
openAddStudentDialog() {
this.$refs.studentEditPopup.openAdd()
},
async handleStudentEditConfirm(result) {
try {
// 调用学生添加API接口
const res = await apiRoute.xs_addStudent(result.studentData)
if (res.code === 1) {
uni.showToast({ title: '保存成功', icon: 'success' })
// 保存成功后刷新学生列表
await this.getStudentList()
} else {
uni.showToast({ title: res.msg || '保存失败', icon: 'none' })
}
} catch (error) {
console.error('保存学生信息失败:', error)
uni.showToast({ title: '保存失败', icon: 'none' })
}
},
// 获取学习计划列表
async getStudyPlanList(studentId = null) {
if (!this.clientInfo?.resource_id) return
const targetStudentId = studentId || this.currentStudent?.id
// 使用模拟数据
this.studyPlanList = [
{
id: 1,
student_id: targetStudentId || 1,
plan_name: '基础体能训练计划',
plan_content: '针对学员的基础体能进行系统性训练',
plan_type: '体能训练',
status: 'active',
progress: 65,
start_date: '2024-01-15',
end_date: '2024-03-15',
create_time: '2024-01-10 14:30:00'
}
]
},
// 获取订单列表
async getOrderList(studentId = null) {
if (!this.clientInfo?.resource_id) return
try {
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,
// 保留原始数据
_raw: order
}))
},
// 格式化支付类型
formatPaymentType(paymentType) {
const typeMap = {
'cash': '现金支付',
'scan_code': '扫码支付',
'subscription': '订阅支付',
'wxpay_online': '微信在线代付'
}
return typeMap[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
// 使用模拟数据
this.serviceList = [
{
id: 1,
student_id: targetStudentId || 1,
service_name: '体能评估服务',
description: '专业的体能测试和评估',
status: 'completed',
total_count: 1,
completed_count: 1,
logs: [
{
id: 1,
service_time: '2024-01-15 14:00:00',
service_content: '完成全面体能测试',
service_staff: '王教练',
duration: 60,
service_rating: 5,
status: 'completed'
}
]
}
]
},
// 打开新增订单弹窗
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(orderData) {
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
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' })
}
}
}
})
},
// 扫码支付
showQRCodePayment(order) {
uni.showModal({
title: '扫码支付',
content: `请使用支付宝或微信扫码支付 ¥${order.total_amount}`,
confirmText: '已完成支付',
cancelText: '取消支付',
success: async (res) => {
if (res.confirm) {
// 用户确认已完成扫码支付
await this.updateOrderStatus(order, 'paid', `QR${Date.now()}`)
}
}
})
},
// 订阅支付
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()
uni.showModal({
title: '订单详情',
content: detailText,
showCancel: false,
confirmText: '知道了'
})
},
// 获取订单状态文本
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
}
}
},
}
</script>
<style lang="less" scoped>
@import './clue_info.less';
</style>