Browse Source

修改 bug

develop
王泽彦 5 months ago
parent
commit
1d3fffba6a
  1. 380
      admin/src/app/views/personnel/components/BaseInfoForm.vue
  2. 350
      admin/src/app/views/personnel/components/DetailInfoForm.vue
  3. 844
      admin/src/app/views/personnel/components/personnel-edit.vue
  4. 32
      niucloud/app/api/controller/apiController/PersonCourseSchedule.php
  5. 51
      niucloud/app/api/controller/apiController/StudentCourse.php
  6. 2
      niucloud/app/api/route/route.php
  7. 40
      niucloud/app/common.php
  8. 1
      niucloud/app/service/api/apiService/CoachStudentService.php
  9. 237
      niucloud/app/service/api/apiService/CourseScheduleService.php
  10. 5
      niucloud/app/service/api/apiService/CourseService.php
  11. 239
      niucloud/app/service/api/apiService/PersonCourseScheduleService.php
  12. 5
      niucloud/app/service/api/student/ContractService.php
  13. 5
      uniapp/api/apiRoute.js
  14. 290
      uniapp/components/course-info-card/index.vue
  15. 24
      uniapp/pages-common/contract/staff-contract-sign.vue
  16. 1614
      uniapp/pages-market/course/course_detail.vue
  17. 10
      uniapp/pages.json

380
admin/src/app/views/personnel/components/BaseInfoForm.vue

@ -0,0 +1,380 @@
<template>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="base-info-form"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="t('name')" prop="name">
<el-input
v-model="formData.name"
clearable
:placeholder="t('namePlaceholder')"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('gender')">
<el-radio-group
v-model="formData.gender"
:placeholder="t('genderPlaceholder')"
>
<el-radio
v-for="(item, index) in genderList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="t('phone')" prop="phone">
<el-input
v-model="formData.phone"
clearable
:placeholder="t('phonePlaceholder')"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('address')">
<el-input
v-model="formData.address"
clearable
:placeholder="t('addressPlaceholder')"
class="input-width"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="t('nativePlace')">
<el-input
v-model="formData.native_place"
clearable
:placeholder="t('nativePlacePlaceholder')"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="头像">
<upload-image v-model="formData.head_img" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="t('education')">
<el-select
class="input-width"
v-model="formData.education"
clearable
:placeholder="t('educationPlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in educationList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('status')" prop="status">
<el-select
class="input-width"
v-model="formData.status"
clearable
:placeholder="t('statusPlaceholder')"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item :label="t('profile')">
<el-input
v-model="formData.profile"
type="textarea"
rows="3"
clearable
:placeholder="t('profilePlaceholder')"
class="input-width"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="t('emergencyContactPhone')">
<el-input
v-model="formData.emergency_contact_phone"
clearable
:placeholder="t('emergencyContactPhonePlaceholder')"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('isSysUser')" prop="is_sys_user">
<el-radio-group
v-model="formData.is_sys_user"
:placeholder="t('isSysUserPlaceholder')"
>
<el-radio
v-for="(item, index) in is_sys_userList"
:key="index"
:label="item.value"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item :label="t('idCardFront')">
<upload-image v-model="formData.id_card_front" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('idCardBack')">
<upload-image v-model="formData.id_card_back" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="所属校区" prop="campus_id">
<el-select
class="input-width"
v-model="formData.campus_id"
clearable
placeholder="请选择所属校区"
style="width: 300px;"
>
<el-option label="请选择" value=""></el-option>
<el-option
v-for="item in campusList"
:key="item.id"
:label="item.campus_name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch, onMounted } from 'vue'
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { getWithCampusList } from '@/app/api/campus_person_role'
// Props
interface Props {
id?: string | number
}
const props = withDefaults(defineProps<Props>(), {
id: ''
})
//
const initialFormData = {
name: '',
gender: '',
head_img: '',
phone: '',
address: '',
native_place: '',
education: '',
profile: '',
emergency_contact_phone: '',
id_card_front: '',
id_card_back: '',
status: '',
is_sys_user: '',
campus_id: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
name: [{ required: true, message: t('namePlaceholder'), trigger: 'blur' }],
gender: [
{ required: true, message: t('genderPlaceholder'), trigger: 'blur' },
],
phone: [
{ required: true, message: t('phonePlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
is_sys_user: [
{ required: true, message: t('isSysUserPlaceholder'), trigger: 'blur' },
],
}
})
//
let genderList = ref([])
let educationList = ref([])
let statusList = ref([])
let is_sys_userList = ref([])
let campusList = ref([])
//
const genderDictList = async () => {
genderList.value = await (await useDictionary('gender')).data.dictionary
}
const educationDictList = async () => {
educationList.value = await (await useDictionary('education')).data.dictionary
}
const statusDictList = async () => {
statusList.value = await (await useDictionary('personnel_status')).data.dictionary
}
const is_sys_userDictList = async () => {
is_sys_userList.value = await (await useDictionary('global_true_or_false')).data.dictionary
}
const getCampusList = async () => {
const res = await getWithCampusList({})
if (res.data) {
campusList.value = res.data
}
}
//
watch(
() => genderList.value,
() => {
if (genderList.value.length > 0 && !formData.gender) {
formData.gender = genderList.value[0].value
}
}
)
watch(
() => educationList.value,
() => {
if (educationList.value.length > 0 && !formData.education) {
formData.education = educationList.value[0].value
}
}
)
watch(
() => statusList.value,
() => {
if (statusList.value.length > 0 && !formData.status) {
formData.status = statusList.value[0].value
}
}
)
watch(
() => is_sys_userList.value,
() => {
if (is_sys_userList.value.length > 0 && !formData.is_sys_user) {
formData.is_sys_user = is_sys_userList.value[0].value
}
}
)
//
const setFormData = async (data: any = null) => {
Object.assign(formData, initialFormData)
if (data) {
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) {
formData[key] = data[key]
}
})
}
}
//
const getFormData = () => {
return { ...formData }
}
//
const validateForm = async () => {
if (!formRef.value) return false
return await formRef.value.validate()
}
//
onMounted(() => {
genderDictList()
educationDictList()
statusDictList()
is_sys_userDictList()
getCampusList()
})
//
defineExpose({
setFormData,
getFormData,
validateForm,
formData
})
</script>
<style lang="scss" scoped>
.base-info-form {
.input-width {
width: 100%;
}
.el-form-item {
margin-bottom: 18px;
}
.el-textarea {
:deep(.el-textarea__inner) {
resize: vertical;
}
}
}
</style>

350
admin/src/app/views/personnel/components/DetailInfoForm.vue

@ -0,0 +1,350 @@
<template>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="detail-info-form"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="花名" prop="name">
<el-input
v-model="formData.name"
placeholder="请填写花名"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="门店" prop="store">
<el-input
v-model="formData.store"
placeholder="请填写门店"
class="input-width"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="民族" prop="ethnicity">
<el-input
v-model="formData.ethnicity"
placeholder="请填写民族"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="生日日期" prop="birthday">
<el-date-picker
v-model="formData.birthday"
type="date"
placeholder="请选择生日日期"
class="input-width"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="年龄" prop="age">
<el-input
v-model="formData.age"
placeholder="请填写年龄"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="司龄" prop="tenure">
<el-input
v-model="formData.tenure"
placeholder="请填写司龄"
class="input-width"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="转正时间" prop="regular_date">
<el-date-picker
v-model="formData.regular_date"
type="date"
placeholder="请选择转正时间"
class="input-width"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否转正" prop="is_regular">
<el-select
v-model="formData.is_regular"
placeholder="请选择是否转正"
class="input-width"
clearable
>
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="政治面貌" prop="politics">
<el-input
v-model="formData.politics"
placeholder="请填写政治面貌"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="毕业院校" prop="university">
<el-input
v-model="formData.university"
placeholder="请填写毕业院校"
class="input-width"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="专业" prop="major">
<el-input
v-model="formData.major"
placeholder="请填写专业"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="毕业时间" prop="graduation_date">
<el-date-picker
v-model="formData.graduation_date"
type="date"
placeholder="请选择毕业时间"
class="input-width"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="户籍所在地" prop="household_place">
<el-input
v-model="formData.household_place"
placeholder="请填写户籍所在地"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="户籍类型" prop="household_type">
<el-input
v-model="formData.household_type"
placeholder="请填写户籍类型"
class="input-width"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="婚否" prop="marital_status">
<el-select
v-model="formData.marital_status"
placeholder="请选择婚否"
class="input-width"
clearable
>
<el-option label="已婚" value="已婚" />
<el-option label="未婚" value="未婚" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="银行卡号" prop="bank_card">
<el-input
v-model="formData.bank_card"
placeholder="请填写银行卡号"
class="input-width"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="开户行" prop="bank_name">
<el-input
v-model="formData.bank_name"
placeholder="请填写开户行"
class="input-width"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合同到期时间" prop="contract_expire">
<el-date-picker
v-model="formData.contract_expire"
type="date"
placeholder="请选择合同到期时间"
class="input-width"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="是否复聘" prop="is_rehired">
<el-select
v-model="formData.is_rehired"
placeholder="请选择是否复聘"
class="input-width"
clearable
>
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注" prop="remark">
<el-input
v-model="formData.remark"
placeholder="请填写备注"
class="input-width"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import type { FormInstance } from 'element-plus'
// Props
interface Props {
id?: string | number
}
const props = withDefaults(defineProps<Props>(), {
id: ''
})
//
const initialFormData = {
name: '',
store: '',
ethnicity: '',
birthday: '',
age: '',
tenure: '',
regular_date: '',
is_regular: '',
politics: '',
university: '',
education: '',
major: '',
graduation_date: '',
native_place: '',
household_place: '',
household_type: '',
household_address: '',
current_address: '',
emergency_contact: '',
emergency_phone: '',
marital_status: '',
bank_card: '',
bank_name: '',
contract_expire: '',
is_rehired: '',
remark: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
//
name: [{ required: false, message: '请填写花名', trigger: 'blur' }],
store: [{ required: false, message: '请填写门店', trigger: 'blur' }],
}
})
//
const setFormData = async (data: any = null) => {
Object.assign(formData, initialFormData)
if (data) {
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) {
formData[key] = data[key]
}
})
}
}
//
const getFormData = () => {
return { ...formData }
}
//
const validateForm = async () => {
if (!formRef.value) return false
return await formRef.value.validate()
}
//
defineExpose({
setFormData,
getFormData,
validateForm,
formData
})
</script>
<style lang="scss" scoped>
.detail-info-form {
.input-width {
width: 100%;
}
.el-form-item {
margin-bottom: 18px;
}
.el-date-picker {
width: 100%;
:deep(.el-input__wrapper) {
width: 100%;
}
:deep(.el-input) {
width: 100%;
}
}
}
</style>

844
admin/src/app/views/personnel/components/personnel-edit.vue

