Browse Source

Merge remote-tracking branch 'origin/master'

master
liutong 10 months ago
parent
commit
3cc97f3a90
  1. 81
      admin/components.d.ts
  2. 26
      admin/src/app/api/communication_records.ts
  3. 29
      admin/src/app/api/student_courses.ts
  4. 11
      admin/src/app/api/sys.ts
  5. 48
      admin/src/app/lang/zh-cn/communication_records.communication_records.json
  6. 4
      admin/src/app/lang/zh-cn/course.course.json
  7. 36
      admin/src/app/lang/zh-cn/student_courses.student_courses.json
  8. 69
      admin/src/app/views/campus_person_role/campus_person_role.vue
  9. 464
      admin/src/app/views/communication_records/communication_records.vue
  10. 440
      admin/src/app/views/communication_records/components/communication-records-edit.vue
  11. 22
      admin/src/app/views/course/components/course-edit.vue
  12. 13
      admin/src/app/views/course/course.vue
  13. 329
      admin/src/app/views/setting/pay.vue
  14. 329
      admin/src/app/views/student_courses/components/student-courses-edit.vue
  15. 350
      admin/src/app/views/student_courses/student_courses.vue
  16. 269
      admin/src/app/views/timetables/timetables.vue
  17. 212
      admin/src/app/views/xsyj/xsyj.vue
  18. 35
      niucloud/app/adminapi/controller/communication_records/CommunicationRecords.php
  19. 1
      niucloud/app/adminapi/controller/course/Course.php
  20. 22
      niucloud/app/adminapi/controller/student_courses/StudentCourses.php
  21. 14
      niucloud/app/adminapi/controller/sys/System.php
  22. 3
      niucloud/app/adminapi/route/communication_records.php
  23. 6
      niucloud/app/adminapi/route/student_courses.php
  24. 5
      niucloud/app/adminapi/route/sys.php
  25. 126
      niucloud/app/model/communication_records/CommunicationRecords.php
  26. 116
      niucloud/app/model/course/Course.php
  27. 55
      niucloud/app/model/student/Student.php
  28. 72
      niucloud/app/model/student_courses/StudentCourses.php
  29. 27
      niucloud/app/service/admin/communication_records/CommunicationRecordsService.php
  30. 45
      niucloud/app/service/admin/student_courses/StudentCoursesService.php
  31. 20
      niucloud/app/service/admin/sys/SystemService.php
  32. 6
      niucloud/app/validate/communication_records/CommunicationRecords.php

81
admin/components.d.ts

@ -0,0 +1,81 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
Attachment: typeof import('./src/components/upload-attachment/attachment.vue')['default']
DiyLink: typeof import('./src/components/diy-link/index.vue')['default']
Editor: typeof import('./src/components/editor/index.vue')['default']
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCol: typeof import('element-plus/es')['ElCol']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload']
ExportSure: typeof import('./src/components/export-sure/index.vue')['default']
HeatMap: typeof import('./src/components/heat-map/index.vue')['default']
Icon: typeof import('./src/components/icon/index.vue')['default']
PopoverInput: typeof import('./src/components/popover-input/index.vue')['default']
RangeInput: typeof import('./src/components/range-input/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SelectArea: typeof import('./src/components/select-area/index.vue')['default']
SelectIcon: typeof import('./src/components/select-icon/index.vue')['default']
TencentMapPicker: typeof import('./src/components/TencentMapPicker.vue')['default']
UploadAttachment: typeof import('./src/components/upload-attachment/index.vue')['default']
UploadFile: typeof import('./src/components/upload-file/index.vue')['default']
UploadImage: typeof import('./src/components/upload-image/index.vue')['default']
UploadVideo: typeof import('./src/components/upload-video/index.vue')['default']
Verify: typeof import('./src/components/verifition/Verify.vue')['default']
VerifyPoints: typeof import('./src/components/verifition/Verify/VerifyPoints.vue')['default']
VerifySlide: typeof import('./src/components/verifition/Verify/VerifySlide.vue')['default']
VideoPlayer: typeof import('./src/components/video-player/index.vue')['default']
WebLink: typeof import('./src/components/web-link/web-link.vue')['default']
}
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}

26
admin/src/app/api/communication_records.ts

@ -1,5 +1,7 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- communication_records // USER_CODE_BEGIN -- communication_records
/** /**
* *
@ -7,7 +9,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getCommunicationRecordsList(params: Record<string, any>) { export function getCommunicationRecordsList(params: Record<string, any>) {
return request.get(`communication_records/communication_records`, { params }) return request.get(`communication_records/communication_records`, {params})
} }
/** /**
@ -16,7 +18,7 @@ export function getCommunicationRecordsList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getCommunicationRecordsInfo(id: number) { export function getCommunicationRecordsInfo(id: number) {
return request.get(`communication_records/communication_records/${id}`) return request.get(`communication_records/communication_records/${id}`);
} }
/** /**
@ -25,10 +27,7 @@ export function getCommunicationRecordsInfo(id: number) {
* @returns * @returns
*/ */
export function addCommunicationRecords(params: Record<string, any>) { export function addCommunicationRecords(params: Record<string, any>) {
return request.post('communication_records/communication_records', params, { return request.post('communication_records/communication_records', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,11 +37,7 @@ export function addCommunicationRecords(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editCommunicationRecords(params: Record<string, any>) { export function editCommunicationRecords(params: Record<string, any>) {
return request.put( return request.put(`communication_records/communication_records/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
`communication_records/communication_records/${params.id}`,
params,
{ showErrorMessage: true, showSuccessMessage: true }
)
} }
/** /**
@ -51,10 +46,11 @@ export function editCommunicationRecords(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteCommunicationRecords(id: number) { export function deleteCommunicationRecords(id: number) {
return request.delete(`communication_records/communication_records/${id}`, { return request.delete(`communication_records/communication_records/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true, }
showSuccessMessage: true,
}) export function getWithCustomerResourcesList(params: Record<string,any>){
return request.get('communication_records/customer_resources_all', {params})
} }
// USER_CODE_END -- communication_records // USER_CODE_END -- communication_records

29
admin/src/app/api/student_courses.ts

@ -1,5 +1,9 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- student_courses // USER_CODE_BEGIN -- student_courses
/** /**
* *
@ -7,7 +11,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getStudentCoursesList(params: Record<string, any>) { export function getStudentCoursesList(params: Record<string, any>) {
return request.get(`student_courses/student_courses`, { params }) return request.get(`student_courses/student_courses`, {params})
} }
/** /**
@ -16,7 +20,7 @@ export function getStudentCoursesList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getStudentCoursesInfo(id: number) { export function getStudentCoursesInfo(id: number) {
return request.get(`student_courses/student_courses/${id}`) return request.get(`student_courses/student_courses/${id}`);
} }
/** /**
@ -25,10 +29,7 @@ export function getStudentCoursesInfo(id: number) {
* @returns * @returns
*/ */
export function addStudentCourses(params: Record<string, any>) { export function addStudentCourses(params: Record<string, any>) {
return request.post('student_courses/student_courses', params, { return request.post('student_courses/student_courses', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,10 +39,7 @@ export function addStudentCourses(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editStudentCourses(params: Record<string, any>) { export function editStudentCourses(params: Record<string, any>) {
return request.put(`student_courses/student_courses/${params.id}`, params, { return request.put(`student_courses/student_courses/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -50,10 +48,13 @@ export function editStudentCourses(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteStudentCourses(id: number) { export function deleteStudentCourses(id: number) {
return request.delete(`student_courses/student_courses/${id}`, { return request.delete(`student_courses/student_courses/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true, }
showSuccessMessage: true,
}) export function getWithStudentList(params: Record<string,any>){
return request.get('student_courses/student_all', {params})
}export function getWithCourseList(params: Record<string,any>){
return request.get('student_courses/course_all', {params})
} }
// USER_CODE_END -- student_courses // USER_CODE_END -- student_courses

11
admin/src/app/api/sys.ts

@ -764,3 +764,14 @@ export function getYjpzConfig() {
export function yjpzConfig(params: Record<string, any>) { export function yjpzConfig(params: Record<string, any>) {
return request.post(`sys/yjpz_config`, params) return request.post(`sys/yjpz_config`, params)
} }
export function xsyjConfig(params: Record<string, any>) {
return request.post(`sys/xsyj_config`, params)
}
export function getXsyjConfig() {
return request.get('sys/get_xsyj_config')
}

48
admin/src/app/lang/zh-cn/communication_records.communication_records.json

@ -1,31 +1,21 @@
{ {
"id": "沟通记录编号", "resourceId":"资源",
"idPlaceholder": "请输入沟通记录编号", "resourceIdPlaceholder":"全部",
"staffId": "员工ID", "resourceType":"资源类型",
"staffIdPlaceholder": "请输入员工ID", "resourceTypePlaceholder":"请输入资源类型",
"resourceId": "资源ID", "communicationType":"沟通类型",
"resourceIdPlaceholder": "请输入资源ID", "communicationTypePlaceholder":"请输入沟通类型",
"resourceType": "资源类型(如设备、文件、系统等)", "communicationResult":"沟通结果",
"resourceTypePlaceholder": "请输入资源类型(如设备、文件、系统等)", "communicationResultPlaceholder":"请输入沟通结果",
"communicationType": "沟通类型: phone-电话, email-邮件, meeting-会议, other-其他", "communicationTime":"沟通时间",
"communicationTypePlaceholder": "请输入沟通类型: phone-电话, email-邮件, meeting-会议, other-其他", "communicationTimePlaceholder":"请输入沟通时间",
"communicationResult": "沟通结果: success-成功, failure-失败, pending-待定", "remarks":"备注",
"communicationResultPlaceholder": "请输入沟通结果: success-成功, failure-失败, pending-待定", "remarksPlaceholder":"请输入备注",
"communicationTime": "沟通时间", "tag":"标签",
"communicationTimePlaceholder": "请输入沟通时间", "tagPlaceholder":"请输入标签",
"remarks": "备注", "addCommunicationRecords":"添加沟通记录",
"remarksPlaceholder": "请输入备注", "updateCommunicationRecords":"编辑沟通记录",
"tag": "标签: high-高, medium-中, low-低", "communicationRecordsDeleteTips":"确定要删除该数据吗?",
"tagPlaceholder": "请输入标签: high-高, medium-中, low-低", "startDate":"请选择开始时间",
"businessId": "关联的业务ID", "endDate":"请选择结束时间"
"businessIdPlaceholder": "请输入关联的业务ID",
"createdAt": "创建时间",
"createdAtPlaceholder": "请输入创建时间",
"updatedAt": "修改时间",
"updatedAtPlaceholder": "请输入修改时间",
"addCommunicationRecords": "添加沟通记录",
"updateCommunicationRecords": "编辑沟通记录",
"communicationRecordsDeleteTips": "确定要删除该数据吗?",
"startDate": "请选择开始时间",
"endDate": "请选择结束时间"
} }

4
admin/src/app/lang/zh-cn/course.course.json

@ -9,8 +9,8 @@
"durationPlaceholder": "请输入课程时长", "durationPlaceholder": "请输入课程时长",
"sessionCount": "课时数量", "sessionCount": "课时数量",
"sessionCountPlaceholder": "请输入课时数量", "sessionCountPlaceholder": "请输入课时数量",
"singleSessionCount": "单次逍客数量", "singleSessionCount": "单次消课数量",
"singleSessionCountPlaceholder": "请输入单次逍客数量", "singleSessionCountPlaceholder": "请输入单次消课数量",
"price": "课程价格", "price": "课程价格",
"pricePlaceholder": "请输入课程价格", "pricePlaceholder": "请输入课程价格",
"internalReminder": "内部提醒课时", "internalReminder": "内部提醒课时",

36
admin/src/app/lang/zh-cn/student_courses.student_courses.json

@ -1,21 +1,19 @@
{ {
"id": "记录编号", "studentId":"学员",
"idPlaceholder": "请输入记录编号", "studentIdPlaceholder":"全部",
"studentId": "学员ID", "courseId":"课程",
"studentIdPlaceholder": "请输入学员ID", "courseIdPlaceholder":"全部",
"courseId": "课程ID", "totalHours":"总正式课时数",
"courseIdPlaceholder": "请输入课程ID", "totalHoursPlaceholder":"请输入总正式课时数",
"totalHours": "总正式课时数", "giftHours":"赠送课时数",
"totalHoursPlaceholder": "请输入总正式课时数", "giftHoursPlaceholder":"请输入赠送课时数",
"giftHours": "赠送课时数", "startDate":"课程开始日期",
"giftHoursPlaceholder": "请输入赠送课时数", "startDatePlaceholder":"请输入课程开始日期",
"startDate": "课程开始日期", "endDate":"课程结束日期",
"startDatePlaceholder": "请输入课程开始日期", "endDatePlaceholder":"请输入课程结束日期",
"endDate": "课程结束日期", "addStudentCourses":"添加学员课程",
"endDatePlaceholder": "请输入课程结束日期", "updateStudentCourses":"编辑学员课程",
"addStudentCourses": "添加学员课程", "studentCoursesDeleteTips":"确定要删除该数据吗?",
"updateStudentCourses": "编辑学员课程", "startDate":"请选择开始时间",
"studentCoursesDeleteTips": "确定要删除该数据吗?", "endDate":"请选择结束时间"
"startDate": "请选择开始时间",
"endDate": "请选择结束时间"
} }

69
admin/src/app/views/campus_person_role/campus_person_role.vue

@ -65,29 +65,29 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- <el-form-item :label="t('deptId')" prop="dept_id">--> <el-form-item :label="t('deptId')" prop="dept_id">
<!-- <el-select--> <el-select
<!-- class="w-[280px]"--> class="w-[280px]"
<!-- v-model="campusPersonRoleTable.searchParam.dept_id"--> v-model="campusPersonRoleTable.searchParam.dept_id"
<!-- clearable--> clearable
<!-- :placeholder="t('deptIdPlaceholder')"--> :placeholder="t('deptIdPlaceholder')"
<!-- >--> >
<!-- <el-option--> <el-option
<!-- v-for="(item, index) in deptIdList"--> v-for="(item, index) in deptIdList"
<!-- :key="index"--> :key="index"
<!-- :label="item['department_name']"--> :label="item['department_name']"
<!-- :value="item['id']"--> :value="item['id']"
<!-- />--> />
<!-- </el-select>--> </el-select>
<!-- </el-form-item>--> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="loadCampusPersonRoleList()" <el-button type="primary" @click="loadCampusPersonRoleList()">{{
>{{ t('search') }} t('search')
</el-button> }}</el-button>
<el-button @click="resetForm(searchFormRef)" <el-button @click="resetForm(searchFormRef)">{{
>{{ t('reset') }} t('reset')
</el-button> }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@ -137,12 +137,12 @@
min-width="120" min-width="120"
> >
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)" <el-button type="primary" link @click="editEvent(row)">{{
>{{ t('edit') }} t('edit')
</el-button> }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)" <el-button type="primary" link @click="deleteEvent(row.id)">{{
>{{ t('delete') }} t('delete')
</el-button> }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -169,7 +169,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import { import {
getCampusPersonRoleList, getCampusPersonRoleList,
deleteCampusPersonRole, deleteCampusPersonRole,
@ -178,14 +178,15 @@ import {
getWithSysRoleList, getWithSysRoleList,
getWithDepartmentsList, getWithDepartmentsList,
} from '@/app/api/campus_person_role' } from '@/app/api/campus_person_role'
import { getQueryString } from '@/utils/common' import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus' import { ElMessageBox, FormInstance } from 'element-plus'
import Edit from '@/app/views/campus_person_role/components/campus-person-role-edit.vue' import Edit from '@/app/views/campus_person_role/components/campus-person-role-edit.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
const pageName = route.meta.title const pageName = route.meta.title
// ?dept_id=1
const dept_id = pageName == '市场人员列表' ? 1 : 2;
let campusPersonRoleTable = reactive({ let campusPersonRoleTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
@ -196,7 +197,7 @@ let campusPersonRoleTable = reactive({
campus_id: '', campus_id: '',
person_id: '', person_id: '',
role_id: '', role_id: '',
dept_id: getQueryString(route.path, 'dept_id'), dept_id: dept_id,
}, },
}) })
@ -205,6 +206,8 @@ const searchFormRef = ref<FormInstance>()
// //
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
//
/** /**
* 获取角色关系列表 * 获取角色关系列表
*/ */
@ -234,9 +237,7 @@ const editCampusPersonRoleDialog: Record<string, any> | null = ref(null)
* 添加角色关系 * 添加角色关系
*/ */
const addEvent = () => { const addEvent = () => {
editCampusPersonRoleDialog.value.setFormData({ editCampusPersonRoleDialog.value.setFormData()
dept_id: campusPersonRoleTable.searchParam.dept_id,
})
editCampusPersonRoleDialog.value.showDialog = true editCampusPersonRoleDialog.value.showDialog = true
} }

464
admin/src/app/views/communication_records/communication_records.vue

@ -1,268 +1,127 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addCommunicationRecords') }} <el-button type="primary" @click="addEvent">
</el-button> {{ t('addCommunicationRecords') }}
</div> </el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="communicationRecordsTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('resourceId')" prop="resource_id">
:inline="true" <el-select class="w-[280px]" v-model="communicationRecordsTable.searchParam.resource_id" clearable :placeholder="t('resourceIdPlaceholder')">
:model="communicationRecordsTable.searchParam" <el-option
ref="searchFormRef" v-for="(item, index) in resourceIdList"
> :key="index"
<el-form-item :label="t('staffId')" prop="staff_id"> :label="item['name']"
<el-input :value="item['id']"
v-model="communicationRecordsTable.searchParam.staff_id" />
:placeholder="t('staffIdPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('resourceId')" prop="resource_id">
<el-input <el-form-item :label="t('communicationType')" prop="communication_type">
v-model="communicationRecordsTable.searchParam.resource_id" <el-select class="w-[280px]" v-model="communicationRecordsTable.searchParam.communication_type" clearable :placeholder="t('communicationTypePlaceholder')">
:placeholder="t('resourceIdPlaceholder')" <el-option label="全部" value=""></el-option>
/> <el-option
</el-form-item> v-for="(item, index) in communication_typeList"
<el-form-item :label="t('resourceType')" prop="resource_type"> :key="index"
<el-input :label="item.name"
v-model="communicationRecordsTable.searchParam.resource_type" :value="item.value"
:placeholder="t('resourceTypePlaceholder')" />
/> </el-select>
</el-form-item> </el-form-item>
<el-form-item
:label="t('communicationType')" <el-form-item>
prop="communication_type" <el-button type="primary" @click="loadCommunicationRecordsList()">{{ t('search') }}</el-button>
> <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
<el-input </el-form-item>
v-model="communicationRecordsTable.searchParam.communication_type" </el-form>
:placeholder="t('communicationTypePlaceholder')" </el-card>
/>
</el-form-item> <div class="mt-[10px]">
<el-form-item <el-table :data="communicationRecordsTable.data" size="large" v-loading="communicationRecordsTable.loading">
:label="t('communicationResult')" <template #empty>
prop="communication_result" <span>{{ !communicationRecordsTable.loading ? t('emptyData') : '' }}</span>
> </template>
<el-input <el-table-column prop="resource_id_name" :label="t('resourceId')" min-width="120" :show-overflow-tooltip="true"/>
v-model="
communicationRecordsTable.searchParam.communication_result <el-table-column prop="resource_type" :label="t('resourceType')" min-width="120" :show-overflow-tooltip="true"/>
"
:placeholder="t('communicationResultPlaceholder')" <el-table-column :label="t('communicationType')" min-width="180" align="center" :show-overflow-tooltip="true">
/> <template #default="{ row }">
</el-form-item> <div v-for="(item, index) in communication_typeList">
<el-form-item <div v-if="item.value == row.communication_type">{{ item.name }}</div>
:label="t('communicationTime')" </div>
prop="communication_time" </template>
> </el-table-column>
<el-input
v-model="communicationRecordsTable.searchParam.communication_time" <el-table-column :label="t('communicationResult')" min-width="180" align="center" :show-overflow-tooltip="true">
:placeholder="t('communicationTimePlaceholder')" <template #default="{ row }">
/> <div v-for="(item, index) in communication_resultList">
</el-form-item> <div v-if="item.value == row.communication_result">{{ item.name }}</div>
<el-form-item :label="t('remarks')" prop="remarks"> </div>
<el-input </template>
v-model="communicationRecordsTable.searchParam.remarks" </el-table-column>
:placeholder="t('remarksPlaceholder')"
/> <el-table-column prop="communication_time" :label="t('communicationTime')" min-width="120" :show-overflow-tooltip="true"/>
</el-form-item>
<el-form-item :label="t('tag')" prop="tag"> <el-table-column prop="remarks" :label="t('remarks')" min-width="120" :show-overflow-tooltip="true"/>
<el-input
v-model="communicationRecordsTable.searchParam.tag" <el-table-column :label="t('tag')" min-width="180" align="center" :show-overflow-tooltip="true">
:placeholder="t('tagPlaceholder')" <template #default="{ row }">
/> <div v-for="(item, index) in tagList">
</el-form-item> <div v-if="item.value == row.tag">{{ item.name }}</div>
<el-form-item :label="t('businessId')" prop="business_id"> </div>
<el-input </template>
v-model="communicationRecordsTable.searchParam.business_id" </el-table-column>
:placeholder="t('businessIdPlaceholder')"
/> <el-table-column :label="t('operation')" fixed="right" min-width="120">
</el-form-item> <template #default="{ row }">
<el-form-item :label="t('createdAt')" prop="created_at"> <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-input <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
v-model="communicationRecordsTable.searchParam.created_at" </template>
:placeholder="t('createdAtPlaceholder')" </el-table-column>
/>
</el-form-item> </el-table>
<el-form-item :label="t('updatedAt')" prop="updated_at"> <div class="mt-[16px] flex justify-end">
<el-input <el-pagination v-model:current-page="communicationRecordsTable.page" v-model:page-size="communicationRecordsTable.limit"
v-model="communicationRecordsTable.searchParam.updated_at" layout="total, sizes, prev, pager, next, jumper" :total="communicationRecordsTable.total"
:placeholder="t('updatedAtPlaceholder')" @size-change="loadCommunicationRecordsList()" @current-change="loadCommunicationRecordsList" />
/> </div>
</el-form-item> </div>
<el-form-item> <edit ref="editCommunicationRecordsDialog" @complete="loadCommunicationRecordsList" />
<el-button type="primary" @click="loadCommunicationRecordsList()">{{ </el-card>
t('search') </div>
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="communicationRecordsTable.data"
size="large"
v-loading="communicationRecordsTable.loading"
>
<template #empty>
<span>{{
!communicationRecordsTable.loading ? t('emptyData') : ''
}}</span>
</template>
<el-table-column
prop="staff_id"
:label="t('staffId')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="resource_id"
:label="t('resourceId')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="resource_type"
:label="t('resourceType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="communication_type"
:label="t('communicationType')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="communication_result"
:label="t('communicationResult')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="communication_time"
:label="t('communicationTime')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="remarks"
:label="t('remarks')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="tag"
:label="t('tag')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="business_id"
:label="t('businessId')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="created_at"
:label="t('createdAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="updated_at"
:label="t('updatedAt')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="communicationRecordsTable.page"
v-model:page-size="communicationRecordsTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="communicationRecordsTable.total"
@size-change="loadCommunicationRecordsList()"
@current-change="loadCommunicationRecordsList"
/>
</div>
</div>
<edit
ref="editCommunicationRecordsDialog"
@complete="loadCommunicationRecordsList"
/>
</el-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { import { getCommunicationRecordsList, deleteCommunicationRecords, getWithCustomerResourcesList } from '@/app/api/communication_records'
getCommunicationRecordsList,
deleteCommunicationRecords,
} from '@/app/api/communication_records'
import { img } from '@/utils/common' import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus' import { ElMessageBox,FormInstance } from 'element-plus'
import Edit from '@/app/views/communication_records/components/communication-records-edit.vue' import Edit from '@/app/views/communication_records/components/communication-records-edit.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
const pageName = route.meta.title const pageName = route.meta.title;
let communicationRecordsTable = reactive({ let communicationRecordsTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam:{
staff_id: '', "resource_id":"",
resource_id: '', "communication_type":""
resource_type: '', }
communication_type: '',
communication_result: '',
communication_time: '',
remarks: '',
tag: '',
business_id: '',
created_at: '',
updated_at: '',
},
}) })
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
@ -271,26 +130,39 @@ const searchFormRef = ref<FormInstance>()
const selectData = ref<any[]>([]) const selectData = ref<any[]>([])
// //
const communication_typeList = ref([] as any[])
const communication_typeDictList = async () => {
communication_typeList.value = await (await useDictionary('communication_type')).data.dictionary
}
communication_typeDictList();
const communication_resultList = ref([] as any[])
const communication_resultDictList = async () => {
communication_resultList.value = await (await useDictionary('communication_result')).data.dictionary
}
communication_resultDictList();
const tagList = ref([] as any[])
const tagDictList = async () => {
tagList.value = await (await useDictionary('tag')).data.dictionary
}
tagDictList();
/** /**
* 获取沟通记录列表 * 获取沟通记录列表
*/ */
const loadCommunicationRecordsList = (page: number = 1) => { const loadCommunicationRecordsList = (page: number = 1) => {
communicationRecordsTable.loading = true communicationRecordsTable.loading = true
communicationRecordsTable.page = page communicationRecordsTable.page = page
getCommunicationRecordsList({ getCommunicationRecordsList({
page: communicationRecordsTable.page, page: communicationRecordsTable.page,
limit: communicationRecordsTable.limit, limit: communicationRecordsTable.limit,
...communicationRecordsTable.searchParam, ...communicationRecordsTable.searchParam
}) }).then(res => {
.then((res) => { communicationRecordsTable.loading = false
communicationRecordsTable.loading = false communicationRecordsTable.data = res.data.data
communicationRecordsTable.data = res.data.data communicationRecordsTable.total = res.data.total
communicationRecordsTable.total = res.data.total }).catch(() => {
}) communicationRecordsTable.loading = false
.catch(() => {
communicationRecordsTable.loading = false
}) })
} }
loadCommunicationRecordsList() loadCommunicationRecordsList()
@ -301,8 +173,8 @@ const editCommunicationRecordsDialog: Record<string, any> | null = ref(null)
* 添加沟通记录 * 添加沟通记录
*/ */
const addEvent = () => { const addEvent = () => {
editCommunicationRecordsDialog.value.setFormData() editCommunicationRecordsDialog.value.setFormData()
editCommunicationRecordsDialog.value.showDialog = true editCommunicationRecordsDialog.value.showDialog = true
} }
/** /**
@ -310,42 +182,50 @@ const addEvent = () => {
* @param data * @param data
*/ */
const editEvent = (data: any) => { const editEvent = (data: any) => {
editCommunicationRecordsDialog.value.setFormData(data) editCommunicationRecordsDialog.value.setFormData(data)
editCommunicationRecordsDialog.value.showDialog = true editCommunicationRecordsDialog.value.showDialog = true
} }
/** /**
* 删除沟通记录 * 删除沟通记录
*/ */
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('communicationRecordsDeleteTips'), t('warning'), { ElMessageBox.confirm(t('communicationRecordsDeleteTips'), t('warning'),
confirmButtonText: t('confirm'), {
cancelButtonText: t('cancel'), confirmButtonText: t('confirm'),
type: 'warning', cancelButtonText: t('cancel'),
}).then(() => { type: 'warning',
deleteCommunicationRecords(id) }
.then(() => { ).then(() => {
loadCommunicationRecordsList() deleteCommunicationRecords(id).then(() => {
}) loadCommunicationRecordsList()
.catch(() => {}) }).catch(() => {
}) })
})
} }
const resourceIdList = ref([])
const setResourceIdList = async () => {
resourceIdList.value = await (await getWithCustomerResourcesList({})).data
}
setResourceIdList()
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
formEl.resetFields() formEl.resetFields()
loadCommunicationRecordsList() loadCommunicationRecordsList()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */ /* 多行超出隐藏 */
.multi-hidden { .multi-hidden {
word-break: break-all; word-break: break-all;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
</style> </style>

440
admin/src/app/views/communication_records/components/communication-records-edit.vue

@ -1,138 +1,82 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateCommunicationRecords') : t('addCommunicationRecords')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
v-model="showDialog" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
:title=" <el-form-item :label="t('resourceId')" prop="resource_id">
formData.id <el-select class="input-width" v-model="formData.resource_id" clearable :placeholder="t('resourceIdPlaceholder')">
? t('updateCommunicationRecords') <el-option label="请选择" value=""></el-option>
: t('addCommunicationRecords') <el-option
" v-for="(item, index) in resourceIdList"
width="50%" :key="index"
class="diy-dialog-wrap" :label="item['name']"
:destroy-on-close="true" :value="item['id']"
> />
<el-form </el-select>
:model="formData" </el-form-item>
label-width="120px"
ref="formRef" <el-form-item :label="t('resourceType')" prop="resource_type">
:rules="formRules" <el-input v-model="formData.resource_type" clearable :placeholder="t('resourceTypePlaceholder')" class="input-width" />
class="page-form" </el-form-item>
v-loading="loading"
> <el-form-item :label="t('communicationType')" prop="communication_type">
<el-form-item :label="t('staffId')" prop="staff_id"> <el-select class="input-width" v-model="formData.communication_type" clearable :placeholder="t('communicationTypePlaceholder')">
<el-input <el-option label="请选择" value=""></el-option>
v-model="formData.staff_id" <el-option
clearable v-for="(item, index) in communication_typeList"
:placeholder="t('staffIdPlaceholder')" :key="index"
class="input-width" :label="item.name"
/> :value="item.value"
</el-form-item> />
</el-select>
<el-form-item :label="t('resourceId')" prop="resource_id"> </el-form-item>
<el-input
v-model="formData.resource_id" <el-form-item :label="t('communicationResult')" prop="communication_result">
clearable <el-select class="input-width" v-model="formData.communication_result" clearable :placeholder="t('communicationResultPlaceholder')">
:placeholder="t('resourceIdPlaceholder')" <el-option label="请选择" value=""></el-option>
class="input-width" <el-option
/> v-for="(item, index) in communication_resultList"
</el-form-item> :key="index"
:label="item.name"
<el-form-item :label="t('resourceType')" prop="resource_type"> :value="item.value"
<el-input />
v-model="formData.resource_type" </el-select>
clearable </el-form-item>
:placeholder="t('resourceTypePlaceholder')"
class="input-width" <el-form-item :label="t('communicationTime')" prop="communication_time" class="input-width">
/> <el-date-picker
</el-form-item> class="flex-1 !flex"
v-model="formData.communication_time"
<el-form-item :label="t('communicationType')" prop="communication_type"> clearable
<el-input type="datetime"
v-model="formData.communication_type" value-format="YYYY-MM-DD HH:mm:ss"
clearable :placeholder="t('communicationTimePlaceholder')">
:placeholder="t('communicationTypePlaceholder')" </el-date-picker>
class="input-width" </el-form-item>
/> <el-form-item :label="t('remarks')" >
</el-form-item> <el-input v-model="formData.remarks" type="textarea" rows="4" clearable :placeholder="t('remarksPlaceholder')" class="input-width"/>
</el-form-item>
<el-form-item <el-form-item :label="t('tag')" >
:label="t('communicationResult')" <el-select class="input-width" v-model="formData.tag" clearable :placeholder="t('tagPlaceholder')">
prop="communication_result" <el-option label="请选择" value=""></el-option>
> <el-option
<el-input v-for="(item, index) in tagList"
v-model="formData.communication_result" :key="index"
clearable :label="item.name"
:placeholder="t('communicationResultPlaceholder')" :value="item.value"
class="input-width" />
/> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('communicationTime')" prop="communication_time"> </el-form>
<el-input
v-model="formData.communication_time" <template #footer>
clearable <span class="dialog-footer">
:placeholder="t('communicationTimePlaceholder')" <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
class="input-width" <el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
/> t('confirm')
</el-form-item> }}</el-button>
</span>
<el-form-item :label="t('remarks')"> </template>
<el-input </el-dialog>
v-model="formData.remarks"
clearable
:placeholder="t('remarksPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('tag')">
<el-input
v-model="formData.tag"
clearable
:placeholder="t('tagPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('businessId')">
<el-input
v-model="formData.business_id"
clearable
:placeholder="t('businessIdPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('createdAt')">
<el-input
v-model="formData.created_at"
clearable
:placeholder="t('createdAtPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('updatedAt')">
<el-input
v-model="formData.updated_at"
clearable
:placeholder="t('updatedAtPlaceholder')"
class="input-width"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -140,11 +84,7 @@ import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { import { addCommunicationRecords, editCommunicationRecords, getCommunicationRecordsInfo, getWithCustomerResourcesList } from '@/app/api/communication_records'
addCommunicationRecords,
editCommunicationRecords,
getCommunicationRecordsInfo,
} from '@/app/api/communication_records'
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
@ -153,18 +93,14 @@ const loading = ref(false)
* 表单数据 * 表单数据
*/ */
const initialFormData = { const initialFormData = {
id: '', id: '',
staff_id: '', resource_id: '',
resource_id: '', resource_type: '',
resource_type: '', communication_type: '',
communication_type: '', communication_result: '',
communication_result: '', communication_time: '',
communication_time: '', remarks: '',
remarks: '', tag: '',
tag: '',
business_id: '',
created_at: '',
updated_at: '',
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -172,55 +108,43 @@ const formRef = ref<FormInstance>()
// //
const formRules = computed(() => { const formRules = computed(() => {
return { return {
staff_id: [
{ required: true, message: t('staffIdPlaceholder'), trigger: 'blur' },
],
resource_id: [ resource_id: [
{ required: true, message: t('resourceIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('resourceIdPlaceholder'), trigger: 'blur' },
],
]
,
resource_type: [ resource_type: [
{ { required: true, message: t('resourceTypePlaceholder'), trigger: 'blur' },
required: true,
message: t('resourceTypePlaceholder'), ]
trigger: 'blur', ,
},
],
communication_type: [ communication_type: [
{ { required: true, message: t('communicationTypePlaceholder'), trigger: 'blur' },
required: true,
message: t('communicationTypePlaceholder'), ]
trigger: 'blur', ,
},
],
communication_result: [ communication_result: [
{ { required: true, message: t('communicationResultPlaceholder'), trigger: 'blur' },
required: true,
message: t('communicationResultPlaceholder'), ]
trigger: 'blur', ,
},
],
communication_time: [ communication_time: [
{ { required: true, message: t('communicationTimePlaceholder'), trigger: 'blur' },
required: true,
message: t('communicationTimePlaceholder'), ]
trigger: 'blur', ,
},
],
remarks: [ remarks: [
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' }, { required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
],
tag: [{ required: true, message: t('tagPlaceholder'), trigger: 'blur' }], ]
business_id: [ ,
{ required: true, message: t('businessIdPlaceholder'), trigger: 'blur' }, tag: [
], { required: true, message: t('tagPlaceholder'), trigger: 'blur' },
created_at: [
{ required: true, message: t('createdAtPlaceholder'), trigger: 'blur' }, ]
], ,
updated_at: [ }
{ required: true, message: t('updatedAtPlaceholder'), trigger: 'blur' },
],
}
}) })
const emit = defineEmits(['complete']) const emit = defineEmits(['complete'])
@ -230,93 +154,109 @@ const emit = defineEmits(['complete'])
* @param formEl * @param formEl
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
let save = formData.id ? editCommunicationRecords : addCommunicationRecords let save = formData.id ? editCommunicationRecords : addCommunicationRecords
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
let data = formData let data = formData
save(data) save(data).then(res => {
.then((res) => { loading.value = false
loading.value = false showDialog.value = false
showDialog.value = false emit('complete')
emit('complete') }).catch(err => {
}) loading.value = false
.catch((err) => { })
loading.value = false }
}) })
}
})
} }
// //
let communication_typeList = ref([])
const communication_typeDictList = async () => {
communication_typeList.value = await (await useDictionary('communication_type')).data.dictionary
}
communication_typeDictList();
watch(() => communication_typeList.value, () => { formData.communication_type = communication_typeList.value[0].value })
let communication_resultList = ref([])
const communication_resultDictList = async () => {
communication_resultList.value = await (await useDictionary('communication_result')).data.dictionary
}
communication_resultDictList();
watch(() => communication_resultList.value, () => { formData.communication_result = communication_resultList.value[0].value })
let tagList = ref([])
const tagDictList = async () => {
tagList.value = await (await useDictionary('tag')).data.dictionary
}
tagDictList();
watch(() => tagList.value, () => { formData.tag = tagList.value[0].value })
const resourceIdList = ref([] as any[])
const setResourceIdList = async () => {
resourceIdList.value = await (await getWithCustomerResourcesList({})).data
}
setResourceIdList()
const setFormData = async (row: any = null) => { const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if (row) { if(row){
const data = await (await getCommunicationRecordsInfo(row.id)).data const data = await (await getCommunicationRecordsInfo(row.id)).data
if (data) if (data) Object.keys(formData).forEach((key: string) => {
Object.keys(formData).forEach((key: string) => { if (data[key] != undefined) formData[key] = data[key]
if (data[key] != undefined) formData[key] = data[key] })
}) }
} loading.value = false
loading.value = false
} }
// //
const mobileVerify = (rule: any, value: any, callback: any) => { const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) { if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile'))) callback(new Error(t('generateMobile')))
} else { } else {
callback() callback()
} }
} }
// //
const idCardVerify = (rule: any, value: any, callback: any) => { const idCardVerify = (rule: any, value: any, callback: any) => {
if ( if (value && !/^[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(value)) {
value && callback(new Error(t('generateIdCard')))
!/^[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( } else {
value callback()
) }
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
} }
// //
const emailVerify = (rule: any, value: any, callback: any) => { const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail'))) callback(new Error(t('generateEmail')))
} else { } else {
callback() callback()
} }
} }
// //
const numberVerify = (rule: any, value: any, callback: any) => { const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) { if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber'))) callback(new Error(t('generateNumber')))
} else { } else {
callback() callback()
} }
} }
defineExpose({ defineExpose({
showDialog, showDialog,
setFormData, setFormData
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>
<style lang="scss"> <style lang="scss">
.diy-dialog-wrap .el-form-item__label { .diy-dialog-wrap .el-form-item__label{
height: auto !important; height: auto !important;
} }
</style> </style>

22
admin/src/app/views/course/components/course-edit.vue

@ -24,14 +24,19 @@
</el-form-item> </el-form-item>
<el-form-item :label="t('courseType')" prop="course_type"> <el-form-item :label="t('courseType')" prop="course_type">
<el-input <el-select
class="input-width"
v-model="formData.course_type" v-model="formData.course_type"
clearable
:placeholder="t('courseTypePlaceholder')" :placeholder="t('courseTypePlaceholder')"
class="input-width" >
/> <el-option
v-for="(item, index) in courseTypeList"
:key="index"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('duration')" prop="duration"> <el-form-item :label="t('duration')" prop="duration">
<el-input <el-input
v-model="formData.duration" v-model="formData.duration"
@ -193,6 +198,13 @@ const formRules = computed(() => {
const emit = defineEmits(['complete']) const emit = defineEmits(['complete'])
const courseTypeList = ref([])
const getcourseTypeList = async () => {
courseTypeList.value = await (
await useDictionary('course_type')
).data.dictionary
}
getcourseTypeList()
/** /**
* 确认 * 确认
* @param formEl * @param formEl

13
admin/src/app/views/course/course.vue

@ -24,10 +24,17 @@
/> />
</el-form-item> </el-form-item>
<el-form-item :label="t('courseType')" prop="course_type"> <el-form-item :label="t('courseType')" prop="course_type">
<el-input <el-select
v-model="courseTable.searchParam.course_type" v-model="courseTable.searchParam.course_type"
:placeholder="t('courseTypePlaceholder')" :placeholder="t('courseTypePlaceholder')"
/> >
<el-option
v-for="item in courseTypeList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('duration')" prop="duration"> <el-form-item :label="t('duration')" prop="duration">
<el-input <el-input
@ -220,7 +227,7 @@ let courseTable = reactive({
remarks: '', remarks: '',
}, },
}) })
const courseTypeList = useDictionary('course_type')
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
// //

329
admin/src/app/views/setting/pay.vue

@ -1,116 +1,63 @@
<template> <template>
<!--支付设置--> <!--支付设置-->
<div class="main-container" v-loading="payLoading"> <div class="main-container" v-loading="payLoading">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center" v-if="!payLoading">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="isEdit = true" ref="setConfigBtn">{{
t('setConfig')
}}</el-button>
</div>
</el-card>
<el-card <el-card class="box-card !border-none" shadow="never">
class="box-card mt-[15px] !border-none" <div class="flex justify-between items-center" v-if="!payLoading">
shadow="never" <span class="text-page-title">{{ pageName }}</span>
v-for="(payItems, payKey) in payConfigData" <el-button type="primary" @click="isEdit = true" ref="setConfigBtn">{{ t('setConfig') }}</el-button>
:key="payKey" </div>
> </el-card>
<h3 class="panel-title !text-sm">{{ payItems.name }}</h3>
<div> <el-card class="box-card mt-[15px] !border-none" shadow="never" v-for="(payItems, payKey) in payConfigData" :key="payKey">
<div <h3 class="panel-title !text-sm">{{ payItems.name }}</h3>
class="flex items-center justify-between p-[10px] table-item-border bg"
>
<span class="text-base w-[230px]">{{ t('payType') }}</span>
<span class="text-base w-[110px] text-center">{{
t('onState')
}}</span>
<span class="text-base w-[80px] text-center" v-if="isEdit">{{
t('templateName')
}}</span>
</div>
<div ref="fieldBoxRefs" :data-key="payKey"> <div>
<div <div class="flex items-center justify-between p-[10px] table-item-border bg">
class="flex items-center justify-between p-[10px] table-item-border" <span class="text-base w-[230px]">{{ t('payType') }}</span>
v-for="(childrenItem, childrenIndex) in payItems.pay_type" <span class="text-base w-[110px] text-center">{{ t('onState') }}</span>
:key="childrenItem.redio_key" <span class="text-base w-[80px] text-center" v-if="isEdit">{{ t('templateName') }}</span>
:id="payKey + '_' + childrenIndex" </div>
>
<div class="flex w-[230px] flex-shrink-0"> <div ref="fieldBoxRefs" :data-key="payKey">
<span <div class="flex items-center justify-between p-[10px] table-item-border" v-for="(childrenItem, childrenIndex) in payItems.pay_type" :key="childrenItem.redio_key" :id="payKey + '_' + childrenIndex">
v-if="isEdit" <div class="flex w-[230px] flex-shrink-0">
class="iconfont icontuodong mr-2 handle cursor-pointer" <span v-if="isEdit" class="iconfont icontuodong mr-2 handle cursor-pointer"></span>
></span> <div class="flex items-center select-none">
<div class="flex items-center select-none"> <div class="mr-[15px] w-[30px] h-[30px] flex-shrink-0">
<div class="mr-[15px] w-[30px] h-[30px] flex-shrink-0"> <img class="w-[30px]" :src="img(childrenItem.icon)" />
<img class="w-[30px]" :src="img(childrenItem.icon)" /> </div>
<span class="text-base text-[#666]">{{ childrenItem.name }}</span>
</div>
</div>
<div class="flex items-center justify-center w-[110px] select-none">
<el-switch v-if="isEdit" v-model="childrenItem.status" :active-value="1" :inactive-value="0" :active-text="t('isEnable')" @change="enablePaymentMode(childrenItem)" />
<div v-else>
<el-tag v-if="childrenItem.status" class="ml-2" type="success">{{ t('open') }}</el-tag>
<el-tag v-else class="ml-2" type="info">{{ t('notOpen') }}</el-tag>
</div>
</div>
<div class="flex items-center justify-center w-[80px] select-none" v-if="isEdit">
<button class="text-base" @click="configPayFn(childrenItem)" v-if="childrenItem.setting_component">{{ t('clickConfigure') }}</button>
<button v-else>--</button>
</div>
</div>
</div> </div>
<span class="text-base text-[#666]">{{
childrenItem.name
}}</span>
</div>
</div>
<div class="flex items-center justify-center w-[110px] select-none">
<el-switch
v-if="isEdit"
v-model="childrenItem.status"
:active-value="1"
:inactive-value="0"
:active-text="t('isEnable')"
@change="enablePaymentMode(childrenItem)"
/>
<div v-else>
<el-tag
v-if="childrenItem.status"
class="ml-2"
type="success"
>{{ t('open') }}</el-tag
>
<el-tag v-else class="ml-2" type="info">{{
t('notOpen')
}}</el-tag>
</div>
</div> </div>
<div </el-card>
class="flex items-center justify-center w-[80px] select-none"
v-if="isEdit" <div class="fixed-footer-wrap" v-if="isEdit">
> <div class="fixed-footer">
<button <el-button type="primary" :loading="loading" @click="cancelFn">{{ t('cancel') }}</el-button>
class="text-base" <el-button type="primary" :loading="loading" @click="saveFn(formRef)">{{ t('save') }}</el-button>
@click="configPayFn(childrenItem)"
v-if="childrenItem.setting_component"
>
{{ t('clickConfigure') }}
</button>
<button v-else>--</button>
</div> </div>
</div>
</div> </div>
</div>
</el-card>
<div class="fixed-footer-wrap" v-if="isEdit"> <template v-for="(item, index) in payTypeList">
<div class="fixed-footer"> <component :is="item.setting_component" :ref="(el) => setPayTypeRefs(el, item.key)" v-if="item.setting_component" @complete="setConfigInfo"/>
<el-button type="primary" :loading="loading" @click="cancelFn">{{ </template>
t('cancel')
}}</el-button>
<el-button type="primary" :loading="loading" @click="saveFn(formRef)">{{
t('save')
}}</el-button>
</div>
</div>
<template v-for="(item, index) in payTypeList"> </div>
<component
:is="item.setting_component"
:ref="(el) => setPayTypeRefs(el, item.key)"
v-if="item.setting_component"
@complete="setConfigInfo"
/>
</template>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -132,17 +79,14 @@ const payTypeRefs = ref({})
const modules: any = import.meta.glob('@/**/*.vue') const modules: any = import.meta.glob('@/**/*.vue')
getAllPayType().then(({ data }) => { getAllPayType().then(({ data }) => {
Object.keys(data).forEach((key: string) => { Object.keys(data).forEach((key: string) => {
data[key].setting_component && data[key].setting_component && (data[key].setting_component = defineAsyncComponent(modules[data[key].setting_component]))
(data[key].setting_component = defineAsyncComponent( })
modules[data[key].setting_component] payTypeList.value = data
))
})
payTypeList.value = data
}) })
const setPayTypeRefs = (el: any, index: string) => { const setPayTypeRefs = (el: any, index: string) => {
payTypeRefs.value[index] = el payTypeRefs.value[index] = (el)
} }
const payLoading = ref(true) const payLoading = ref(true)
@ -153,127 +97,124 @@ const setConfigBtn = ref()
// //
const payConfigData = ref([]) const payConfigData = ref([])
const checkPayConfigList = () => { const checkPayConfigList = () => {
getPayConfigList().then((res) => { getPayConfigList().then(res => {
const payData = res.data const payData = res.data
for (const i in payData) { for (const i in payData) {
const payType = payData[i].pay_type const payType = payData[i].pay_type
const pay_type = [] const pay_type = []
let default_key = '' let default_key = ''
payType.forEach((item, index) => { payType.forEach((item, index) => {
item.redio_key = payData[i].key + '_' + item.key item.redio_key = payData[i].key + '_' + item.key
item.defauit_key = '' item.defauit_key = ''
if (item.is_default == 1) { if (item.is_default == 1) {
default_key = item.redio_key default_key = item.redio_key
}
pay_type.push(item)
})
payData[payData[i].key].default_pay_type = default_key
payData[payData[i].key].pay_type = pay_type
} }
pay_type.push(item) payConfigData.value = payData
}) payLoading.value = false
nextTick(() => {
payData[payData[i].key].default_pay_type = default_key fieldBoxRefs.value.forEach((item, index) => {
payData[payData[i].key].pay_type = pay_type sortableFn(item, index)
} })
payConfigData.value = payData })
payLoading.value = false
nextTick(() => {
fieldBoxRefs.value.forEach((item, index) => {
sortableFn(item, index)
})
}) })
})
} }
checkPayConfigList() checkPayConfigList()
// //
const setConfigInfo = (data: any) => { const setConfigInfo = (data:any) => {
payConfigData.value[data.channel].pay_type.forEach((element) => { payConfigData.value[data.channel].pay_type.forEach(element => {
if (element.key == data.type) { if (element.key == data.type) {
element.config = data.config element.config = data.config
} }
}) })
console.log(payConfigData.value) console.log(payConfigData.value)
} }
// //
const configPayFn = (data: any) => { const configPayFn = (data:any) => {
payTypeRefs.value[data.key].setFormData(data) payTypeRefs.value[data.key].setFormData(data)
payTypeRefs.value[data.key].showDialog = true payTypeRefs.value[data.key].showDialog = true
} }
// //
const enablePaymentMode = async (data: any) => { const enablePaymentMode = async (data: any) => {
if ( if (payTypeRefs.value[data.key] && typeof payTypeRefs.value[data.key].enableVerify == 'function') {
payTypeRefs.value[data.key] && payTypeRefs.value[data.key].setFormData(data)
typeof payTypeRefs.value[data.key].enableVerify == 'function'
) { const verify = payTypeRefs.value[data.key].enableVerify()
payTypeRefs.value[data.key].setFormData(data) if (!verify) {
data.status = 0
const verify = payTypeRefs.value[data.key].enableVerify() ElMessage(t('configurePaymentMethod'))
if (!verify) { return false
data.status = 0 }
ElMessage(t('configurePaymentMethod'))
return false
} }
}
} }
interface SortableEvt extends SortableEvent { interface SortableEvt extends SortableEvent {
originalEvent?: DragEvent originalEvent?: DragEvent
} }
// //
const fieldBoxRefs = ref<any>([]) const fieldBoxRefs = ref<any>([])
watch(isEdit, (newValue, oldValue) => { watch(isEdit, (newValue, oldValue) => {
if (newValue) { if (newValue) {
nextTick(() => { nextTick(() => {
fieldBoxRefs.value.forEach((item: any, index: any) => { fieldBoxRefs.value.forEach((item:any, index:any) => {
sortableFn(item, index) sortableFn(item, index)
}) })
}) })
} }
}) })
// item=>index // item=>index
const sortableFn = (item, index) => { const sortableFn = (item, index) => {
const sortable = Sortable.create(item, { const sortable = Sortable.create(item, {
group: { group: {
put: false, // put: false //
}, },
handle: '.handle', handle: '.handle',
animation: 200, animation: 200,
disabled: false, disabled: false,
onEnd: (evt) => { onEnd: (evt) => {
const key = evt.target.getAttribute('data-key') const key = evt.target.getAttribute('data-key')
const data = payConfigData.value[key].pay_type const data = payConfigData.value[key].pay_type
data.splice(evt.newIndex, 0, ...data.splice(evt.oldIndex, 1)) data.splice(evt.newIndex, 0, ...data.splice(evt.oldIndex, 1))
}, }
}) })
} }
// //
const saveFn = () => { const saveFn = () => {
payLoading.value = true payLoading.value = true
const data = cloneDeep(payConfigData.value) const data = cloneDeep(payConfigData.value)
Object.values(data).forEach((item, index) => { Object.values(data).forEach((item, index) => {
item.pay_type.forEach((subItem: any, subIndex: any) => { item.pay_type.forEach((subItem:any, subIndex:any) => {
subItem.sort = subIndex subItem.sort = subIndex
})
}) })
})
setPatConfig({ config: data }).then((res) => { setPatConfig({ config: data }).then(res => {
checkPayConfigList() checkPayConfigList()
isEdit.value = false isEdit.value = false
payLoading.value = false payLoading.value = false
}) })
} }
// //
const cancelFn = () => { const cancelFn = () => {
location.reload() location.reload()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.table-item-border { .table-item-border {
@apply border-b border-[var(--el-border-color)]; @apply border-b border-[var(--el-border-color)];
} }
</style> </style>

329
admin/src/app/views/student_courses/components/student-courses-edit.vue

@ -1,86 +1,69 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateStudentCourses') : t('addStudentCourses')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
v-model="showDialog" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
:title="formData.id ? t('updateStudentCourses') : t('addStudentCourses')" <el-form-item :label="t('studentId')" prop="student_id">
width="50%" <el-select class="input-width" v-model="formData.student_id" clearable :placeholder="t('studentIdPlaceholder')">
class="diy-dialog-wrap" <el-option label="请选择" value=""></el-option>
:destroy-on-close="true" <el-option
> v-for="(item, index) in studentIdList"
<el-form :key="index"
:model="formData" :label="item['name']"
label-width="120px" :value="item['id']"
ref="formRef" />
:rules="formRules" </el-select>
class="page-form" </el-form-item>
v-loading="loading"
> <el-form-item :label="t('courseId')" prop="course_id">
<el-form-item :label="t('studentId')" prop="student_id"> <el-select class="input-width" v-model="formData.course_id" clearable :placeholder="t('courseIdPlaceholder')">
<el-input <el-option label="请选择" value=""></el-option>
v-model="formData.student_id" <el-option
clearable v-for="(item, index) in courseIdList"
:placeholder="t('studentIdPlaceholder')" :key="index"
class="input-width" :label="item['course_name']"
/> :value="item['id']"
</el-form-item> />
</el-select>
<el-form-item :label="t('courseId')" prop="course_id"> </el-form-item>
<el-input
v-model="formData.course_id" <el-form-item :label="t('totalHours')" prop="total_hours">
clearable <el-input v-model="formData.total_hours" clearable :placeholder="t('totalHoursPlaceholder')" class="input-width" />
:placeholder="t('courseIdPlaceholder')" </el-form-item>
class="input-width"
/> <el-form-item :label="t('giftHours')" >
</el-form-item> <el-input v-model="formData.gift_hours" clearable :placeholder="t('giftHoursPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('totalHours')" prop="total_hours">
<el-input <el-form-item :label="t('startDate')" prop="start_date" class="input-width">
v-model="formData.total_hours" <el-date-picker
clearable class="flex-1 !flex"
:placeholder="t('totalHoursPlaceholder')" v-model="formData.start_date"
class="input-width" clearable
/> type="datetime"
</el-form-item> value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="t('startDatePlaceholder')">
<el-form-item :label="t('giftHours')"> </el-date-picker>
<el-input </el-form-item>
v-model="formData.gift_hours" <el-form-item :label="t('endDate')" prop="end_date" class="input-width">
clearable <el-date-picker
:placeholder="t('giftHoursPlaceholder')" class="flex-1 !flex"
class="input-width" v-model="formData.end_date"
/> clearable
</el-form-item> type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
<el-form-item :label="t('startDate')" prop="start_date"> :placeholder="t('endDatePlaceholder')">
<el-input </el-date-picker>
v-model="formData.start_date" </el-form-item>
clearable </el-form>
:placeholder="t('startDatePlaceholder')"
class="input-width" <template #footer>
/> <span class="dialog-footer">
</el-form-item> <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
<el-form-item :label="t('endDate')" prop="end_date"> t('confirm')
<el-input }}</el-button>
v-model="formData.end_date" </span>
clearable </template>
:placeholder="t('endDatePlaceholder')" </el-dialog>
class="input-width"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
</span>
</template>
</el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -88,11 +71,7 @@ import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { import { addStudentCourses, editStudentCourses, getStudentCoursesInfo, getWithStudentList, getWithCourseList } from '@/app/api/student_courses'
addStudentCourses,
editStudentCourses,
getStudentCoursesInfo,
} from '@/app/api/student_courses'
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
@ -101,13 +80,13 @@ const loading = ref(false)
* 表单数据 * 表单数据
*/ */
const initialFormData = { const initialFormData = {
id: '', id: '',
student_id: '', student_id: '',
course_id: '', course_id: '',
total_hours: '', total_hours: '',
gift_hours: '', gift_hours: '',
start_date: '', start_date: '',
end_date: '', end_date: '',
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData: Record<string, any> = reactive({ ...initialFormData })
@ -115,26 +94,38 @@ const formRef = ref<FormInstance>()
// //
const formRules = computed(() => { const formRules = computed(() => {
return { return {
student_id: [ student_id: [
{ required: true, message: t('studentIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('studentIdPlaceholder'), trigger: 'blur' },
],
]
,
course_id: [ course_id: [
{ required: true, message: t('courseIdPlaceholder'), trigger: 'blur' }, { required: true, message: t('courseIdPlaceholder'), trigger: 'blur' },
],
]
,
total_hours: [ total_hours: [
{ required: true, message: t('totalHoursPlaceholder'), trigger: 'blur' }, { required: true, message: t('totalHoursPlaceholder'), trigger: 'blur' },
],
]
,
gift_hours: [ gift_hours: [
{ required: true, message: t('giftHoursPlaceholder'), trigger: 'blur' }, { required: true, message: t('giftHoursPlaceholder'), trigger: 'blur' },
],
]
,
start_date: [ start_date: [
{ required: true, message: t('startDatePlaceholder'), trigger: 'blur' }, { required: true, message: t('startDatePlaceholder'), trigger: 'blur' },
],
]
,
end_date: [ end_date: [
{ required: true, message: t('endDatePlaceholder'), trigger: 'blur' }, { required: true, message: t('endDatePlaceholder'), trigger: 'blur' },
],
} ]
,
}
}) })
const emit = defineEmits(['complete']) const emit = defineEmits(['complete'])
@ -144,93 +135,97 @@ const emit = defineEmits(['complete'])
* @param formEl * @param formEl
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
let save = formData.id ? editStudentCourses : addStudentCourses let save = formData.id ? editStudentCourses : addStudentCourses
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
let data = formData let data = formData
save(data) save(data).then(res => {
.then((res) => { loading.value = false
loading.value = false showDialog.value = false
showDialog.value = false emit('complete')
emit('complete') }).catch(err => {
}) loading.value = false
.catch((err) => { })
loading.value = false }
}) })
}
})
} }
// //
const studentIdList = ref([] as any[])
const setStudentIdList = async () => {
studentIdList.value = await (await getWithStudentList({})).data
}
setStudentIdList()
const courseIdList = ref([] as any[])
const setCourseIdList = async () => {
courseIdList.value = await (await getWithCourseList({})).data
}
setCourseIdList()
const setFormData = async (row: any = null) => { const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if (row) { if(row){
const data = await (await getStudentCoursesInfo(row.id)).data const data = await (await getStudentCoursesInfo(row.id)).data
if (data) if (data) Object.keys(formData).forEach((key: string) => {
Object.keys(formData).forEach((key: string) => { if (data[key] != undefined) formData[key] = data[key]
if (data[key] != undefined) formData[key] = data[key] })
}) }
} loading.value = false
loading.value = false
} }
// //
const mobileVerify = (rule: any, value: any, callback: any) => { const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) { if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile'))) callback(new Error(t('generateMobile')))
} else { } else {
callback() callback()
} }
} }
// //
const idCardVerify = (rule: any, value: any, callback: any) => { const idCardVerify = (rule: any, value: any, callback: any) => {
if ( if (value && !/^[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(value)) {
value && callback(new Error(t('generateIdCard')))
!/^[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( } else {
value callback()
) }
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
} }
// //
const emailVerify = (rule: any, value: any, callback: any) => { const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail'))) callback(new Error(t('generateEmail')))
} else { } else {
callback() callback()
} }
} }
// //
const numberVerify = (rule: any, value: any, callback: any) => { const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) { if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber'))) callback(new Error(t('generateNumber')))
} else { } else {
callback() callback()
} }
} }
defineExpose({ defineExpose({
showDialog, showDialog,
setFormData, setFormData
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>
<style lang="scss"> <style lang="scss">
.diy-dialog-wrap .el-form-item__label { .diy-dialog-wrap .el-form-item__label{
height: auto !important; height: auto !important;
} }
</style> </style>

350
admin/src/app/views/student_courses/student_courses.vue

@ -1,184 +1,106 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addStudentCourses') }} <el-button type="primary" @click="addEvent">
</el-button> {{ t('addStudentCourses') }}
</div> </el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="studentCoursesTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('studentId')" prop="student_id">
:inline="true" <el-select class="w-[280px]" v-model="studentCoursesTable.searchParam.student_id" clearable :placeholder="t('studentIdPlaceholder')">
:model="studentCoursesTable.searchParam" <el-option
ref="searchFormRef" v-for="(item, index) in studentIdList"
> :key="index"
<el-form-item :label="t('studentId')" prop="student_id"> :label="item['name']"
<el-input :value="item['id']"
v-model="studentCoursesTable.searchParam.student_id" />
:placeholder="t('studentIdPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('courseId')" prop="course_id">
<el-input <el-form-item :label="t('courseId')" prop="course_id">
v-model="studentCoursesTable.searchParam.course_id" <el-select class="w-[280px]" v-model="studentCoursesTable.searchParam.course_id" clearable :placeholder="t('courseIdPlaceholder')">
:placeholder="t('courseIdPlaceholder')" <el-option
/> v-for="(item, index) in courseIdList"
</el-form-item> :key="index"
<el-form-item :label="t('totalHours')" prop="total_hours"> :label="item['course_name']"
<el-input :value="item['id']"
v-model="studentCoursesTable.searchParam.total_hours" />
:placeholder="t('totalHoursPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('giftHours')" prop="gift_hours"> <el-form-item>
<el-input <el-button type="primary" @click="loadStudentCoursesList()">{{ t('search') }}</el-button>
v-model="studentCoursesTable.searchParam.gift_hours" <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
:placeholder="t('giftHoursPlaceholder')" </el-form-item>
/> </el-form>
</el-form-item> </el-card>
<el-form-item :label="t('startDate')" prop="start_date">
<el-input <div class="mt-[10px]">
v-model="studentCoursesTable.searchParam.start_date" <el-table :data="studentCoursesTable.data" size="large" v-loading="studentCoursesTable.loading">
:placeholder="t('startDatePlaceholder')" <template #empty>
/> <span>{{ !studentCoursesTable.loading ? t('emptyData') : '' }}</span>
</el-form-item> </template>
<el-form-item :label="t('endDate')" prop="end_date"> <el-table-column prop="student_id_name" :label="t('studentId')" min-width="120" :show-overflow-tooltip="true"/>
<el-input
v-model="studentCoursesTable.searchParam.end_date" <el-table-column prop="course_id_name" :label="t('courseId')" min-width="120" :show-overflow-tooltip="true"/>
:placeholder="t('endDatePlaceholder')"
/> <el-table-column prop="total_hours" :label="t('totalHours')" min-width="120" :show-overflow-tooltip="true"/>
</el-form-item>
<el-table-column prop="gift_hours" :label="t('giftHours')" min-width="120" :show-overflow-tooltip="true"/>
<el-form-item>
<el-button type="primary" @click="loadStudentCoursesList()">{{ <el-table-column prop="start_date" :label="t('startDate')" min-width="120" :show-overflow-tooltip="true"/>
t('search')
}}</el-button> <el-table-column prop="end_date" :label="t('endDate')" min-width="120" :show-overflow-tooltip="true"/>
<el-button @click="resetForm(searchFormRef)">{{
t('reset') <el-table-column :label="t('operation')" fixed="right" min-width="120">
}}</el-button> <template #default="{ row }">
</el-form-item> <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
</el-form> <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</el-card> </template>
</el-table-column>
<div class="mt-[10px]">
<el-table </el-table>
:data="studentCoursesTable.data" <div class="mt-[16px] flex justify-end">
size="large" <el-pagination v-model:current-page="studentCoursesTable.page" v-model:page-size="studentCoursesTable.limit"
v-loading="studentCoursesTable.loading" layout="total, sizes, prev, pager, next, jumper" :total="studentCoursesTable.total"
> @size-change="loadStudentCoursesList()" @current-change="loadStudentCoursesList" />
<template #empty> </div>
<span>{{ </div>
!studentCoursesTable.loading ? t('emptyData') : ''
}}</span> <edit ref="editStudentCoursesDialog" @complete="loadStudentCoursesList" />
</template> </el-card>
<el-table-column </div>
prop="student_id"
:label="t('studentId')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="course_id"
:label="t('courseId')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="total_hours"
:label="t('totalHours')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="gift_hours"
:label="t('giftHours')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="start_date"
:label="t('startDate')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="end_date"
:label="t('endDate')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
>
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="studentCoursesTable.page"
v-model:page-size="studentCoursesTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="studentCoursesTable.total"
@size-change="loadStudentCoursesList()"
@current-change="loadStudentCoursesList"
/>
</div>
</div>
<edit ref="editStudentCoursesDialog" @complete="loadStudentCoursesList" />
</el-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import { t } from '@/lang' import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { import { getStudentCoursesList, deleteStudentCourses, getWithStudentList, getWithCourseList } from '@/app/api/student_courses'
getStudentCoursesList,
deleteStudentCourses,
} from '@/app/api/student_courses'
import { img } from '@/utils/common' import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus' import { ElMessageBox,FormInstance } from 'element-plus'
import Edit from '@/app/views/student_courses/components/student-courses-edit.vue' import Edit from '@/app/views/student_courses/components/student-courses-edit.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
const route = useRoute() const route = useRoute()
const pageName = route.meta.title const pageName = route.meta.title;
let studentCoursesTable = reactive({ let studentCoursesTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
total: 0, total: 0,
loading: true, loading: true,
data: [], data: [],
searchParam: { searchParam:{
student_id: '', "student_id":"",
course_id: '', "course_id":""
total_hours: '', }
gift_hours: '',
start_date: '',
end_date: '',
},
}) })
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
@ -188,25 +110,24 @@ const selectData = ref<any[]>([])
// //
/** /**
* 获取学员课程列表 * 获取学员课程列表
*/ */
const loadStudentCoursesList = (page: number = 1) => { const loadStudentCoursesList = (page: number = 1) => {
studentCoursesTable.loading = true studentCoursesTable.loading = true
studentCoursesTable.page = page studentCoursesTable.page = page
getStudentCoursesList({ getStudentCoursesList({
page: studentCoursesTable.page, page: studentCoursesTable.page,
limit: studentCoursesTable.limit, limit: studentCoursesTable.limit,
...studentCoursesTable.searchParam, ...studentCoursesTable.searchParam
}) }).then(res => {
.then((res) => { studentCoursesTable.loading = false
studentCoursesTable.loading = false studentCoursesTable.data = res.data.data
studentCoursesTable.data = res.data.data studentCoursesTable.total = res.data.total
studentCoursesTable.total = res.data.total }).catch(() => {
}) studentCoursesTable.loading = false
.catch(() => {
studentCoursesTable.loading = false
}) })
} }
loadStudentCoursesList() loadStudentCoursesList()
@ -217,8 +138,8 @@ const editStudentCoursesDialog: Record<string, any> | null = ref(null)
* 添加学员课程 * 添加学员课程
*/ */
const addEvent = () => { const addEvent = () => {
editStudentCoursesDialog.value.setFormData() editStudentCoursesDialog.value.setFormData()
editStudentCoursesDialog.value.showDialog = true editStudentCoursesDialog.value.showDialog = true
} }
/** /**
@ -226,42 +147,55 @@ const addEvent = () => {
* @param data * @param data
*/ */
const editEvent = (data: any) => { const editEvent = (data: any) => {
editStudentCoursesDialog.value.setFormData(data) editStudentCoursesDialog.value.setFormData(data)
editStudentCoursesDialog.value.showDialog = true editStudentCoursesDialog.value.showDialog = true
} }
/** /**
* 删除学员课程 * 删除学员课程
*/ */
const deleteEvent = (id: number) => { const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('studentCoursesDeleteTips'), t('warning'), { ElMessageBox.confirm(t('studentCoursesDeleteTips'), t('warning'),
confirmButtonText: t('confirm'), {
cancelButtonText: t('cancel'), confirmButtonText: t('confirm'),
type: 'warning', cancelButtonText: t('cancel'),
}).then(() => { type: 'warning',
deleteStudentCourses(id) }
.then(() => { ).then(() => {
loadStudentCoursesList() deleteStudentCourses(id).then(() => {
}) loadStudentCoursesList()
.catch(() => {}) }).catch(() => {
}) })
})
} }
const studentIdList = ref([])
const setStudentIdList = async () => {
studentIdList.value = await (await getWithStudentList({})).data
}
setStudentIdList()
const courseIdList = ref([])
const setCourseIdList = async () => {
courseIdList.value = await (await getWithCourseList({})).data
}
setCourseIdList()
const resetForm = (formEl: FormInstance | undefined) => { const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
formEl.resetFields() formEl.resetFields()
loadStudentCoursesList() loadStudentCoursesList()
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 多行超出隐藏 */ /* 多行超出隐藏 */
.multi-hidden { .multi-hidden {
word-break: break-all; word-break: break-all;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }
</style> </style>

269
admin/src/app/views/timetables/timetables.vue

@ -1,125 +1,198 @@
<template> <template>
<div class="schedule-container"> <el-card class="box-card !border-none" shadow="never">
<!-- 周视图日历 --> <el-button @click="addDict">添加</el-button>
<FullCalendar <div class="schedule-container">
:options="calendarOptions" <!-- 周一到周日的布局 -->
ref="fullCalendar" <div v-for="(day, index) in days" :key="index" class="day-column">
class="calendar" <div class="day-header">{{ day.date }}</div>
/> <el-table
:data="day.timeSlots"
<!-- 课程详情弹窗 --> border
<el-dialog :span-method="(data) => objectSpanMethod(day.timeSlots, data)"
v-model="dialogVisible" style="width: 100%"
title="课程人员安排" @cell-click="handleCellClick"
width="30%" >
:before-close="handleClose" <!-- 时间列 -->
> <el-table-column
<div v-if="selectedEvent"> prop="timeRange"
<h3>{{ selectedEvent.title }}</h3> label="时间"
<p>时间{{ selectedEvent.time }}</p> width="80"
<h4>参与人员</h4> align="center"
<ul> >
<li v-for="person in selectedEvent.people" :key="person.id"> <template #default="{ row }">
{{ person.name }} - {{ person.role }} <div :style="{ backgroundColor: row.color }">
</li> {{ row.timeRange }}
</ul> </div>
</template>
</el-table-column>
<!-- 教室列 -->
<el-table-column
v-for="(classroom, idx) in day.classrooms"
:key="idx"
:label="`教室${idx + 1}`"
:prop="`classroom${idx + 1}`"
align="center"
>
<template #default="{ row }">
<div v-if="row.course && row.course.classroom === classroom">
<div class="teacher-name">{{ row.course.teacher }}</div>
<div class="student-list">
<el-tag
v-for="student in row.course.students"
:key="student"
size="small"
effect="plain"
>
{{ student }}
</el-tag>
</div>
<div class="classroom-name">
剩余空位{{ row.course.hasnumber }}
</div>
</div>
</template>
</el-table-column>
</el-table>
</div> </div>
</el-dialog>
</div> <!-- 详情弹窗 -->
<el-dialog v-model="dialogVisible" title="课程详情">
<p><strong>教师:</strong> {{ selectedCourse?.teacher }}</p>
<p><strong>学员:</strong> {{ selectedCourse?.students.join(', ') }}</p>
</el-dialog>
</div>
</el-card>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid' const days = [
import timeGridPlugin from '@fullcalendar/timegrid' //
import interactionPlugin from '@fullcalendar/interaction' //
import zhLocale from '@fullcalendar/core/locales/zh-cn'
//
const courses = ref([
{ {
id: 1, date: '周一',
title: '数学课', timeSlots: [
start: '2025-05-18 08:00:00', {
end: '2025-05-18 09:00:00', timeRange: '9:00-10:00',
people: [ color: '#FFA07A',
{ id: 1, name: '张老师', role: '主讲' }, course: {
{ id: 2, name: '李同学', role: '学生' }, teacher: '',
students: [],
classroom: '教室1',
hasnumber: 5,
},
},
// ...
], ],
classrooms: ['教室1'], //
}, },
{ {
id: 2, date: '周二',
title: '英语课', timeSlots: [
start: '2025-05-18 10:00:00', {
end: '2025-05-18 11:00:00', timeRange: '9:00-10:00',
people: [{ id: 3, name: '王老师', role: '主讲' }], color: '#FFA07A',
course: {
teacher: '张老师',
students: ['小明', '小红'],
classroom: '教室1',
hasnumber: 5,
},
},
{
timeRange: '11:00-12:00',
color: '#712e13',
course: {
teacher: '张老师',
students: ['小明', '小红'],
classroom: '教室2',
hasnumber: 5,
},
},
// ...
],
classrooms: ['教室1', '教室2'], //
}, },
]) ]
//
const dialogVisible = ref(false) const dialogVisible = ref(false)
const selectedEvent = ref(null) const selectedCourse = ref(null)
// FullCalendar //
const calendarOptions = ref({ const objectSpanMethod = (timeSlots, { row, column, rowIndex }) => {
locale: zhLocale, if (column.property === 'timeRange') {
plugins: [ //
dayGridPlugin, const current = timeSlots[rowIndex]
timeGridPlugin,
interactionPlugin, // //
], if (!current || !current.timeRange) return { rowspan: 0, colspan: 0 }
initialView: 'timeGridWeek', //
headerToolbar: { let spanCount = 1
left: 'prev,next today',
center: 'title', //
right: 'timeGridWeek,timeGridDay', // while (timeSlots[rowIndex + spanCount]?.timeRange === current.timeRange) {
}, spanCount++
events: courses.value,
eventClick: (info) => {
selectedEvent.value = {
title: info.event.title,
time: `${info.event.start.toLocaleTimeString()} - ${info.event.end.toLocaleTimeString()}`,
people: info.event.extendedProps.people,
} }
dialogVisible.value = true
},
eventContent: (arg) => {
//
return { html: `<div class="fc-event-title">${arg.event.title}</div>` }
},
allDaySlot: false, //
slotDuration: '01:00', // 1
slotLabelFormat: {
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: false,
meridiem: 'short',
},
})
// //
const handleClose = () => { if (
dialogVisible.value = false spanCount > 1 &&
selectedEvent.value = null rowIndex > 0 &&
timeSlots[rowIndex - 1]?.timeRange === current.timeRange
) {
return { rowspan: 0, colspan: 0 }
}
return { rowspan: spanCount, colspan: 1 }
}
return { rowspan: 1, colspan: 1 }
}
//
const handleCellClick = (row, column, cell, event) => {
console.log(row, column, cell, event)
if (column.property.startsWith('classroom')) {
selectedCourse.value = row.course
dialogVisible.value = true
}
} }
</script> </script>
<style> <style scoped>
.schedule-container { .schedule-container {
padding: 20px; display: flex;
gap: 10px;
}
.day-column {
flex: 1;
min-width: 200px;
}
.day-header {
text-align: center;
font-weight: bold;
padding: 8px;
background-color: #f0f0f0;
}
.teacher-name {
font-weight: bold;
margin-bottom: 5px;
} }
.calendar { .student-list {
margin-bottom: 20px; margin-top: 5px;
} }
.fc-event-title { .el-table__cell {
white-space: normal !important; display: flex;
font-size: 14px; align-items: center;
} }
.fc-timegrid-event { .classroom-name {
cursor: pointer; margin-bottom: 5px;
} }
</style> </style>