@ -1,656 +1,274 @@
<template> <template>
<el-dialog v-model="showDialog" :title="formData.id ? t('updatePersonnel') : t('addPersonnel')" width="50%" <el-dialog
class="diy-dialog-wrap" :destroy-on-close="true"> v-model="showDialog"
:title="formData.id ? t('updatePersonnel') : t('addPersonnel')"
<el-tabs v-model="activeTab" class="tab-pane-half"> width="70%"
class="personnel-edit-dialog"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-tabs v-model="activeTab" class="personnel-tabs">
<!-- Tab 1: 基本信息 --> <!-- Tab 1: 基本信息 -->
<el-tab-pane label="基本信息" name="base"> <el-tab-pane label="基本信息" name="base">
<div class="tab-content">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form"> <BaseInfoForm
ref="baseInfoFormRef"
<el-form-item :label="t('name')" prop="name"> :id="formData.id"
<el-input v-model="formData.name" clearable :placeholder="t('namePlaceholder')" />
class="input-width" /> </div>
</el-form-item>
<el-form-item :label="t('gender')">
<el-radio-group v-model="formData.gender" :placeholder="t('genderPlaceholder')">
<el-radio v-for="(item, index) in genderList" :key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('phone')" prop="phone">
<el-input v-model="formData.phone" clearable :placeholder="t('phonePlaceholder')"
class="input-width" />
</el-form-item>
<el-form-item :label="t('address')">
<el-input v-model="formData.address" clearable :placeholder="t('addressPlaceholder')"
class="input-width" />
</el-form-item>
<el-form-item :label="t('nativePlace')">
<el-input v-model="formData.native_place" clearable :placeholder="t('nativePlacePlaceholder')"
class="input-width" />
</el-form-item>
<el-form-item label="头像">
<upload-image v-model="formData.head_img" />
</el-form-item>
<el-form-item :label="t('education')">
<el-select class="input-width" v-model="formData.education" clearable
:placeholder="t('educationPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option v-for="(item, index) in educationList" :key="index" :label="item.name"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('profile')">
<el-input v-model="formData.profile" type="textarea" rows="4" clearable
:placeholder="t('profilePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('emergencyContactPhone')">
<el-input v-model="formData.emergency_contact_phone" clearable
:placeholder="t('emergencyContactPhonePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('idCardFront')">
<upload-image v-model="formData.id_card_front" />
</el-form-item>
<el-form-item :label="t('idCardBack')">
<upload-image v-model="formData.id_card_back" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select class="input-width" v-model="formData.status" clearable
:placeholder="t('statusPlaceholder')">
<el-option label="请选择" value=""></el-option>
<el-option v-for="(item, index) in statusList" :key="index" :label="item.name"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('isSysUser')" prop="is_sys_user">
<el-radio-group v-model="formData.is_sys_user" :placeholder="t('isSysUserPlaceholder')">
<el-radio v-for="(item, index) in is_sys_userList" :key="index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="所属校区" prop="campus_id">
<el-select class="input-width" v-model="formData.campus_id" clearable placeholder="请选择所属校区">
<el-option label="请选择" value=""></el-option>
<el-option v-for="item in campusList" :key="item.id" :label="item.campus_name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
</el-tab-pane> </el-tab-pane>
<!-- Tab 2: 详情信息 -->
<el-tab-pane label="详情信息" name="info"> <el-tab-pane label="详情信息" name="info">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form"> <div class="tab-content">
<DetailInfoForm
<el-row :gutter="20"> ref="detailInfoFormRef"
<el-col :span="12"> :id="formData.id"
<el-form-item label="花名" prop="info.name"> />
<el-input v-model="formData.info.name" placeholder="请填写花名" class="input-width" /> </div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="门店" prop="info.store">
<el-input v-model="formData.info.store" placeholder="请填写门店" class="input-width" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="民族" prop="info.ethnicity">
<el-input v-model="formData.info.ethnicity" placeholder="请填写民族" class="input-width" />
</el-form-item>
</el-col>
<el-form-item label="生日日期" prop="info.birthday">
<el-date-picker
v-model="formData.info.birthday"
type="date"
placeholder="请选择生日日期"
class="input-width"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="年龄" prop="info.age">
<el-input v-model="formData.info.age" placeholder="请填写年龄" class="input-width" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="司龄" prop="info.tenure">
<el-input v-model="formData.info.tenure" placeholder="请填写司龄" class="input-width" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="转正时间" prop="info.regular_date">
<el-date-picker
v-model="formData.info.regular_date"
type="date"
placeholder="请选择转正时间"
class="input-width"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否转正" prop="info.is_regular">
<el-select
v-model="formData.info.is_regular"
placeholder="请选择是否转正"
class="input-width"
clearable
>
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="政治面貌" prop="info.politics">
<el-input v-model="formData.info.politics" placeholder="请填写政治面貌" class="input-width" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="毕业院校" prop="info.university">
<el-input v-model="formData.info.university" placeholder="请填写毕业院校" class="input-width" />
</el-form-item>
</el-col>
</el-row>
<!-- <el-row :gutter="20">
<el-col :span="12">
<el-form-item label="学历" prop="info.education">
<el-input v-model="formData.info.education" placeholder="请填写学历" class="input-width" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业" prop="info.major">
<el-input v-model="formData.info.major" placeholder="请填写专业" class="input-width" />
</el-form-item>
</el-col>
</el-row> -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="专业" prop="info.major">
<el-input v-model="formData.info.major" placeholder="请填写专业" class="input-width" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="毕业时间" prop="info.graduation_date">
<el-date-picker
v-model="formData.info.graduation_date"
type="date"
placeholder="请选择毕业时间"
class="input-width"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="户籍所在地" prop="info.household_place">
<el-input v-model="formData.info.household_place" placeholder="请填写户籍所在地" class="input-width" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="户籍类型" prop="info.household_type">
<el-input v-model="formData.info.household_type" placeholder="请填写户籍类型" class="input-width" />
</el-form-item>
</el-col>
</el-row>
<!-- <el-row :gutter="20">
<el-col :span="12">
<el-form-item label="户籍地址" prop="info.household_address">
<el-input v-model="formData.info.household_address" placeholder="请填写户籍地址" class="input-width" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="现居地址" prop="info.current_address">
<el-input v-model="formData.info.current_address" placeholder="请填写现居地址" class="input-width" />
</el-form-item>
</el-col>
</el-row> -->
<!--
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="紧急联系人" prop="info.emergency_contact">
<el-input v-model="formData.info.emergency_contact" placeholder="请填写紧急联系人" class="input-width" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="紧急联系人联系电话" prop="info.emergency_phone">
<el-input v-model="formData.info.emergency_phone" placeholder="请填写紧急联系人联系电话" class="input-width" />
</el-form-item>
</el-col>
</el-row> -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="婚否" prop="info.marital_status">
<el-select
v-model="formData.info.marital_status"
placeholder="请选择婚否"
class="input-width"
clearable
>
<el-option label="已婚" value="已婚" />
<el-option label="未婚" value="未婚" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="银行卡号" prop="info.bank_card">
<el-input v-model="formData.info.bank_card" placeholder="请填写银行卡号" class="input-width" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="开户行" prop="info.bank_name">
<el-input v-model="formData.info.bank_name" placeholder="请填写开户行" class="input-width" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合同到期时间" prop="info.contract_expire">
<el-date-picker
v-model="formData.info.contract_expire"
type="date"
placeholder="请选择合同到期时间"
class="input-width"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="是否复聘" prop="info.is_rehired">
<el-select
v-model="formData.info.is_rehired"
placeholder="请选择是否复聘"
class="input-width"
clearable
>
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注" prop="info.remark">
<el-input v-model="formData.info.remark" placeholder="请填写备注" class="input-width" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button> <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" <el-button type="primary" :loading="loading"
@click="confirm(formRef)">{{ t('confirm') }}</el-button> @click="confirm">{{ t('confirm') }}</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, computed, watch, onMounted } from 'vue' import { ref, reactive } from 'vue'
import { useDictionary } from '@/app/api/dict' import { t } from '@/lang'
import { t } from '@/lang' import {
import type { FormInstance } from 'element-plus' addPersonnel,
import { editPersonnel,
addPersonnel, getPersonnelInfo,
editPersonnel, } from '@/app/api/personnel'
getPersonnelInfo, import BaseInfoForm from './BaseInfoForm.vue'
} from '@/app/api/personnel' import DetailInfoForm from './DetailInfoForm.vue'
import { getWithCampusList } from '@/app/api/campus_person_role'
let showDialog = ref(false)
let showDialog = ref(false) const loading = ref(false)
const loading = ref(false) const activeTab = ref('base')
const activeTab = ref('base')
/** //
* 表单数据 const baseInfoFormRef = ref()
*/ const detailInfoFormRef = ref()
const initialFormData = {
id: '', /**
name: '', * 表单数据
gender: '', */
head_img: '', const initialFormData = {
phone: '', id: '',
address: '', info: {}
native_place: '', }
education: '',
profile: '', const formData: Record<string, any> = reactive({ ...initialFormData })
emergency_contact_phone: '',
id_card_front: '', const emit = defineEmits(['complete'])
id_card_back: '',
status: '', /**
is_sys_user: '', * 确认
campus_id: '', */
info:{ const confirm = async () => {
name:'', if (loading.value) return
store:'',
ethnicity:'', //
birthday:'', const baseInfoValid = await baseInfoFormRef.value?.validateForm()
age:'', const detailInfoValid = await detailInfoFormRef.value?.validateForm()
tenure:'',
regular_date:'', if (!baseInfoValid) {
is_regular:'', activeTab.value = 'base'
politics:'', return
university:'',
education:'',
major:'',
graduation_date:'',
native_place:'',
household_place:'',
household_type:'',
household_address:'',
current_address:'',
emergency_contact:'',
emergency_phone:'',
marital_status:'',
bank_card:'',
bank_name:'',
contract_expire:'',
is_rehired:'',
remark:''
}
}
const formData : Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
//
const formRules = computed(() => {
return {
name: [{ required: true, message: t('namePlaceholder'), trigger: 'blur' }],
gender: [
{ required: true, message: t('genderPlaceholder'), trigger: 'blur' },
],
phone: [
{ required: true, message: t('phonePlaceholder'), trigger: 'blur' },
],
address: [
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' },
],
native_place: [
{ required: true, message: t('nativePlacePlaceholder'), trigger: 'blur' },
],
education: [
{ required: true, message: t('educationPlaceholder'), trigger: 'blur' },
],
profile: [
{ required: true, message: t('profilePlaceholder'), trigger: 'blur' },
],
emergency_contact_phone: [
{
required: true,
message: t('emergencyContactPhonePlaceholder'),
trigger: 'blur',
},
],
id_card_front: [
{ required: true, message: t('idCardFrontPlaceholder'), trigger: 'blur' },
],
id_card_back: [
{ required: true, message: t('idCardBackPlaceholder'), trigger: 'blur' },
],
status: [
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' },
],
is_sys_user: [
{ required: true, message: t('isSysUserPlaceholder'), trigger: 'blur' },
],
}
})
const emit = defineEmits(['complete'])
/**
* 确认
* @param formEl
*/
const confirm = async (formEl : FormInstance | undefined) => {
if (loading.value || !formEl) return
let save = formData.id ? editPersonnel : addPersonnel
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = formData
save(data)
.then((res) => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
loading.value = false
})
}
})
} }
// loading.value = true
let genderList = ref([])
const genderDictList = async () => { try {
genderList.value = await (await useDictionary('gender')).data.dictionary //
} const baseData = baseInfoFormRef.value?.getFormData() || {}
genderDictList() const detailData = detailInfoFormRef.value?.getFormData() || {}
watch(
() => genderList.value, const submitData = {
() => { ...baseData,
formData.gender = genderList.value[0].value ...formData,
} info: detailData
)
let educationList = ref([])
const educationDictList = async () => {
educationList.value = await (await useDictionary('education')).data.dictionary
}
educationDictList()
watch(
() => educationList.value,
() => {
formData.education = educationList.value[0].value
}
)
let statusList = ref([])
const statusDictList = async () => {
statusList.value = await (
await useDictionary('personnel_status')
).data.dictionary
}
statusDictList()
watch(
() => statusList.value,
() => {
formData.status = statusList.value[0].value
}
)
let is_sys_userList = ref([])
const is_sys_userDictList = async () => {
is_sys_userList.value = await (
await useDictionary('global_true_or_false')
).data.dictionary
}
is_sys_userDictList()
watch(
() => is_sys_userList.value,
() => {
formData.is_sys_user = is_sys_userList.value[0].value
}
)
//
let campusList = ref([])
const getCampusList = async () => {
const res = await getWithCampusList({})
if (res.data) {
campusList.value = res.data
} }
const save = formData.id ? editPersonnel : addPersonnel
await save(submitData)
loading.value = false
showDialog.value = false
emit('complete')
} catch (err) {
loading.value = false
} }
}
onMounted(() => { /**
getCampusList() * 设置表单数据
}) */
const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
loading.value = true
const setFormData = async (row : any = null) => { try {
Object.assign(formData, initialFormData)
loading.value = true
if (row) { if (row) {
const data = await (await getPersonnelInfo(row.id)).data const data = await (await getPersonnelInfo(row.id)).data
if (data) if (data) {
Object.keys(formData).forEach((key : string) => { formData.id = data.id
if (data[key] != undefined) formData[key] = data[key] //
}) await baseInfoFormRef.value?.setFormData(data)
//
await detailInfoFormRef.value?.setFormData(data.info || {})
}
} else {
//
await baseInfoFormRef.value?.setFormData()
await detailInfoFormRef.value?.setFormData()
} }
} catch (error) {
console.error('设置表单数据失败:', error)
} finally {
loading.value = false loading.value = false
} }
}
// defineExpose({
const mobileVerify = (rule : any, value : any, callback : any) => { showDialog,
if (value && !/^1[3-9]\d{9}$/.test(value)) { setFormData,
callback(new Error(t('generateMobile'))) })
} else { </script>
callback()
} <style lang="scss" scoped>
.personnel-edit-dialog {
:deep(.el-dialog) {
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
} }
// :deep(.el-dialog__header) {
const idCardVerify = (rule : any, value : any, callback : any) => { padding: 20px 24px 16px;
if ( border-bottom: 1px solid #f0f0f0;
value && background: #fafafa;
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test( border-radius: 8px 8px 0 0;
value
) .el-dialog__title {
) { font-size: 18px;
callback(new Error(t('generateIdCard'))) font-weight: 600;
} else { color: #1f2937;
callback()
} }
} }
// :deep(.el-dialog__body) {
const emailVerify = (rule : any, value : any, callback : any) => { padding: 24px;
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { max-height: 70vh;
callback(new Error(t('generateEmail'))) overflow-y: auto;
} else {
callback()
}
} }
// :deep(.el-dialog__footer) {
const numberVerify = (rule : any, value : any, callback : any) => { padding: 16px 24px 20px;
if (!Number.isInteger(value)) { border-top: 1px solid #f0f0f0;
callback(new Error(t('generateNumber'))) background: #fafafa;
} else { border-radius: 0 0 8px 8px;
callback() }
}
.personnel-tabs {
:deep(.el-tabs__header) {
margin-bottom: 24px;
border-bottom: 2px solid #e5e7eb;
.el-tabs__nav-wrap {
&::after {
display: none;
}
}
.el-tabs__item {
padding: 12px 24px;
font-size: 15px;
font-weight: 500;
color: #6b7280;
border-bottom: none;
transition: all 0.3s ease;
&:hover {
color: #3b82f6;
}
&.is-active {
color: #3b82f6;
background: #eff6ff;
border: 1px solid #3b82f6;
border-bottom: 1px solid #eff6ff;
border-radius: 8px 8px 0 0;
position: relative;
top: 2px;
}
} }
} }
defineExpose({ .tab-content {
showDialog, padding: 20px;
setFormData, background: #ffffff;
}) border: 1px solid #e5e7eb;
</script> border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
}
.dialog-footer {
.el-button {
padding: 10px 24px;
font-size: 14px;
font-weight: 500;
border-radius: 6px;
transition: all 0.3s ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
}
}
</style>
<style lang="scss" scoped></style>
<style lang="scss"> <style lang="scss">
.diy-dialog-wrap .el-form-item__label { .personnel-edit-dialog .el-form-item__label {
height: auto !important; height: auto !important;
line-height: 1.6;
}
.personnel-edit-dialog .el-tabs__content {
overflow: visible;
}
.personnel-edit-dialog .el-dialog__body {
padding: 24px;
}
//
.personnel-edit-dialog .el-dialog__body::-webkit-scrollbar {
width: 6px;
}
.personnel-edit-dialog .el-dialog__body::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.personnel-edit-dialog .el-dialog__body::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
&:hover {
background: #a8a8a8;
} }
}
</style> </style>