212
admin/src/app/views/xsyj/xsyj.vue

@ -0,0 +1,212 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never" v-loading="loading">
<div class="flex justify-between items-center">
<span class="text-lg">{{ pageName }}</span>
<el-button type="primary" @click="addStage">
新增阶段
</el-button>
</div>
</el-card>
<el-card class="box-card !border-none" shadow="never" >
<div class="flex items-center justify-between p-[10px] table-item-border bg">
<span class="text-base w-[230px]">阶段名称</span>
<span class="text-base w-[110px] text-center">底薪</span>
</div>
<el-collapse v-model="activeNames" accordion >
<el-collapse-item v-for="(stage, index) in stages" :key="stage.id" :name="stage.id">
<template #title>
<div class="collapse-title">
<span class="title-name">{{ stage.name }}</span>
<span class="arrow">{{ stage.price }} </span>
<!-- <span class="arrow">&gt;</span> -->
</div>
</template>
<el-form label-width="100px" style="margin-bottom: 10px">
<el-form-item label="阶段名称">
<el-input v-model="stage.name" placeholder="请输入阶段名称" />
</el-form-item>
</el-form>
<el-form label-width="100px" style="margin-bottom: 10px">
<el-form-item label="阶段底薪">
<el-input v-model="stage.price" placeholder="请输入阶段底薪" />
</el-form-item>
</el-form>
<el-button type="success" size="small" @click="addRule(stage)">新增规则</el-button>
<el-table :data="stage.rules" border style="margin-top: 10px">
<el-table-column prop="renewal_standard_min" label="续费上限">
<template #default="{ row }">
<el-input v-model="row.renewal_standard_min" placeholder="请输入续费上限" />
</template>
</el-table-column>
<el-table-column prop="renewal_standard_max" label="续费下限">
<template #default="{ row }">
<el-input v-model="row.renewal_standard_max" placeholder="请输入续费下限" />
</template>
</el-table-column>
<el-table-column prop="renewal_commission" label="续费提成">
<template #default="{ row }">
<el-input v-model="row.renewal_commission" placeholder="%" />
</template>
</el-table-column>
<el-table-column prop="new_count_min" label="新单成交数上限">
<template #default="{ row }">
<el-input v-model="row.new_count_min" />
</template>
</el-table-column>
<el-table-column prop="new_count_max" label="新单成交数下限">
<template #default="{ row }">
<el-input v-model="row.new_count_max" />
</template>
</el-table-column>
<el-table-column prop="new_move_5" label="新招(5+1)x3">
<template #default="{ row }">
<el-input v-model="row.new_move_5" />
</template>
</el-table-column>
<el-table-column prop="new_move_7" label="新招(7+1)x3">
<template #default="{ row }">
<el-input v-model="row.new_move_7" />
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ $index }">
<el-button type="danger" size="small" @click="removeRule(stage, $index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-button type="danger" size="small" style="margin-top: 10px"
@click="removeStage(index)">删除该阶段</el-button>
</el-collapse-item>
</el-collapse>
<div style="text-align: right; margin-top: 20px;">
<el-button type="primary" @click="onSave">提交保存</el-button>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { xsyjConfig, getXsyjConfig } from '@/app/api/sys'
import { reactive, ref } from 'vue'
import {ElMessage} from 'element-plus'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
const loading = ref(true)
const stages = ref([])
const activeNames = ref(null)
function addStage() {
const newStage = {
name: '默认阶段',
price:0,
rules: [{
renewal_standard_min: '',
renewal_standard_max: '',
renewal_commission: '',
new_count_min: '',
new_count_max: '',
new_move_5: '',
new_move_7: ''
}]
}
stages.value.push(newStage)
activeNames.value = newStage.name
}
function removeStage(index) {
stages.value.splice(index, 1)
}
function addRule(stage) {
stage.rules.push({
renewal_standard_min: '',
renewal_standard_max: '',
renewal_commission: '',
new_count_min: '',
new_count_max: '',
new_move_5: '',
new_move_7: ''
})
}
function removeRule(stage, ruleIndex) {
if (stage.rules.length === 1) {
ElMessage.warning('至少保留一条规则')
return
}
stage.rules.splice(ruleIndex, 1)
}
const setFormData = async () => {
const data = await (await getXsyjConfig()).data
stages.value = data
loading.value = false
}
setFormData();
const onSave = async () => {
xsyjConfig(stages.value)
.then(() => {
loading.value = true
setFormData()
})
.catch(() => {
loading.value = false
})
}
</script>
<style lang="scss" scoped>
.collapse-title {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
font-size: 14px;
font-weight: 500;
padding-right: 10px;
color: #333;
padding: 10px;
}
.title-name {
width: 230px;
}
.title-salary {
width: 110px;
text-align: center;
color: #7438D5;
}
.arrow {
margin-left: auto;
color: #999;
font-size: 14px;
}
</style>

35
niucloud/app/adminapi/controller/communication_records/CommunicationRecords.php

@ -28,17 +28,8 @@ class CommunicationRecords extends BaseAdminController
*/ */
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["staff_id",""],
["resource_id",""], ["resource_id",""],
["resource_type",""], ["communication_type",""]
["communication_type",""],
["communication_result",""],
["communication_time",""],
["remarks",""],
["tag",""],
["business_id",""],
["created_at",""],
["updated_at",""]
]); ]);
return success((new CommunicationRecordsService())->getPage($data)); return success((new CommunicationRecordsService())->getPage($data));
} }
@ -58,21 +49,17 @@ class CommunicationRecords extends BaseAdminController
*/ */
public function add(){ public function add(){
$data = $this->request->params([ $data = $this->request->params([
["staff_id",0],
["resource_id",0], ["resource_id",0],
["resource_type",""], ["resource_type",""],
["communication_type",""], ["communication_type",""],
["communication_result",""], ["communication_result",""],
["communication_time","2025-05-16 17:20:44"], ["communication_time","2025-05-19 13:11:39"],
["remarks",""], ["remarks",""],
["tag",""], ["tag",""],
["business_id",0],
["created_at",1747387244],
["updated_at",1747387244]
]); ]);
$this->validate($data, 'app\validate\communication_records\CommunicationRecords.add'); $this->validate($data, 'app\validate\communication_records\CommunicationRecords.add');
$id = (new CommunicationRecordsService())->add($data); return (new CommunicationRecordsService())->add($data);
return success('ADD_SUCCESS', ['id' => $id]);
} }
/** /**
@ -82,21 +69,17 @@ class CommunicationRecords extends BaseAdminController
*/ */
public function edit(int $id){ public function edit(int $id){
$data = $this->request->params([ $data = $this->request->params([
["staff_id",0],
["resource_id",0], ["resource_id",0],
["resource_type",""], ["resource_type",""],
["communication_type",""], ["communication_type",""],
["communication_result",""], ["communication_result",""],
["communication_time","2025-05-16 17:20:44"], ["communication_time","2025-05-19 13:11:39"],
["remarks",""], ["remarks",""],
["tag",""], ["tag",""],
["business_id",0],
["created_at",1747387244],
["updated_at",1747387244]
]); ]);
$this->validate($data, 'app\validate\communication_records\CommunicationRecords.edit'); $this->validate($data, 'app\validate\communication_records\CommunicationRecords.edit');
(new CommunicationRecordsService())->edit($id, $data); return (new CommunicationRecordsService())->edit($id, $data);
return success('EDIT_SUCCESS');
} }
/** /**
@ -110,4 +93,8 @@ class CommunicationRecords extends BaseAdminController
} }
public function getCustomerResourcesAll(){
return success(( new CommunicationRecordsService())->getCustomerResourcesAll());
}
} }

1
niucloud/app/adminapi/controller/course/Course.php

@ -105,5 +105,4 @@ class Course extends BaseAdminController
return success('DELETE_SUCCESS'); return success('DELETE_SUCCESS');
} }
} }

22
niucloud/app/adminapi/controller/student_courses/StudentCourses.php

@ -29,11 +29,7 @@ class StudentCourses extends BaseAdminController
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["student_id",""], ["student_id",""],
["course_id",""], ["course_id",""]
["total_hours",""],
["gift_hours",""],
["start_date",""],
["end_date",""]
]); ]);
return success((new StudentCoursesService())->getPage($data)); return success((new StudentCoursesService())->getPage($data));
} }
@ -57,8 +53,8 @@ class StudentCourses extends BaseAdminController
["course_id",0], ["course_id",0],
["total_hours",0], ["total_hours",0],
["gift_hours",0], ["gift_hours",0],
["start_date","2025-05-16 18:02:13"], ["start_date","2025-05-19 15:41:16"],
["end_date","2025-05-16 18:02:13"], ["end_date","2025-05-19 15:41:16"],
]); ]);
$this->validate($data, 'app\validate\student_courses\StudentCourses.add'); $this->validate($data, 'app\validate\student_courses\StudentCourses.add');
@ -77,8 +73,8 @@ class StudentCourses extends BaseAdminController
["course_id",0], ["course_id",0],
["total_hours",0], ["total_hours",0],
["gift_hours",0], ["gift_hours",0],
["start_date","2025-05-16 18:02:13"], ["start_date","2025-05-19 15:41:16"],
["end_date","2025-05-16 18:02:13"], ["end_date","2025-05-19 15:41:16"],
]); ]);
$this->validate($data, 'app\validate\student_courses\StudentCourses.edit'); $this->validate($data, 'app\validate\student_courses\StudentCourses.edit');
@ -97,4 +93,12 @@ class StudentCourses extends BaseAdminController
} }
public function getStudentAll(){
return success(( new StudentCoursesService())->getStudentAll());
}
public function getCourseAll(){
return success(( new StudentCoursesService())->getCourseAll());
}
} }

14
niucloud/app/adminapi/controller/sys/System.php

@ -124,4 +124,18 @@ class System extends BaseAdminController
} }
public function xsyj_config(){
$data = $this->request->post();
return success(data: (new SystemService())->xsyj_config($data));
}
public function get_xsyj_config(){
return success(data: (new SystemService())->get_xsyj_config());
}
} }

3
niucloud/app/adminapi/route/communication_records.php

@ -14,6 +14,7 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- communication_records // USER_CODE_BEGIN -- communication_records
Route::group('communication_records', function () { Route::group('communication_records', function () {
@ -29,6 +30,8 @@ Route::group('communication_records', function () {
//删除沟通记录 //删除沟通记录
Route::delete('communication_records/:id', 'communication_records.CommunicationRecords/del'); Route::delete('communication_records/:id', 'communication_records.CommunicationRecords/del');
Route::get('customer_resources_all','communication_records.CommunicationRecords/getCustomerResourcesAll');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

6
niucloud/app/adminapi/route/student_courses.php

@ -14,6 +14,8 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- student_courses // USER_CODE_BEGIN -- student_courses
Route::group('student_courses', function () { Route::group('student_courses', function () {
@ -29,6 +31,10 @@ Route::group('student_courses', function () {
//删除学员课程 //删除学员课程
Route::delete('student_courses/:id', 'student_courses.StudentCourses/del'); Route::delete('student_courses/:id', 'student_courses.StudentCourses/del');
Route::get('student_all','student_courses.StudentCourses/getStudentAll');
Route::get('course_all','student_courses.StudentCourses/getCourseAll');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

5
niucloud/app/adminapi/route/sys.php

@ -330,6 +330,11 @@ Route::group('sys', function() {
Route::get('get_yjpz_config', 'sys.System/get_yjpz_config'); Route::get('get_yjpz_config', 'sys.System/get_yjpz_config');
Route::post('yjpz_config', 'sys.System/yjpz_config'); Route::post('yjpz_config', 'sys.System/yjpz_config');
//销售业绩配置
Route::post('xsyj_config', 'sys.System/xsyj_config');
Route::get('get_xsyj_config', 'sys.System/get_xsyj_config');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

126
niucloud/app/model/communication_records/CommunicationRecords.php

@ -16,6 +16,8 @@ use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
use app\model\customer_resources\CustomerResources;
/** /**
* 沟通记录模型 * 沟通记录模型
* Class CommunicationRecords * Class CommunicationRecords
@ -43,31 +45,7 @@ class CommunicationRecords extends BaseModel
/** /**
* 搜索器:沟通记录沟通记录编号 * 搜索器:沟通记录资源
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:沟通记录员工ID
* @param $value
* @param $data
*/
public function searchStaffIdAttr($query, $value, $data)
{
if ($value) {
$query->where("staff_id", $value);
}
}
/**
* 搜索器:沟通记录资源ID
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -79,19 +57,7 @@ class CommunicationRecords extends BaseModel
} }
/** /**
* 搜索器:沟通记录资源类型(如设备、文件、系统等) * 搜索器:沟通记录沟通类型
* @param $value
* @param $data
*/
public function searchResourceTypeAttr($query, $value, $data)
{
if ($value) {
$query->where("resource_type", $value);
}
}
/**
* 搜索器:沟通记录沟通类型: phone-电话, email-邮件, meeting-会议, other-其他
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -102,93 +68,13 @@ class CommunicationRecords extends BaseModel
} }
} }
/**
* 搜索器:沟通记录沟通结果: success-成功, failure-失败, pending-待定
* @param $value
* @param $data
*/
public function searchCommunicationResultAttr($query, $value, $data)
{
if ($value) {
$query->where("communication_result", $value);
}
}
/**
* 搜索器:沟通记录沟通时间
* @param $value
* @param $data
*/
public function searchCommunicationTimeAttr($query, $value, $data)
{
if ($value) {
$query->where("communication_time", $value);
}
}
/**
* 搜索器:沟通记录备注
* @param $value
* @param $data
*/
public function searchRemarksAttr($query, $value, $data)
{
if ($value) {
$query->where("remarks", $value);
}
}
/**
* 搜索器:沟通记录标签: high-高, medium-中, low-低
* @param $value
* @param $data
*/
public function searchTagAttr($query, $value, $data)
{
if ($value) {
$query->where("tag", $value);
}
}
/**
* 搜索器:沟通记录关联的业务ID
* @param $value
* @param $data
*/
public function searchBusinessIdAttr($query, $value, $data)
{
if ($value) {
$query->where("business_id", $value);
}
}
/**
* 搜索器:沟通记录创建时间
* @param $value
* @param $data
*/
public function searchCreatedAtAttr($query, $value, $data)
{
if ($value) {
$query->where("created_at", $value);
}
}
/** public function customerResources(){
* 搜索器:沟通记录修改时间 return $this->hasOne(CustomerResources::class, 'id', 'resource_id')->joinType('left')->withField('name,id')->bind(['resource_id_name'=>'name']);
* @param $value
* @param $data
*/
public function searchUpdatedAtAttr($query, $value, $data)
{
if ($value) {
$query->where("updated_at", $value);
}
} }
} }