32
niucloud/app/api/controller/apiController/PersonCourseSchedule.php

@ -161,17 +161,17 @@ class PersonCourseSchedule extends BaseApiService
$resource_id = $request->param('resource_id', '');//客户资源ID $resource_id = $request->param('resource_id', '');//客户资源ID
$member_id = $request->param('member_id', '');//会员ID $member_id = $request->param('member_id', '');//会员ID
$student_id = $request->param('student_id', '');//学生ID $student_id = $request->param('student_id', '');//学生ID
if (empty($resource_id)) { if (empty($resource_id)) {
return fail('缺少参数resource_id'); return fail('缺少参数resource_id');
} }
$where = [ $where = [
'resource_id' => $resource_id, 'resource_id' => $resource_id,
'member_id' => $member_id, 'member_id' => $member_id,
'student_id' => $student_id, 'student_id' => $student_id,
]; ];
$res = (new PersonCourseScheduleService())->getStudentCourseInfo($where); $res = (new PersonCourseScheduleService())->getStudentCourseInfo($where);
if(!$res['code']){ if(!$res['code']){
return fail($res['msg']); return fail($res['msg']);
@ -194,21 +194,41 @@ class PersonCourseSchedule extends BaseApiService
$main_coach_id = $request->param('main_coach_id', '');//主教练ID $main_coach_id = $request->param('main_coach_id', '');//主教练ID
$education_id = $request->param('education_id', '');//教务ID $education_id = $request->param('education_id', '');//教务ID
$assistant_ids = $request->param('assistant_ids', '');//助教IDs,逗号分隔 $assistant_ids = $request->param('assistant_ids', '');//助教IDs,逗号分隔
if (empty($student_course_id)) { if (empty($student_course_id)) {
return fail('缺少参数student_course_id'); return fail('缺少参数student_course_id');
} }
$data = [ $data = [
'main_coach_id' => $main_coach_id, 'main_coach_id' => $main_coach_id,
'education_id' => $education_id, 'education_id' => $education_id,
'assistant_ids' => $assistant_ids, 'assistant_ids' => $assistant_ids,
]; ];
$res = (new PersonCourseScheduleService())->updateStudentCoursePersonnel($student_course_id, $data); $res = (new PersonCourseScheduleService())->updateStudentCoursePersonnel($student_course_id, $data);
if(!$res['code']){ if(!$res['code']){
return fail($res['msg']); return fail($res['msg']);
} }
return success($res['data']); return success($res['data']);
} }
//获取学生课程详情包含已上课的情况和订单情况
public function getStudentCourseDetail(Request $request)
{
$student_course_id = $request->param('student_course_id', '');
if (empty($student_course_id)) {
return fail('缺少参数student_course_id');
}
$where = [
'student_course_id' => $student_course_id,
];
$res = (new PersonCourseScheduleService())->getStudentCourseDetail($where);
if(!$res['code']){
return fail($res['msg']);
}
return success($res['data']);
}
} }

51
niucloud/app/api/controller/apiController/StudentCourse.php

@ -32,39 +32,39 @@ class StudentCourse extends BaseApiService
{ {
$course_id = $request->param('course_id', ''); $course_id = $request->param('course_id', '');
$resource_id = $request->param('resource_id', ''); $resource_id = $request->param('resource_id', '');
if (empty($course_id)) { if (empty($course_id)) {
return fail('课程ID不能为空'); return fail('课程ID不能为空');
} }
// 如果没有传resource_id,尝试从当前登录用户获取 // 如果没有传resource_id,尝试从当前登录用户获取
if (empty($resource_id)) { if (empty($resource_id)) {
// 这里需要根据实际情况获取当前学员的resource_id // 这里需要根据实际情况获取当前学员的resource_id
// 可能需要从member_id获取对应的resource_id // 可能需要从member_id获取对应的resource_id
$resource_id = $this->getResourceIdByMemberId($this->member_id); $resource_id = $this->getResourceIdByMemberId($this->member_id);
} }
if (empty($resource_id)) { if (empty($resource_id)) {
return fail('资源ID不能为空'); return fail('资源ID不能为空');
} }
$where = [ $where = [
'course_id' => $course_id, 'course_id' => $course_id,
'resource_id' => $resource_id 'resource_id' => $resource_id
]; ];
try { try {
$res = (new StudentCourseService())->getCourseDetail($where); $res = (new StudentCourseService())->getCourseDetail($where);
if (!$res['code']) { if (!$res['code']) {
return fail($res['msg']); return fail($res['msg']);
} }
return success($res['data']); return success($res['data']);
} catch (\Exception $e) { } catch (\Exception $e) {
return fail('获取课程详情失败:' . $e->getMessage()); return fail('获取课程详情失败:' . $e->getMessage());
} }
} }
/** /**
* 获取学员服务记录 * 获取学员服务记录
* @param Request $request * @param Request $request
@ -73,28 +73,28 @@ class StudentCourse extends BaseApiService
public function getServiceList(Request $request) public function getServiceList(Request $request)
{ {
$student_id = $request->param('student_id', ''); $student_id = $request->param('student_id', '');
// 如果没有传student_id,尝试从当前登录用户获取 // 如果没有传student_id,尝试从当前登录用户获取
if (empty($student_id)) { if (empty($student_id)) {
$student_id = $this->getResourceIdByMemberId($this->member_id); $student_id = $this->getResourceIdByMemberId($this->member_id);
} }
if (empty($student_id)) { if (empty($student_id)) {
return fail('学员ID不能为空'); return fail('学员ID不能为空');
} }
try { try {
$res = (new ServiceService())->getStudentServiceList((int)$student_id); $res = (new ServiceService())->getStudentServiceList((int)$student_id);
if (!$res['code']) { if (!$res['code']) {
return fail($res['msg']); return fail($res['msg']);
} }
return success($res['data']); return success($res['data']);
} catch (\Exception $e) { } catch (\Exception $e) {
return fail('获取服务记录失败:' . $e->getMessage()); return fail('获取服务记录失败:' . $e->getMessage());
} }
} }
/** /**
* 获取教练列表 * 获取教练列表
* @param Request $request * @param Request $request
@ -108,13 +108,13 @@ class StudentCourse extends BaseApiService
if (!$res['code']) { if (!$res['code']) {
return fail($res['msg']); return fail($res['msg']);
} }
return success($res['data']); return success($res['data']);
} catch (\Exception $e) { } catch (\Exception $e) {
return fail('获取教练列表失败:' . $e->getMessage()); return fail('获取教练列表失败:' . $e->getMessage());
} }
} }
/** /**
* 获取教务人员列表 * 获取教务人员列表
* @param Request $request * @param Request $request
@ -128,13 +128,13 @@ class StudentCourse extends BaseApiService
if (!$res['code']) { if (!$res['code']) {
return fail($res['msg']); return fail($res['msg']);
} }
return success($res['data']); return success($res['data']);
} catch (\Exception $e) { } catch (\Exception $e) {
return fail('获取教务人员列表失败:' . $e->getMessage()); return fail('获取教务人员列表失败:' . $e->getMessage());
} }
} }
/** /**
* 更新学员课程信息 * 更新学员课程信息
* @param Request $request * @param Request $request
@ -150,22 +150,22 @@ class StudentCourse extends BaseApiService
["education_id", 0], ["education_id", 0],
["class_id", 0] // 可选,如果需要更新班级关联 ["class_id", 0] // 可选,如果需要更新班级关联
]); ]);
if (empty($data['student_course_id'])) { if (empty($data['student_course_id'])) {
return fail('学员课程ID不能为空'); return fail('学员课程ID不能为空');
} }
$res = (new StudentCourseService())->updateCourseInfo($data); $res = (new StudentCourseService())->updateCourseInfo($data);
if (!$res['code']) { if (!$res['code']) {
return fail($res['msg']); return fail($res['msg']);
} }
return success('更新成功', $res['data']); return success('更新成功', $res['data']);
} catch (\Exception $e) { } catch (\Exception $e) {
return fail('更新失败:' . $e->getMessage()); return fail('更新失败:' . $e->getMessage());
} }
} }
/** /**
* 检查学员班级关联情况 * 检查学员班级关联情况
* @param Request $request * @param Request $request
@ -175,21 +175,21 @@ class StudentCourse extends BaseApiService
{ {
try { try {
$resource_id = $request->param('resource_id', ''); $resource_id = $request->param('resource_id', '');
if (empty($resource_id)) { if (empty($resource_id)) {
// 尝试从当前登录用户获取 // 尝试从当前登录用户获取
$resource_id = $this->getResourceIdByMemberId($this->member_id); $resource_id = $this->getResourceIdByMemberId($this->member_id);
} }
if (empty($resource_id)) { if (empty($resource_id)) {
return fail('资源ID不能为空'); return fail('资源ID不能为空');
} }
$res = (new StudentCourseService())->checkClassRelation($resource_id); $res = (new StudentCourseService())->checkClassRelation($resource_id);
if (!$res['code']) { if (!$res['code']) {
return fail($res['msg']); return fail($res['msg']);
} }
return success($res['data']); return success($res['data']);
} catch (\Exception $e) { } catch (\Exception $e) {
return fail('检查班级关联失败:' . $e->getMessage()); return fail('检查班级关联失败:' . $e->getMessage());
@ -208,4 +208,5 @@ class StudentCourse extends BaseApiService
$customerResource = \app\model\customer_resources\CustomerResources::where('member_id', $member_id)->find(); $customerResource = \app\model\customer_resources\CustomerResources::where('member_id', $member_id)->find();
return $customerResource ? $customerResource->id : ''; return $customerResource ? $customerResource->id : '';
} }
}
}

2
niucloud/app/api/route/route.php

@ -582,6 +582,8 @@ Route::group(function () {
//获取学生课程信息列表(包含教练配置) //获取学生课程信息列表(包含教练配置)
Route::get('getStudentCourseInfo', 'apiController.PersonCourseSchedule/getStudentCourseInfo'); Route::get('getStudentCourseInfo', 'apiController.PersonCourseSchedule/getStudentCourseInfo');
//获取学生课程详情包含已上课的情况和订单情况
Route::post('getStudentCourseDetail', 'apiController.PersonCourseSchedule/getStudentCourseDetail');
//获取人员列表(教练、教务、助教) //获取人员列表(教练、教务、助教)
Route::get('getPersonnelList', 'apiController.PersonCourseSchedule/getPersonnelList'); Route::get('getPersonnelList', 'apiController.PersonCourseSchedule/getPersonnelList');
//更新学生课程人员配置 //更新学生课程人员配置

40
niucloud/app/common.php

@ -2305,3 +2305,43 @@ function save_user_signature($data)
return false; return false;
} }
} }
// ==================== 合同占位符处理函数 ====================
/**
* 将合同内容中的占位符替换为下划线输入框
* 使用正则表达式将 {{占位符}} 格式替换为指定长度的下划线
*
* @param string $contractContent 合同内容
* @param int $underlineLength 下划线长度(默认为10个字符)
* @return string 替换后的合同内容
*/
function replace_placeholders_with_underlines($contractContent, $underlineLength = 10)
{
if (empty($contractContent)) {
return $contractContent;
}
// 使用正则表达式匹配 {{}} 格式的占位符
// \{\{ 匹配 {{
// [^}]+ 匹配一个或多个非 } 字符(占位符内容)
// \}\} 匹配 }}
$pattern = '/\{\{[^}]+\}\}/';
// 生成替换字符串 - 根据占位符内容长度动态调整下划线数量
$replacement = function($matches) use ($underlineLength) {
$placeholder = $matches[0];
// 提取占位符中的内容
$content = preg_replace('/^\{\{|\}\}$/', '', $placeholder);
// 根据内容长度动态决定下划线数量,最少8个,最多20个
$contentLength = mb_strlen($content, 'UTF-8');
$adjustedLength = max(8, min(20, $contentLength + 4));
return str_repeat('_', $adjustedLength); // 使用全角下划线,显示效果更好
};
// 执行替换
return preg_replace_callback($pattern, $replacement, $contractContent);
}