116
niucloud/app/model/course/Course.php

@ -9,7 +9,7 @@
// | Author: Niucloud Team // | Author: Niucloud Team
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
namespace app\model\campus; namespace app\model\course;
use core\base\BaseModel; use core\base\BaseModel;
use think\model\concern\SoftDelete; use think\model\concern\SoftDelete;
@ -17,11 +17,11 @@ use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
/** /**
* 校区模型 * 课程模型
* Class Campus * Class Course
* @package app\model\campus * @package app\model\course
*/ */
class Campus extends BaseModel class Course extends BaseModel
{ {
use SoftDelete; use SoftDelete;
@ -36,13 +36,13 @@ class Campus extends BaseModel
* 模型名称 * 模型名称
* @var string * @var string
*/ */
protected $name = 'campus'; protected $name = 'course';
/** /**
* 定义软删除标记字段. * 定义软删除标记字段.
* @var string * @var string
*/ */
protected $deleteTime = 'delete_time'; protected $deleteTime = 'deleted_at';
/** /**
* 定义软删除字段的默认值. * 定义软删除字段的默认值.
@ -51,38 +51,122 @@ class Campus extends BaseModel
protected $defaultSoftDelete = 0; protected $defaultSoftDelete = 0;
/** /**
* 搜索器:校区校区名称 * 搜索器:课程课程编号
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusNameAttr($query, $value, $data) public function searchIdAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("campus_name", "like", "%".$value."%"); $query->where("id", $value);
} }
} }
/** /**
* 搜索器:校区校区地址 * 搜索器:课程课程名称
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusAddressAttr($query, $value, $data) public function searchCourseNameAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("campus_address", $value); $query->where("course_name", $value);
} }
} }
/** /**
* 搜索器:校区校区状态 * 搜索器:课程课程类型
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchCampusStatusAttr($query, $value, $data) public function searchCourseTypeAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("campus_status", $value); $query->where("course_type", $value);
}
}
/**
* 搜索器:课程课程时长
* @param $value
* @param $data
*/
public function searchDurationAttr($query, $value, $data)
{
if ($value) {
$query->where("duration", $value);
}
}
/**
* 搜索器:课程课时数量
* @param $value
* @param $data
*/
public function searchSessionCountAttr($query, $value, $data)
{
if ($value) {
$query->where("session_count", $value);
}
}
/**
* 搜索器:课程单次逍客数量
* @param $value
* @param $data
*/
public function searchSingleSessionCountAttr($query, $value, $data)
{
if ($value) {
$query->where("single_session_count", $value);
}
}
/**
* 搜索器:课程课程价格
* @param $value
* @param $data
*/
public function searchPriceAttr($query, $value, $data)
{
if ($value) {
$query->where("price", $value);
}
}
/**
* 搜索器:课程内部提醒课时
* @param $value
* @param $data
*/
public function searchInternalReminderAttr($query, $value, $data)
{
if ($value) {
$query->where("internal_reminder", $value);
}
}
/**
* 搜索器:课程客户提醒课时
* @param $value
* @param $data
*/
public function searchCustomerReminderAttr($query, $value, $data)
{
if ($value) {
$query->where("customer_reminder", $value);
}
}
/**
* 搜索器:课程课程备注
* @param $value
* @param $data
*/
public function searchRemarksAttr($query, $value, $data)
{
if ($value) {
$query->where("remarks", $value);
} }
} }

55
niucloud/app/model/student/Student.php

@ -0,0 +1,55 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\model\student;
use core\base\BaseModel;
use think\model\concern\SoftDelete;
use think\model\relation\HasMany;
use think\model\relation\HasOne;
class Student extends BaseModel
{
use SoftDelete;
/**
* 数据表主键
* @var string
*/
protected $pk = 'id';
/**
* 模型名称
* @var string
*/
protected $name = 'student';
/**
* 定义软删除标记字段
* @var string
*/
protected $deleteTime = 'deleted_at';
/**
* 定义软删除字段的默认值
* @var int
*/
protected $defaultSoftDelete = 0;
}

72
niucloud/app/model/student_courses/StudentCourses.php

@ -16,6 +16,10 @@ use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
use app\model\student\Student;
use app\model\course\Course;
/** /**
* 学员课程模型 * 学员课程模型
* Class StudentCourses * Class StudentCourses
@ -43,19 +47,7 @@ class StudentCourses extends BaseModel
/** /**
* 搜索器:学员课程记录编号 * 搜索器:学员课程学员
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:学员课程学员ID
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -67,7 +59,7 @@ class StudentCourses extends BaseModel
} }
/** /**
* 搜索器:学员课程课程ID * 搜索器:学员课程课程
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -78,57 +70,17 @@ class StudentCourses extends BaseModel
} }
} }
/**
* 搜索器:学员课程总正式课时数
* @param $value
* @param $data
*/
public function searchTotalHoursAttr($query, $value, $data)
{
if ($value) {
$query->where("total_hours", $value);
}
}
/**
* 搜索器:学员课程赠送课时数
* @param $value
* @param $data
*/
public function searchGiftHoursAttr($query, $value, $data)
{
if ($value) {
$query->where("gift_hours", $value);
}
}
/**
* 搜索器:学员课程课程开始日期
* @param $value
* @param $data
*/
public function searchStartDateAttr($query, $value, $data)
{
if ($value) {
$query->where("start_date", $value);
}
}
/**
* 搜索器:学员课程课程结束日期
* @param $value
* @param $data
*/
public function searchEndDateAttr($query, $value, $data)
{
if ($value) {
$query->where("end_date", $value);
}
}
public function student(){
return $this->hasOne(Student::class, 'id', 'student_id')->joinType('left')->withField('name,id')->bind(['student_id_name'=>'name']);
}
public function course(){
return $this->hasOne(Course::class, 'id', 'course_id')->joinType('left')->withField('course_name,id')->bind(['course_id_name'=>'course_name']);
}
} }