1
niucloud/app/service/api/apiService/CoachStudentService.php

@ -285,7 +285,6 @@ class CoachStudentService extends BaseApiService
// resource_sharing_id 是 school_resource_assignment 表的 id // resource_sharing_id 是 school_resource_assignment 表的 id
$resourceAssignment = Db::table('school_resource_assignment') $resourceAssignment = Db::table('school_resource_assignment')
->where('resource_id', $customerResource['id']) ->where('resource_id', $customerResource['id'])
->where('assignee_type', 'user')
->field('id') ->field('id')
->order('assigned_at', 'desc') ->order('assigned_at', 'desc')
->find(); ->find();

237
niucloud/app/service/api/apiService/CourseScheduleService.php

@ -2771,6 +2771,231 @@ class CourseScheduleService extends BaseApiService
} }
} }
/**
* 处理正式学员课程消减逻辑
* @param array $enrollment 学员课程安排记录
* @param Student $student 学员信息
* @throws \Exception
*/
private function handlePaidStudentCourseDeduction($enrollment, $student)
{
try {
$scheduleId = $enrollment['schedule_id'] ?? 0;
if (empty($scheduleId)) {
throw new \Exception('课程安排ID不能为空');
}
// 获取课程安排信息以确定课程日期
$schedule = Db::name('course_schedule')
->where('id', $scheduleId)
->find();
if (!$schedule) {
throw new \Exception('找不到课程安排信息');
}
$courseDate = $schedule['course_date'];
// 根据课程日期动态查找应该核销的课程包
$studentCourse = $this->findApplicableStudentCourse($student->id, $courseDate);
if (!$studentCourse) {
throw new \Exception('找不到该日期有效的学员课程包');
}
// 获取课程信息以计算消耗课时数
$course = Db::name('course')
->where('id', $studentCourse['course_id'])
->find();
if (!$course) {
throw new \Exception('找不到课程信息');
}
// 计算本次消耗的课时数
$deductHours = $this->calculateCourseDeduction($course, $scheduleId);
// 检查剩余课时是否足够
$remainingHours = $studentCourse['total_hours'] - $studentCourse['use_total_hours'];
if ($remainingHours < $deductHours) {
throw new \Exception('剩余课时不足,当前剩余:' . $remainingHours . ',需要消耗:' . $deductHours);
}
// 1. 更新学员课程的已使用课时数
$updateResult = Db::name('student_courses')
->where('id', $studentCourse['id'])
->inc('use_total_hours', $deductHours)
->update(['updated_at' => date('Y-m-d H:i:s')]);
if (!$updateResult) {
throw new \Exception('更新学员课程课时失败');
}
// 2. 插入课程消减记录
$usageData = [
'student_course_id' => $studentCourse['id'],
'student_id' => $student->id,
'resource_id' => $enrollment['resources_id'] ?? null,
'used_hours' => $deductHours,
'usage_date' => date('Y-m-d'),
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
];
$usageResult = Db::name('student_course_usage')->insert($usageData);
if (!$usageResult) {
throw new \Exception('插入课程消减记录失败');
}
// 记录日志
trace('Paid student course deduction processed', 'info');
trace('Deduction details: ' . json_encode([
'student_id' => $student->id,
'student_course_id' => $studentCourse['id'],
'course_start_date' => $studentCourse['start_date'],
'course_end_date' => $studentCourse['end_date'],
'schedule_date' => $courseDate,
'deducted_hours' => $deductHours,
'remaining_hours' => $remainingHours - $deductHours,
'usage_date' => date('Y-m-d')
]), 'info');
} catch (\Exception $e) {
// 抛出异常以便外层事务回滚
throw new \Exception('处理正式学员课程消减失败:' . $e->getMessage());
}
}
/**
* 根据课程日期查找适用的学员课程包
* 优先选择:
* 1. 在有效期内(开始日期 ≤ 课程日期 ≤ 结束日期)
* 2. 还有剩余课时
* 3. 最早开始的课程包
* @param int $studentId 学员ID
* @param string $courseDate 课程日期
* @return array|null 学员课程记录
*/
private function findApplicableStudentCourse($studentId, $courseDate)
{
try {
// 查找在有效期内且有剩余课时的课程包
$applicableCourses = Db::name('student_courses')
->where('student_id', $studentId)
->where('status', 1) // 有效状态
->where('start_date', '<=', $courseDate) // 开始日期 <= 课程日期
->where('end_date', '>=', $courseDate) // 结束日期 >= 课程日期
->whereRaw('total_hours > use_total_hours') // 有剩余课时
->order('start_date ASC, created_at ASC') // 按开始日期升序,创建时间升序
->select()
->toArray();
if (empty($applicableCourses)) {
return null;
}
// 返回最早开始的有效课程包
return $applicableCourses[0];
} catch (\Exception $e) {
trace('Find applicable student course error: ' . $e->getMessage(), 'error');
return null;
}
}
/**
* 计算课程消耗课时数
* @param array $course 课程信息
* @param int $scheduleId 课程安排ID
* @return float 消耗课时数
*/
private function calculateCourseDeduction($course, $scheduleId)
{
try {
// 获取课程安排信息
$schedule = Db::name('course_schedule')
->where('id', $scheduleId)
->find();
if (!$schedule) {
throw new \Exception('找不到课程安排信息');
}
// 根据课程类型计算消耗课时数
switch ($course['course_type']) {
case 1: // 按课时
// 解析时间段计算实际课时
$timeSlot = $schedule['time_slot'] ?? '';
$deductHours = $this->parseTimeSlotToHours($timeSlot);
break;
case 2: // 按次卡
// 按次卡每次消耗1次
$deductHours = 1;
break;
case 3: // 按周期
// 周期课程每次消耗1次
$deductHours = 1;
break;
case 4: // 按时长
// 按实际时长计算,转换为小时
$duration = $course['duration'] ?? 0; // 分钟
$deductHours = $duration / 60;
break;
default:
// 默认每次消耗1课时
$deductHours = 1;
break;
}
// 确保返回正数
return max(0.01, $deductHours);
} catch (\Exception $e) {
// 计算失败时默认消耗1课时
return 1;
}
}
/**
* 解析时间段为小时数
* @param string $timeSlot 时间段,格式:08:00-08:30
* @return float 小时数
*/
private function parseTimeSlotToHours($timeSlot)
{
try {
if (empty($timeSlot) || strpos($timeSlot, '-') === false) {
return 1; // 默认1小时
}
$times = explode('-', $timeSlot);
if (count($times) !== 2) {
return 1;
}
$startTime = strtotime($times[0]);
$endTime = strtotime($times[1]);
if (!$startTime || !$endTime) {
return 1;
}
$minutes = ($endTime - $startTime) / 60;
$hours = $minutes / 60;
// 如果计算结果小于0.1小时,按0.1小时计算
return max(0.1, $hours);
} catch (\Exception $e) {
return 1; // 解析失败时默认1小时
}
}
/** /**
* 处理单个学员签到(内部方法,不管理事务) * 处理单个学员签到(内部方法,不管理事务)
* @param int $scheduleId 课程安排ID * @param int $scheduleId 课程安排ID
@ -2856,9 +3081,15 @@ class CourseScheduleService extends BaseApiService
return ['success' => false, 'error' => '更新签到状态失败']; return ['success' => false, 'error' => '更新签到状态失败'];
} }
// 处理试听课签到逻辑(仅在签到成功且学员未付费时处理) // 处理签到后的课程消减逻辑
if ($status === 1 && $student->pay_status != 1) { if ($status === 1) {
$this->handleTrialClassCheckin($student); if ($student->pay_status != 1) {
// 处理试听课签到逻辑
$this->handleTrialClassCheckin($student);
} else {
// 处理正式学员的课程消减逻辑
$this->handlePaidStudentCourseDeduction($enrollment, $student);
}
} }
return ['success' => true, 'error' => '']; return ['success' => true, 'error' => ''];

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

@ -793,9 +793,12 @@ class CourseService extends BaseApiService
$phone = $student['student']['contact_phone'] ?: ''; $phone = $student['student']['contact_phone'] ?: '';
$trialClassCount = $student['student']['trial_class_count'] ?: 0; $trialClassCount = $student['student']['trial_class_count'] ?: 0;
// 获取学员最新的付费课程信息 // 获取学员最新的有效课程信息
$studentCourseInfo = Db::name('student_courses') $studentCourseInfo = Db::name('student_courses')
->where('student_id', $student['student_id']) ->where('student_id', $student['student_id'])
->where('status', 1) // 只获取状态为1的有效课程
->where('start_date', '<=', date('Y-m-d')) // 开始时间小于等于当前时间
->where('end_date', '>=', date('Y-m-d')) // 结束时间大于等于当前时间
->order('created_at DESC') ->order('created_at DESC')
->find(); ->find();

239
niucloud/app/service/api/apiService/PersonCourseScheduleService.php

@ -406,6 +406,31 @@ class PersonCourseScheduleService extends BaseApiService
->where('status', 2) // 2表示请假 ->where('status', 2) // 2表示请假
->count(); ->count();
// 获取班级关联信息
$classInfo = null;
try {
$classRel = \app\model\class_resources_rel\ClassResourcesRel::alias('crr')
->join(['school_class' => 'c'], 'crr.class_id = c.id', 'left')
->where([
'crr.resource_id' => $where['resource_id'],
'crr.status' => 1
])
->field([
'crr.class_id',
'c.class_name',
'c.head_coach',
'c.educational_id'
])
->find();
if ($classRel) {
$classInfo = $classRel->toArray();
}
} catch (\Exception $e) {
// 班级查询失败,不影响整体功能
$classInfo = null;
}
// 获取教练配置信息 // 获取教练配置信息
$mainCoach = null; $mainCoach = null;
$education = null; $education = null;
@ -471,12 +496,19 @@ class PersonCourseScheduleService extends BaseApiService
'status' => $status, // 课程状态 'status' => $status, // 课程状态
'db_status' => $dbStatus, // 数据库原始状态 'db_status' => $dbStatus, // 数据库原始状态
'single_session_count' => $course['single_session_count'] ?? 1, // 单次消课数量 'single_session_count' => $course['single_session_count'] ?? 1, // 单次消课数量
'resource_id' => $course['resource_id'] ?? null, // 添加资源ID
'student_course_id' => $course['id'], // 添加学生课程ID
'main_coach_id' => $course['main_coach_id'] ?? null, 'main_coach_id' => $course['main_coach_id'] ?? null,
'main_coach_name' => $mainCoach['name'] ?? '未分配', 'main_coach_name' => $mainCoach['name'] ?? '未分配',
'education_id' => $course['education_id'] ?? null, 'education_id' => $course['education_id'] ?? null,
'education_name' => $education['name'] ?? '未分配', 'education_name' => $education['name'] ?? '未分配',
'assistant_ids' => $course['assistant_ids'] ?? '', 'assistant_ids' => $course['assistant_ids'] ?? '',
'assistant_names' => implode(', ', array_column($assistants, 'name')) ?: '无' 'assistant_names' => implode(', ', array_column($assistants, 'name')) ?: '无',
// 班级相关字段
'class_id' => $classInfo['class_id'] ?? null,
'class_name' => $classInfo['class_name'] ?? null,
'has_class' => !empty($classInfo), // 是否有班级关联
'class_info' => $classInfo // 完整的班级信息
]; ];
} }
@ -553,6 +585,209 @@ class PersonCourseScheduleService extends BaseApiService
return $res; return $res;
} }
//获取学生课程详情(包含课时使用记录和订单信息)
public function getStudentCourseDetail(array $where)
{
$res = [
'code' => 0,
'msg' => '获取失败',
'data' => []
];
try {
$studentCourseId = $where['student_course_id'];
// 1. 获取学员课程基础信息
$studentCourse = StudentCourses::where('id', $studentCourseId)
->with([
'course' => function($query) {
$query->field('id,course_name');
},
'student' => function($query) {
$query->field('id,name');
}
])
->find();
if (!$studentCourse) {
$res['msg'] = '学员课程不存在';
return $res;
}
$studentCourse = $studentCourse->toArray();
// 2. 获取课时使用记录(一对多关联)
$usageRecords = StudentCourseUsage::where('student_course_id', $studentCourseId)
->order('usage_date', 'desc')
->select()
->toArray();
// 3. 获取关联的订单信息(一对一关联,通过course_plan_id)
$orderInfo = \app\model\order_table\OrderTable::where('course_plan_id', $studentCourseId)
->with([
'course' => function($query) {
$query->field('id,course_name');
},
'personnel' => function($query) {
$query->field('id,name');
},
'campus' => function($query) {
$query->field('id,campus_name');
}
])
->find();
// 4. 计算课时统计信息
$totalHours = ($studentCourse['total_hours'] ?? 0) + ($studentCourse['gift_hours'] ?? 0);
$usedHours = ($studentCourse['use_total_hours'] ?? 0) + ($studentCourse['use_gift_hours'] ?? 0);
$remainingHours = $totalHours - $usedHours;
// 5. 获取教练配置信息
$mainCoach = null;
$education = null;
$assistants = [];
if (!empty($studentCourse['main_coach_id'])) {
$mainCoach = Personnel::where('id', $studentCourse['main_coach_id'])->field('id,name')->find();
}
if (!empty($studentCourse['education_id'])) {
$education = Personnel::where('id', $studentCourse['education_id'])->field('id,name')->find();
}
if (!empty($studentCourse['assistant_ids'])) {
$assistantIds = array_filter(explode(',', $studentCourse['assistant_ids']));
if (!empty($assistantIds)) {
$assistants = Personnel::whereIn('id', $assistantIds)->field('id,name')->select()->toArray();
}
}
// 6. 计算课程状态
$status = 'active';
$dbStatus = $studentCourse['status'] ?? 1;
switch ($dbStatus) {
case 1:
$status = 'active';
break;
case 2:
$status = 'expired';
break;
case 3:
$status = 'waiting';
break;
case 4:
$status = 'delayed';
break;
}
if ($status === 'active' && !empty($studentCourse['end_date'])) {
if (strtotime($studentCourse['end_date']) < time()) {
$status = 'expired';
}
}
if ($remainingHours <= 0) {
$status = 'completed';
}
// 7. 组装返回数据
$data = [
// 学员课程基础信息
'student_course_info' => [
'id' => $studentCourse['id'],
'student_id' => $studentCourse['student_id'],
'student_name' => $studentCourse['student']['name'] ?? '未知学员',
'course_id' => $studentCourse['course_id'],
'course_name' => $studentCourse['course']['course_name'] ?? '未知课程',
'total_hours' => $studentCourse['total_hours'] ?? 0,
'gift_hours' => $studentCourse['gift_hours'] ?? 0,
'use_total_hours' => $studentCourse['use_total_hours'] ?? 0,
'use_gift_hours' => $studentCourse['use_gift_hours'] ?? 0,
'start_date' => $studentCourse['start_date'] ?? '',
'end_date' => $studentCourse['end_date'] ?? '',
'status' => $status,
'db_status' => $dbStatus,
'single_session_count' => $studentCourse['single_session_count'] ?? 1,
'resource_id' => $studentCourse['resource_id'] ?? null,
'main_coach_id' => $studentCourse['main_coach_id'] ?? null,
'education_id' => $studentCourse['education_id'] ?? null,
'assistant_ids' => $studentCourse['assistant_ids'] ?? '',
// 课时统计
'total_class_hours' => $totalHours,
'used_class_hours' => $usedHours,
'remaining_class_hours' => $remainingHours,
// 教练信息
'main_coach_name' => $mainCoach['name'] ?? '未分配',
'education_name' => $education['name'] ?? '未分配',
'assistant_names' => implode(', ', array_column($assistants, 'name')) ?: '无',
'coach_details' => [
'main_coach' => $mainCoach,
'education' => $education,
'assistants' => $assistants
]
],
// 课时使用记录列表
'usage_records' => array_map(function($record) {
return [
'id' => $record['id'],
'used_hours' => $record['used_hours'],
'usage_date' => $record['usage_date'],
'created_at' => $record['created_at'],
'updated_at' => $record['updated_at'],
'student_id' => $record['student_id'],
'resource_id' => $record['resource_id']
];
}, $usageRecords),
// 订单信息(如果存在)
'order_info' => $orderInfo ? [
'id' => $orderInfo['id'],
'payment_id' => $orderInfo['payment_id'] ?? '',
'order_type' => $orderInfo['order_type'] ?? '',
'order_status' => $orderInfo['order_status'] ?? '',
'payment_type' => $orderInfo['payment_type'] ?? '',
'order_amount' => $orderInfo['order_amount'] ?? 0,
'course_id' => $orderInfo['course_id'] ?? 0,
'class_id' => $orderInfo['class_id'] ?? null,
'staff_id' => $orderInfo['staff_id'] ?? 0,
'resource_id' => $orderInfo['resource_id'] ?? 0,
'campus_id' => $orderInfo['campus_id'] ?? 0,
'student_id' => $orderInfo['student_id'] ?? null,
'discount_amount' => $orderInfo['discount_amount'] ?? 0,
'remark' => $orderInfo['remark'] ?? '',
'payment_time' => $orderInfo['payment_time'] ?? '',
'created_at' => $orderInfo['created_at'] ?? '',
// 关联信息
'course_name' => $orderInfo['course']['course_name'] ?? '',
'staff_name' => $orderInfo['personnel']['name'] ?? '',
'campus_name' => $orderInfo['campus']['campus_name'] ?? ''
] : null,
// 统计信息
'statistics' => [
'total_usage_records' => count($usageRecords),
'total_used_hours_from_records' => array_sum(array_column($usageRecords, 'used_hours')),
'has_order' => !empty($orderInfo),
'usage_date_range' => $usageRecords ? [
'first_usage' => min(array_column($usageRecords, 'usage_date')),
'last_usage' => max(array_column($usageRecords, 'usage_date'))
] : null
]
];
$res = [
'code' => 1,
'msg' => '获取成功',
'data' => $data
];
} catch (\Exception $e) {
$res['msg'] = '获取异常:' . $e->getMessage();
}
return $res;
}
//更新学生课程人员配置 //更新学生课程人员配置
public function updateStudentCoursePersonnel($studentCourseId, array $data) public function updateStudentCoursePersonnel($studentCourseId, array $data)
{ {
@ -585,7 +820,7 @@ class PersonCourseScheduleService extends BaseApiService
// 执行更新 // 执行更新
$result = StudentCourses::where('id', $studentCourseId)->update($updateData); $result = StudentCourses::where('id', $studentCourseId)->update($updateData);
if ($result !== false) { if ($result !== false) {
$res = [ $res = [
'code' => 1, 'code' => 1,

5
niucloud/app/service/api/student/ContractService.php

@ -1096,7 +1096,7 @@ class ContractService extends BaseService
/** /**
* 格式化字段值 * 格式化字段值
* 对特定类型的字段值进行格式化处理 * 对特定类型的字段值进行格式化处理
* *
* @param string $fieldName 字段名 * @param string $fieldName 字段名
* @param mixed $value 原始值 * @param mixed $value 原始值
* @return string 格式化后的值 * @return string 格式化后的值
@ -1133,4 +1133,5 @@ class ContractService extends BaseService
return (string)$value; return (string)$value;
} }
}
}

5
uniapp/api/apiRoute.js

@ -1083,6 +1083,11 @@ export default {
return response return response
}, },
// 获取学员课程详情
async getStudentCourseDetail(data = {}) {
return await http.post('/getStudentCourseDetail', data)
},

290
uniapp/components/course-info-card/index.vue

@ -3,11 +3,11 @@
<view class="course-info-card"> <view class="course-info-card">
<!-- 课程信息列表 --> <!-- 课程信息列表 -->
<view class="course-list" v-if="courseList && courseList.length > 0"> <view class="course-list" v-if="courseList && courseList.length > 0">
<view <view
class="course-item" class="course-item"
v-for="(course, index) in courseList" v-for="(course, index) in courseList"
:key="course.id || index" :key="course.id || index"
@tap="viewCourseDetail(course)" @tap="viewCourseDetail(courseList[index])"
> >
<view class="course-header"> <view class="course-header">
<view class="course-title">{{ course.course_name || '未知课程' }}</view> <view class="course-title">{{ course.course_name || '未知课程' }}</view>
@ -20,12 +20,12 @@
</view> </view>
</view> </view>
</view> </view>
<!-- 课程进度 --> <!-- 课程进度 -->
<view class="course-progress" v-if="course.total_count"> <view class="course-progress" v-if="course.total_count">
<view class="progress-bar"> <view class="progress-bar">
<view <view
class="progress-fill" class="progress-fill"
:style="{ width: getProgressPercent(course) + '%' }" :style="{ width: getProgressPercent(course) + '%' }"
></view> ></view>
</view> </view>
@ -33,7 +33,7 @@
{{ course.used_count || 0 }}/{{ course.total_count }} {{ course.used_count || 0 }}/{{ course.total_count }}
</view> </view>
</view> </view>
<view class="course-details"> <view class="course-details">
<!-- 基本信息 --> <!-- 基本信息 -->
<view class="detail-section"> <view class="detail-section">
@ -50,7 +50,7 @@
<text class="detail-value highlight">{{ getRemainingCount(course) }}</text> <text class="detail-value highlight">{{ getRemainingCount(course) }}</text>
</view> </view>
</view> </view>
<!-- 时间信息 --> <!-- 时间信息 -->
<view class="detail-section" v-if="course.start_date || course.end_date || course.expiry_date"> <view class="detail-section" v-if="course.start_date || course.end_date || course.expiry_date">
<view class="detail-item" v-if="course.start_date"> <view class="detail-item" v-if="course.start_date">
@ -62,7 +62,7 @@
<text class="detail-value">{{ formatDate(course.end_date || course.expiry_date) }}</text> <text class="detail-value">{{ formatDate(course.end_date || course.expiry_date) }}</text>
</view> </view>
</view> </view>
<!-- 其他信息 --> <!-- 其他信息 -->
<view class="detail-section"> <view class="detail-section">
<view class="detail-item" v-if="course.course_price"> <view class="detail-item" v-if="course.course_price">
@ -74,7 +74,7 @@
<text class="detail-value">{{ formatTime(course.create_time) }}</text> <text class="detail-value">{{ formatTime(course.create_time) }}</text>
</view> </view>
</view> </view>
<!-- 备注信息 --> <!-- 备注信息 -->
<view class="detail-section" v-if="course.remark"> <view class="detail-section" v-if="course.remark">
<view class="remark-item"> <view class="remark-item">
@ -85,14 +85,14 @@
</view> </view>
</view> </view>
</view> </view>
<!-- 空状态 --> <!-- 空状态 -->
<view class="empty-state" v-else> <view class="empty-state" v-else>
<view class="empty-icon">📖</view> <view class="empty-icon">📖</view>
<view class="empty-text">暂无课程信息</view> <view class="empty-text">暂无课程信息</view>
<view class="empty-tip">学生还未报名任何课程</view> <view class="empty-tip">学生还未报名任何课程</view>
</view> </view>
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<view v-if="showEditModal" class="modal-overlay" @click="closeEditModal"> <view v-if="showEditModal" class="modal-overlay" @click="closeEditModal">
<view class="modal-content" @click.stop> <view class="modal-content" @click.stop>
@ -100,17 +100,17 @@
<view class="modal-title">编辑课程信息</view> <view class="modal-title">编辑课程信息</view>
<view class="close-btn" @click="closeEditModal">×</view> <view class="close-btn" @click="closeEditModal">×</view>
</view> </view>
<view class="modal-body"> <view class="modal-body">
<view class="form-section"> <view class="form-section">
<view class="section-title">人员配置</view> <view class="section-title">人员配置</view>
<!-- 主教练选择 --> <!-- 主教练选择 -->
<view class="form-item"> <view class="form-item">
<text class="form-label">主教练</text> <text class="form-label">主教练</text>
<picker <picker
:value="selectedMainCoachIndex" :value="selectedMainCoachIndex"
:range="coachList" :range="coachList"
range-key="name" range-key="name"
@change="onMainCoachChange" @change="onMainCoachChange"
style="width: 100%" style="width: 100%"
@ -121,15 +121,15 @@
</view> </view>
</picker> </picker>
</view> </view>
<!-- 助教选择 --> <!-- 助教选择 -->
<view class="form-item"> <view class="form-item">
<text class="form-label">助教</text> <text class="form-label">助教</text>
<picker <picker
style="width: 100%" style="width: 100%"
mode="multiSelector" mode="multiSelector"
:value="selectedAssistantIndexes" :value="selectedAssistantIndexes"
:range="[coachList]" :range="[coachList]"
:range-key="['name']" :range-key="['name']"
@change="onAssistantChange" @change="onAssistantChange"
> >
@ -139,14 +139,14 @@
</view> </view>
</picker> </picker>
</view> </view>
<!-- 教务选择 --> <!-- 教务选择 -->
<view class="form-item"> <view class="form-item">
<text class="form-label">教务</text> <text class="form-label">教务</text>
<picker <picker
style="width: 100%" style="width: 100%"
:value="selectedEducationIndex" :value="selectedEducationIndex"
:range="educationList" :range="educationList"
range-key="name" range-key="name"
@change="onEducationChange" @change="onEducationChange"
> >
@ -157,7 +157,7 @@
</picker> </picker>
</view> </view>
</view> </view>
<!-- 班级选择区域 - 始终允许编辑 --> <!-- 班级选择区域 - 始终允许编辑 -->
<view class="form-section"> <view class="form-section">
<view class="section-title">班级配置</view> <view class="section-title">班级配置</view>
@ -165,8 +165,8 @@
<text class="form-label">所属班级</text> <text class="form-label">所属班级</text>
<picker <picker
style="width: 100%" style="width: 100%"
:value="selectedClassIndex" :value="selectedClassIndex"
:range="classList" :range="classList"
range-key="class_name" range-key="class_name"
@change="onClassChange" @change="onClassChange"
> >
@ -182,7 +182,7 @@
</view> </view>
</view> </view>
</view> </view>
<view class="modal-footer"> <view class="modal-footer">
<button class="btn btn-cancel" @tap="closeEditModal">取消</button> <button class="btn btn-cancel" @tap="closeEditModal">取消</button>
<button class="btn btn-confirm" @tap="confirmEdit" :loading="saving">保存</button> <button class="btn btn-confirm" @tap="confirmEdit" :loading="saving">保存</button>
@ -204,14 +204,14 @@ export default {
default: () => [] default: () => []
} }
}, },
data() { data() {
return { return {
// //
showEditModal: false, showEditModal: false,
saving: false, saving: false,
currentCourse: null, currentCourse: null,
// //
editForm: { editForm: {
student_course_id: '', student_course_id: '',
@ -224,34 +224,39 @@ export default {
class_id: '', class_id: '',
class_name: '' class_name: ''
}, },
// //
selectedMainCoachIndex: 0, selectedMainCoachIndex: 0,
selectedAssistantIndexes: [0], selectedAssistantIndexes: [0],
selectedEducationIndex: 0, selectedEducationIndex: 0,
selectedClassIndex: 0, selectedClassIndex: 0,
// //
coachList: [], coachList: [],
educationList: [], educationList: [],
classList: [], classList: [],
// //
hasClass: false, hasClass: false,
currentClassInfo: {} currentClassInfo: {}
} }
}, },
mounted() { mounted() {
// 使 // 使
if (!this.courseList || this.courseList.length === 0) { if (!this.courseList || this.courseList.length === 0) {
console.log('使用测试课程数据') console.log('使用测试课程数据')
} }
}, },
methods: { methods: {
// //
viewCourseDetail(course) { viewCourseDetail(course) {
console.log('viewCourseDetail 被调用,course:', course)
console.log('course 类型:', typeof course)
console.log('course.id:', course?.id)
console.log('course.student_course_id:', course?.student_course_id)
if (!course) { if (!course) {
console.error('viewCourseDetail: course参数为空') console.error('viewCourseDetail: course参数为空')
uni.showToast({ uni.showToast({
@ -260,15 +265,26 @@ export default {
}) })
return return
} }
//
if (!course.id && !course.student_course_id) {
console.error('viewCourseDetail: 课程缺少ID信息')
uni.showToast({
title: '课程信息不完整',
icon: 'none'
})
return
}
this.$emit('view-detail', course) this.$emit('view-detail', course)
}, },
// //
handleEditClick(e) { handleEditClick(e) {
console.log('编辑按钮点击事件:', e) console.log('编辑按钮点击事件:', e)
const courseIndex = parseInt(e.currentTarget.dataset.courseIndex) const courseIndex = parseInt(e.currentTarget.dataset.courseIndex)
console.log('课程索引:', courseIndex, '类型:', typeof courseIndex) console.log('课程索引:', courseIndex, '类型:', typeof courseIndex)
if (!isNaN(courseIndex) && this.courseList && this.courseList[courseIndex]) { if (!isNaN(courseIndex) && this.courseList && this.courseList[courseIndex]) {
const course = this.courseList[courseIndex] const course = this.courseList[courseIndex]
console.log('通过索引获取到的课程数据:', course) console.log('通过索引获取到的课程数据:', course)
@ -289,7 +305,7 @@ export default {
// //
async editCourse(course) { async editCourse(course) {
console.log('编辑课程数据:', course) console.log('编辑课程数据:', course)
// course // course
if (!course) { if (!course) {
console.error('editCourse: course参数为空') console.error('editCourse: course参数为空')
@ -299,7 +315,7 @@ export default {
}) })
return return
} }
// //
if (!course.id && !course.student_course_id) { if (!course.id && !course.student_course_id) {
console.error('editCourse: 缺少课程ID', { id: course.id, student_course_id: course.student_course_id }) console.error('editCourse: 缺少课程ID', { id: course.id, student_course_id: course.student_course_id })
@ -309,7 +325,7 @@ export default {
}) })
return return
} }
if (!course.resource_id) { if (!course.resource_id) {
console.error('editCourse: 缺少resource_id', { resource_id: course.resource_id }) console.error('editCourse: 缺少resource_id', { resource_id: course.resource_id })
uni.showToast({ uni.showToast({
@ -318,19 +334,19 @@ export default {
}) })
return return
} }
this.currentCourse = course this.currentCourse = course
// - // -
console.log('原始课程数据:', course) console.log('原始课程数据:', course)
// //
const mainCoachId = course.main_coach_id || course.head_coach || course.coach_id || course.teacher_id || '' const mainCoachId = course.main_coach_id || course.head_coach || course.coach_id || course.teacher_id || ''
const educationId = course.education_id || course.educational_id || course.education || '' const educationId = course.education_id || course.educational_id || course.education || ''
// //
const classId = course.class_id || course.current_class_id || course.belong_class_id || '' const classId = course.class_id || course.current_class_id || course.belong_class_id || ''
const className = course.class_name || course.current_class_name || course.belong_class_name || '' const className = course.class_name || course.current_class_name || course.belong_class_name || ''
this.editForm = { this.editForm = {
student_course_id: course.student_course_id || course.id, student_course_id: course.student_course_id || course.id,
main_coach_id: mainCoachId, main_coach_id: mainCoachId,
@ -342,26 +358,26 @@ export default {
class_id: classId, class_id: classId,
class_name: className class_name: className
} }
console.log('处理后的editForm:', this.editForm) console.log('处理后的editForm:', this.editForm)
try { try {
// //
uni.showLoading({ uni.showLoading({
title: '加载中...' title: '加载中...'
}) })
// //
await this.loadBaseData() await this.loadBaseData()
// //
await this.checkClassRelation(course.resource_id) await this.checkClassRelation(course.resource_id)
// //
this.setPickerIndexes() this.setPickerIndexes()
uni.hideLoading() uni.hideLoading()
// //
this.showEditModal = true this.showEditModal = true
} catch (error) { } catch (error) {
@ -373,18 +389,18 @@ export default {
}) })
} }
}, },
// //
formatAssistantNames(assistantIds) { formatAssistantNames(assistantIds) {
if (!assistantIds) return '' if (!assistantIds) return ''
// //
return '' return ''
}, },
// //
async loadBaseData() { async loadBaseData() {
console.log('开始加载基础数据') console.log('开始加载基础数据')
try { try {
// 使API // 使API
const [coachRes, educationRes, classRes] = await Promise.all([ const [coachRes, educationRes, classRes] = await Promise.all([
@ -395,11 +411,11 @@ export default {
// //
apiRoute.jlGetClassesList() apiRoute.jlGetClassesList()
]) ])
console.log('教练列表响应:', coachRes) console.log('教练列表响应:', coachRes)
console.log('教务列表响应:', educationRes) console.log('教务列表响应:', educationRes)
console.log('班级列表响应:', classRes) console.log('班级列表响应:', classRes)
// - // -
if (coachRes && coachRes.code === 1 && coachRes.data) { if (coachRes && coachRes.code === 1 && coachRes.data) {
// common_getCoachListcoach_list // common_getCoachListcoach_list
@ -414,7 +430,7 @@ export default {
{ id: 4, name: '张教练', phone: '13800138004' } { id: 4, name: '张教练', phone: '13800138004' }
] ]
} }
// //
if (educationRes && educationRes.code === 1) { if (educationRes && educationRes.code === 1) {
this.educationList = educationRes.data || [] this.educationList = educationRes.data || []
@ -426,7 +442,7 @@ export default {
{ id: 2, name: '刘教务', phone: '13800138004' } { id: 2, name: '刘教务', phone: '13800138004' }
] ]
} }
// - // -
if (classRes && classRes.code === 1) { if (classRes && classRes.code === 1) {
// jlGetClassesListdata // jlGetClassesListdata
@ -445,13 +461,13 @@ export default {
{ id: 2, class_name: '测试班级2', head_coach: 6, educational_id: 0 } { id: 2, class_name: '测试班级2', head_coach: 6, educational_id: 0 }
] ]
} }
console.log('最终数据:', { console.log('最终数据:', {
coachList: this.coachList, coachList: this.coachList,
educationList: this.educationList, educationList: this.educationList,
classList: this.classList classList: this.classList
}) })
} catch (error) { } catch (error) {
console.error('加载基础数据失败:', error) console.error('加载基础数据失败:', error)
// 使 // 使
@ -470,7 +486,7 @@ export default {
{ id: 1, class_name: '测试班级1', head_coach: 5, educational_id: 0 }, { id: 1, class_name: '测试班级1', head_coach: 5, educational_id: 0 },
{ id: 2, class_name: '测试班级2', head_coach: 6, educational_id: 0 } { id: 2, class_name: '测试班级2', head_coach: 6, educational_id: 0 }
] ]
uni.showToast({ uni.showToast({
title: '使用模拟数据进行测试', title: '使用模拟数据进行测试',
icon: 'none', icon: 'none',
@ -478,19 +494,19 @@ export default {
}) })
} }
}, },
// //
async checkClassRelation(resourceId) { async checkClassRelation(resourceId) {
try { try {
console.log('检查班级关联,资源ID:', resourceId) console.log('检查班级关联,资源ID:', resourceId)
// 使API // 使API
const res = await apiRoute.checkClassRelation({ resource_id: resourceId }) const res = await apiRoute.checkClassRelation({ resource_id: resourceId })
if (res && res.code === 1) { if (res && res.code === 1) {
this.hasClass = res.data.has_class this.hasClass = res.data.has_class
this.currentClassInfo = res.data.class_info || {} this.currentClassInfo = res.data.class_info || {}
// editForm // editForm
if (this.hasClass && this.currentClassInfo) { if (this.hasClass && this.currentClassInfo) {
this.editForm.class_id = this.currentClassInfo.id || this.currentClassInfo.class_id || '' this.editForm.class_id = this.currentClassInfo.id || this.currentClassInfo.class_id || ''
@ -506,7 +522,7 @@ export default {
this.hasClass = false this.hasClass = false
this.currentClassInfo = {} this.currentClassInfo = {}
} }
} catch (error) { } catch (error) {
console.error('检查班级关联失败:', error) console.error('检查班级关联失败:', error)
// //
@ -514,7 +530,7 @@ export default {
this.currentClassInfo = {} this.currentClassInfo = {}
} }
}, },
// //
setPickerIndexes() { setPickerIndexes() {
console.log('设置选择器索引,当前数据:', { console.log('设置选择器索引,当前数据:', {
@ -524,13 +540,13 @@ export default {
coachListLength: this.coachList.length, coachListLength: this.coachList.length,
educationListLength: this.educationList.length educationListLength: this.educationList.length
}) })
// //
if (!this.editForm) { if (!this.editForm) {
console.warn('editForm未初始化,跳过索引设置') console.warn('editForm未初始化,跳过索引设置')
return return
} }
// //
if (this.editForm.main_coach_id) { if (this.editForm.main_coach_id) {
console.log('主教练ID类型:', typeof this.editForm.main_coach_id, '值:', this.editForm.main_coach_id) console.log('主教练ID类型:', typeof this.editForm.main_coach_id, '值:', this.editForm.main_coach_id)
@ -541,7 +557,7 @@ export default {
}) })
} }
} }
// - // -
if (this.editForm.main_coach_id && this.coachList.length > 0) { if (this.editForm.main_coach_id && this.coachList.length > 0) {
// //
@ -555,7 +571,7 @@ export default {
console.log('未找到匹配的主教练ID:', this.editForm.main_coach_id, '教练列表:', this.coachList.map(c => c.id)) console.log('未找到匹配的主教练ID:', this.editForm.main_coach_id, '教练列表:', this.coachList.map(c => c.id))
} }
} }
// - // -
if (this.editForm.assistant_ids && this.coachList.length > 0) { if (this.editForm.assistant_ids && this.coachList.length > 0) {
const assistantIds = this.editForm.assistant_ids.split(',').map(id => id.trim()).filter(id => id) const assistantIds = this.editForm.assistant_ids.split(',').map(id => id.trim()).filter(id => id)
@ -583,7 +599,7 @@ export default {
this.selectedAssistantIndexes = [0] this.selectedAssistantIndexes = [0]
this.editForm.assistant_names = '' this.editForm.assistant_names = ''
} }
// - // -
if (this.editForm.education_id && this.educationList.length > 0) { if (this.editForm.education_id && this.educationList.length > 0) {
// //
@ -597,22 +613,42 @@ export default {
console.log('未找到匹配的教务ID:', this.editForm.education_id, '教务列表:', this.educationList.map(e => e.id)) console.log('未找到匹配的教务ID:', this.editForm.education_id, '教务列表:', this.educationList.map(e => e.id))
} }
} }
// - // -
if (this.classList.length > 0) { if (this.classList.length > 0) {
// class_id0 // class_id0
const targetId = String(this.editForm.class_id || 0) const targetId = String(this.editForm.class_id || 0)
const classIndex = this.classList.findIndex(item => String(item.id) === targetId) const classIndex = this.classList.findIndex(item => String(item.id) === targetId)
console.log('班级索引设置调试信息:', {
targetId,
targetIdType: typeof targetId,
editFormClassId: this.editForm.class_id,
editFormClassIdType: typeof this.editForm.class_id,
classList: this.classList.map(c => ({ id: c.id, idType: typeof c.id, name: c.class_name })),
classIndex,
classIndexType: typeof classIndex
})
if (classIndex >= 0) { if (classIndex >= 0) {
this.selectedClassIndex = classIndex this.selectedClassIndex = Number(classIndex) //
this.editForm.class_name = this.classList[classIndex].class_name // 使使API
console.log('班级设置成功:', this.classList[classIndex].class_name, '索引:', classIndex) const listClassName = this.classList[classIndex].class_name
this.editForm.class_name = listClassName || this.editForm.class_name || '请选择班级'
console.log('班级设置成功:', {
className: this.editForm.class_name,
listClassName,
classIndex: this.selectedClassIndex,
classIndexType: typeof this.selectedClassIndex
})
} else { } else {
// "" // API""
this.selectedClassIndex = 0 this.selectedClassIndex = 0
this.editForm.class_id = 0 // class_idclass_name0
this.editForm.class_name = '无班级' console.log('未找到匹配的班级ID,保留原有值:', {
console.log('未找到匹配的班级ID,默认设置为无班级:', this.editForm.class_id, '班级列表:', this.classList.map(c => c.id)) classId: this.editForm.class_id,
className: this.editForm.class_name,
classList: this.classList.map(c => c.id)
})
} }
} else { } else {
console.log('班级列表为空:', { console.log('班级列表为空:', {
@ -620,7 +656,7 @@ export default {
classList_length: this.classList.length classList_length: this.classList.length
}) })
} }
console.log('选择器索引设置完成:', { console.log('选择器索引设置完成:', {
selectedMainCoachIndex: this.selectedMainCoachIndex, selectedMainCoachIndex: this.selectedMainCoachIndex,
selectedAssistantIndexes: this.selectedAssistantIndexes, selectedAssistantIndexes: this.selectedAssistantIndexes,
@ -628,7 +664,7 @@ export default {
selectedClassIndex: this.selectedClassIndex selectedClassIndex: this.selectedClassIndex
}) })
}, },
// //
onMainCoachChange(e) { onMainCoachChange(e) {
const index = e.detail.value const index = e.detail.value
@ -639,17 +675,17 @@ export default {
this.editForm.main_coach_name = selectedCoach.name this.editForm.main_coach_name = selectedCoach.name
} }
}, },
// //
onAssistantChange(e) { onAssistantChange(e) {
const indexes = e.detail.value const indexes = e.detail.value
this.selectedAssistantIndexes = indexes this.selectedAssistantIndexes = indexes
const selectedAssistants = indexes.map(index => this.coachList[index]).filter(Boolean) const selectedAssistants = indexes.map(index => this.coachList[index]).filter(Boolean)
this.editForm.assistant_ids = selectedAssistants.map(item => item.id).join(',') this.editForm.assistant_ids = selectedAssistants.map(item => item.id).join(',')
this.editForm.assistant_names = selectedAssistants.map(item => item.name).join(',') this.editForm.assistant_names = selectedAssistants.map(item => item.name).join(',')
}, },
// //
onEducationChange(e) { onEducationChange(e) {
const index = e.detail.value const index = e.detail.value
@ -660,7 +696,7 @@ export default {
this.editForm.education_name = selectedEducation.name this.editForm.education_name = selectedEducation.name
} }
}, },
// //
onClassChange(e) { onClassChange(e) {
const index = e.detail.value const index = e.detail.value
@ -669,7 +705,7 @@ export default {
if (selectedClass) { if (selectedClass) {
this.editForm.class_id = selectedClass.id this.editForm.class_id = selectedClass.id
this.editForm.class_name = selectedClass.class_name this.editForm.class_name = selectedClass.class_name
// "" // ""
if (selectedClass.id > 0 && selectedClass.head_coach) { if (selectedClass.id > 0 && selectedClass.head_coach) {
// //
@ -680,7 +716,7 @@ export default {
this.editForm.main_coach_name = this.coachList[coachIndex].name this.editForm.main_coach_name = this.coachList[coachIndex].name
} }
} }
if (selectedClass.id > 0 && selectedClass.educational_id) { if (selectedClass.id > 0 && selectedClass.educational_id) {
// //
const educationIndex = this.educationList.findIndex(item => item.id == selectedClass.educational_id) const educationIndex = this.educationList.findIndex(item => item.id == selectedClass.educational_id)
@ -692,14 +728,14 @@ export default {
} }
} }
}, },
// //
closeEditModal() { closeEditModal() {
this.showEditModal = false this.showEditModal = false
this.currentCourse = null this.currentCourse = null
this.resetForm() this.resetForm()
}, },
// //
resetForm() { resetForm() {
console.log('重置表单数据') console.log('重置表单数据')
@ -720,17 +756,17 @@ export default {
this.selectedClassIndex = 0 this.selectedClassIndex = 0
this.hasClass = false this.hasClass = false
this.currentClassInfo = {} this.currentClassInfo = {}
// //
this.coachList = [] this.coachList = []
this.educationList = [] this.educationList = []
this.classList = [] this.classList = []
}, },
// //
async confirmEdit() { async confirmEdit() {
if (this.saving) return if (this.saving) return
// //
if (!this.editForm.student_course_id) { if (!this.editForm.student_course_id) {
uni.showToast({ uni.showToast({
@ -739,23 +775,23 @@ export default {
}) })
return return
} }
try { try {
this.saving = true this.saving = true
console.log('提交编辑数据:', this.editForm) console.log('提交编辑数据:', this.editForm)
// 使API // 使API
const res = await apiRoute.updateStudentCoursePersonnel(this.editForm) const res = await apiRoute.updateStudentCoursePersonnel(this.editForm)
console.log('更新响应:', res) console.log('更新响应:', res)
if (res && res.code === 1) { if (res && res.code === 1) {
uni.showToast({ uni.showToast({
title: '更新成功', title: '更新成功',
icon: 'success' icon: 'success'
}) })
// //
this.$emit('course-updated', this.currentCourse) this.$emit('course-updated', this.currentCourse)
this.closeEditModal() this.closeEditModal()
@ -766,7 +802,7 @@ export default {
title: '更新成功(模拟)', title: '更新成功(模拟)',
icon: 'success' icon: 'success'
}) })
// //
this.$emit('course-updated', this.currentCourse) this.$emit('course-updated', this.currentCourse)
this.closeEditModal() this.closeEditModal()
@ -778,7 +814,7 @@ export default {
title: '更新成功(模拟)', title: '更新成功(模拟)',
icon: 'success' icon: 'success'
}) })
// //
this.$emit('course-updated', this.currentCourse) this.$emit('course-updated', this.currentCourse)
this.closeEditModal() this.closeEditModal()
@ -787,7 +823,7 @@ export default {
} }
}, },
// //
getStatusClass(status) { getStatusClass(status) {
const statusMap = { const statusMap = {
@ -798,7 +834,7 @@ export default {
} }
return statusMap[status] || 'status-default' return statusMap[status] || 'status-default'
}, },
// //
getStatusText(status) { getStatusText(status) {
const statusMap = { const statusMap = {
@ -809,7 +845,7 @@ export default {
} }
return statusMap[status] || '未知状态' return statusMap[status] || '未知状态'
}, },
// //
getProgressPercent(course) { getProgressPercent(course) {
if (!course) return 0 if (!course) return 0
@ -817,7 +853,7 @@ export default {
const used = course.used_count || 0 const used = course.used_count || 0
return Math.round((used / course.total_count) * 100) return Math.round((used / course.total_count) * 100)
}, },
// //
getRemainingCount(course) { getRemainingCount(course) {
if (!course) return 0 if (!course) return 0
@ -825,7 +861,7 @@ export default {
const used = course.used_count || 0 const used = course.used_count || 0
return Math.max(0, total - used) return Math.max(0, total - used)
}, },
// //
formatTime(timeStr) { formatTime(timeStr) {
if (!timeStr) return '' if (!timeStr) return ''
@ -836,7 +872,7 @@ export default {
return timeStr return timeStr
} }
}, },
// //
formatDate(dateStr) { formatDate(dateStr) {
if (!dateStr) return '' if (!dateStr) return ''
@ -872,7 +908,7 @@ export default {
padding: 32rpx; padding: 32rpx;
border: 1px solid #404040; border: 1px solid #404040;
transition: all 0.3s ease; transition: all 0.3s ease;
&:active { &:active {
background: #4A4A4A; background: #4A4A4A;
} }
@ -896,7 +932,7 @@ export default {
background: rgba(41, 211, 180, 0.1); background: rgba(41, 211, 180, 0.1);
border-radius: 8rpx; border-radius: 8rpx;
border: 1px solid #29D3B4; border: 1px solid #29D3B4;
&:active { &:active {
background: rgba(41, 211, 180, 0.2); background: rgba(41, 211, 180, 0.2);
} }
@ -920,27 +956,27 @@ export default {
border-radius: 20rpx; border-radius: 20rpx;
font-size: 24rpx; font-size: 24rpx;
font-weight: 500; font-weight: 500;
&.status-active { &.status-active {
background: rgba(41, 211, 180, 0.2); background: rgba(41, 211, 180, 0.2);
color: #29D3B4; color: #29D3B4;
} }
&.status-completed { &.status-completed {
background: rgba(76, 175, 80, 0.2); background: rgba(76, 175, 80, 0.2);
color: #4CAF50; color: #4CAF50;
} }
&.status-expired { &.status-expired {
background: rgba(244, 67, 54, 0.2); background: rgba(244, 67, 54, 0.2);
color: #F44336; color: #F44336;
} }
&.status-pending { &.status-pending {
background: rgba(255, 193, 7, 0.2); background: rgba(255, 193, 7, 0.2);
color: #FFC107; color: #FFC107;
} }
&.status-default { &.status-default {
background: rgba(158, 158, 158, 0.2); background: rgba(158, 158, 158, 0.2);
color: #9E9E9E; color: #9E9E9E;
@ -1012,17 +1048,17 @@ export default {
color: #ffffff; color: #ffffff;
flex: 1; flex: 1;
text-align: right; text-align: right;
&.highlight { &.highlight {
color: #29D3B4; color: #29D3B4;
font-weight: 600; font-weight: 600;
} }
&.price { &.price {
color: #FFC107; color: #FFC107;
font-weight: 600; font-weight: 600;
} }
&.remark { &.remark {
text-align: left; text-align: left;
line-height: 1.5; line-height: 1.5;
@ -1131,7 +1167,7 @@ export default {
font-size: 32rpx; font-size: 32rpx;
color: #999999; color: #999999;
border-radius: 50%; border-radius: 50%;
&:active { &:active {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
} }
@ -1146,7 +1182,7 @@ export default {
.form-section { .form-section {
margin-bottom: 32rpx; margin-bottom: 32rpx;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@ -1189,7 +1225,7 @@ export default {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
&:active { &:active {
border-color: #29D3B4; border-color: #29D3B4;
background: #4A4A4A; background: #4A4A4A;
@ -1253,7 +1289,7 @@ export default {
font-weight: 500; font-weight: 500;
border: none; border: none;
outline: none; outline: none;
&.btn-cancel { &.btn-cancel {
background: #404040; background: #404040;
color: #ffffff; color: #ffffff;
@ -1261,25 +1297,25 @@ export default {
background: #4A4A4A; background: #4A4A4A;
} }
} }
&.btn-confirm { &.btn-confirm {
background: #29D3B4; background: #29D3B4;
color: #ffffff; color: #ffffff;
&:active { &:active {
background: #24B89E; background: #24B89E;
} }
&:disabled { &:disabled {
background: #666666; background: #666666;
color: #999999; color: #999999;
} }
} }
&.btn-test { &.btn-test {
background: #FF6B35; background: #FF6B35;
color: #ffffff; color: #ffffff;
&:active { &:active {
background: #E55A2B; background: #E55A2B;
} }
@ -1303,4 +1339,4 @@ export default {
.modal-body::-webkit-scrollbar-thumb:hover { .modal-body::-webkit-scrollbar-thumb:hover {
background: #24B89E; background: #24B89E;
} }
</style> </style>

24
uniapp/pages-common/contract/staff-contract-sign.vue

@ -35,8 +35,8 @@
<view class="form_section" v-if="!loading"> <view class="form_section" v-if="!loading">
<view class="form_title">请填写以下信息</view> <view class="form_title">请填写以下信息</view>
<view class="form_content"> <view class="form_content">
<view <view
v-for="(field, index) in formFields" v-for="(field, index) in partyBFormFields"
:key="index" :key="index"
class="form_field" class="form_field"
> >
@ -180,7 +180,7 @@ export default {
// //
renderContractContent() { renderContractContent() {
if (!this.contractContent) return '' if (!this.contractContent) return ''
let content = this.contractContent let content = this.contractContent
// //
Object.keys(this.formData).forEach(key => { Object.keys(this.formData).forEach(key => {
@ -188,8 +188,13 @@ export default {
const regex = new RegExp(`\\{\\{${key}\\}\\}`, 'g') const regex = new RegExp(`\\{\\{${key}\\}\\}`, 'g')
content = content.replace(regex, value) content = content.replace(regex, value)
}) })
return content return content
},
// party_b
partyBFormFields() {
return this.formFields.filter(field => field.sign_party === 'party_b')
} }
}, },
@ -276,7 +281,8 @@ export default {
initFormData() { initFormData() {
const data = {} const data = {}
this.formFields.forEach(field => { // party_b
this.partyBFormFields.forEach(field => {
const key = field.placeholder || field.name const key = field.placeholder || field.name
// //
if (field.data_type === 'database' || field.data_type === 'system') { if (field.data_type === 'database' || field.data_type === 'system') {
@ -400,8 +406,8 @@ export default {
}, },
validateForm() { validateForm() {
// // party_b
for (const field of this.formFields) { for (const field of this.partyBFormFields) {
if (field.is_required) { if (field.is_required) {
const key = field.placeholder || field.name const key = field.placeholder || field.name
const value = this.formData[key] const value = this.formData[key]
@ -466,8 +472,8 @@ export default {
}, },
getSignatureImage() { getSignatureImage() {
// signature // signatureparty_b
for (const field of this.formFields) { for (const field of this.partyBFormFields) {
if (field.data_type === 'signature') { if (field.data_type === 'signature') {
const key = field.placeholder || field.name const key = field.placeholder || field.name
return this.formData[key] || '' return this.formData[key] || ''

1614
uniapp/pages-market/course/course_detail.vue

File diff suppressed because it is too large

10
uniapp/pages.json

@ -395,6 +395,14 @@
"navigationBarBackgroundColor": "#292929", "navigationBarBackgroundColor": "#292929",
"navigationBarTextStyle": "white" "navigationBarTextStyle": "white"
} }
},
{
"path": "course/course_detail",
"style": {
"navigationBarTitleText": "课程详情",
"navigationBarBackgroundColor": "#292929",
"navigationBarTextStyle": "white"
}
} }
] ]
}, },
@ -469,7 +477,7 @@
"style": { "style": {
"navigationBarTitleText": "我的工资", "navigationBarTitleText": "我的工资",
"navigationStyle": "default", "navigationStyle": "default",
"navigationBarBackgroundColor": "#29d3b4", "navigationBarBackgroundColor": "#171717",
"navigationBarTextStyle": "white" "navigationBarTextStyle": "white"
} }
}, },

Loading…
Cancel
Save