27
niucloud/app/service/admin/communication_records/CommunicationRecordsService.php

@ -12,7 +12,9 @@
namespace app\service\admin\communication_records; namespace app\service\admin\communication_records;
use app\model\communication_records\CommunicationRecords; use app\model\communication_records\CommunicationRecords;
use app\model\customer_resources\CustomerResources;
use app\model\personnel\Personnel;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -39,7 +41,7 @@ class CommunicationRecordsService extends BaseAdminService
$field = 'id,staff_id,resource_id,resource_type,communication_type,communication_result,communication_time,remarks,tag,business_id,created_at,updated_at'; $field = 'id,staff_id,resource_id,resource_type,communication_type,communication_result,communication_time,remarks,tag,business_id,created_at,updated_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","staff_id","resource_id","resource_type","communication_type","communication_result","communication_time","remarks","tag","business_id","created_at","updated_at"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["resource_id","communication_type"], $where)->with(['customerResources'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -53,7 +55,7 @@ class CommunicationRecordsService extends BaseAdminService
{ {
$field = 'id,staff_id,resource_id,resource_type,communication_type,communication_result,communication_time,remarks,tag,business_id,created_at,updated_at'; $field = 'id,staff_id,resource_id,resource_type,communication_type,communication_result,communication_time,remarks,tag,business_id,created_at,updated_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['customerResources'])->findOrEmpty()->toArray();
return $info; return $info;
} }
@ -64,8 +66,15 @@ class CommunicationRecordsService extends BaseAdminService
*/ */
public function add(array $data) public function add(array $data)
{ {
$personnel = new Personnel();
$data['staff_id'] = $personnel->where(['sys_user_id' => $this->uid])->value("id");
if(!$data['staff_id']){
return fail("操作失败");
}
$res = $this->model->create($data); $res = $this->model->create($data);
return $res->id; return success("操作成功");
} }
@ -77,9 +86,14 @@ class CommunicationRecordsService extends BaseAdminService
*/ */
public function edit(int $id, array $data) public function edit(int $id, array $data)
{ {
$personnel = new Personnel();
$data['staff_id'] = $personnel->where(['sys_user_id' => $this->uid])->value("id");
if(!$data['staff_id']){
return fail("操作失败");
}
$this->model->where([['id', '=', $id]])->update($data); $this->model->where([['id', '=', $id]])->update($data);
return true; return success("操作成功");
} }
/** /**
@ -95,5 +109,10 @@ class CommunicationRecordsService extends BaseAdminService
} }
public function getCustomerResourcesAll(){
$customerResourcesModel = new CustomerResources();
return $customerResourcesModel->select()->toArray();
}
} }

45
niucloud/app/service/admin/student_courses/StudentCoursesService.php

@ -9,57 +9,58 @@
// | Author: Niucloud Team // | Author: Niucloud Team
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
namespace app\service\admin\campus; namespace app\service\admin\student_courses;
use app\model\campus\Campus; use app\model\student_courses\StudentCourses;
use app\model\student\Student;
use app\model\course\Course;
use core\base\BaseAdminService; use core\base\BaseAdminService;
/** /**
* 校区服务层 * 学员课程服务层
* Class CampusService * Class StudentCoursesService
* @package app\service\admin\campus * @package app\service\admin\student_courses
*/ */
class StudentCoursesService extends BaseAdminService class StudentCoursesService extends BaseAdminService
{ {
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
$this->model = new Campus(); $this->model = new StudentCourses();
} }
/** /**
* 获取校区列表 * 获取学员课程列表
* @param array $where * @param array $where
* @return array * @return array
*/ */
public function getPage(array $where = []) public function getPage(array $where = [])
{ {
$field = 'id,campus_name,campus_address,campus_preview_image,campus_coordinates,campus_introduction,campus_status,create_time,update_time,delete_time'; $field = 'id,student_id,course_id,total_hours,gift_hours,start_date,end_date,created_at,updated_at';
$order = ''; $order = 'id desc';
$search_model = $this->model->withSearch(["campus_name","campus_address","campus_status"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["student_id","course_id"], $where)->with(['student','course'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
/** /**
* 获取校区信息 * 获取学员课程信息
* @param int $id * @param int $id
* @return array * @return array
*/ */
public function getInfo(int $id) public function getInfo(int $id)
{ {
$field = 'id,campus_name,campus_address,campus_preview_image,campus_coordinates,campus_introduction,campus_status,create_time,update_time,delete_time'; $field = 'id,student_id,course_id,total_hours,gift_hours,start_date,end_date,created_at,updated_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['student','course'])->findOrEmpty()->toArray();
$info['campus_status'] = strval($info['campus_status']);
return $info; return $info;
} }
/** /**
* 添加校区 * 添加学员课程
* @param array $data * @param array $data
* @return mixed * @return mixed
*/ */
@ -71,7 +72,7 @@ class StudentCoursesService extends BaseAdminService
} }
/** /**
* 校区编辑 * 学员课程编辑
* @param int $id * @param int $id
* @param array $data * @param array $data
* @return bool * @return bool
@ -84,7 +85,7 @@ class StudentCoursesService extends BaseAdminService
} }
/** /**
* 删除校区 * 删除学员课程
* @param int $id * @param int $id
* @return bool * @return bool
*/ */
@ -96,5 +97,15 @@ class StudentCoursesService extends BaseAdminService
} }
public function getStudentAll(){
$studentModel = new Student();
return $studentModel->select()->toArray();
}
public function getCourseAll(){
$courseModel = new Course();
return $courseModel->select()->toArray();
}
} }

20
niucloud/app/service/admin/sys/SystemService.php

@ -188,4 +188,24 @@ class SystemService extends BaseAdminService
return true; return true;
} }
public function xsyj_config(array $data){
$config = new SysConfig();
$config->where(['config_key' => 'XSYJ'])->update([
'value' => json_encode($data)
]);
return true;
}
public function get_xsyj_config(){
$config = new SysConfig();
$data = $config->where(['config_key' => 'XSYJ'])->value("value");
return $data;
}
} }

6
niucloud/app/validate/communication_records/CommunicationRecords.php

@ -20,7 +20,6 @@ class CommunicationRecords extends BaseValidate
{ {
protected $rule = [ protected $rule = [
'staff_id' => 'require',
'resource_id' => 'require', 'resource_id' => 'require',
'resource_type' => 'require', 'resource_type' => 'require',
'communication_type' => 'require', 'communication_type' => 'require',
@ -29,7 +28,6 @@ class CommunicationRecords extends BaseValidate
]; ];
protected $message = [ protected $message = [
'staff_id.require' => ['common_validate.require', ['staff_id']],
'resource_id.require' => ['common_validate.require', ['resource_id']], 'resource_id.require' => ['common_validate.require', ['resource_id']],
'resource_type.require' => ['common_validate.require', ['resource_type']], 'resource_type.require' => ['common_validate.require', ['resource_type']],
'communication_type.require' => ['common_validate.require', ['communication_type']], 'communication_type.require' => ['common_validate.require', ['communication_type']],
@ -38,8 +36,8 @@ class CommunicationRecords extends BaseValidate
]; ];
protected $scene = [ protected $scene = [
"add" => ['staff_id', 'resource_id', 'resource_type', 'communication_type', 'communication_result', 'communication_time', 'remarks', 'tag', 'business_id', 'created_at', 'updated_at'], "add" => ['resource_id', 'resource_type', 'communication_type', 'communication_result', 'communication_time', 'remarks', 'tag'],
"edit" => ['staff_id', 'resource_id', 'resource_type', 'communication_type', 'communication_result', 'communication_time', 'remarks', 'tag', 'business_id', 'created_at', 'updated_at'] "edit" => ['resource_id', 'resource_type', 'communication_type', 'communication_result', 'communication_time', 'remarks', 'tag']
]; ];
} }

Loading…
Cancel
Save