Browse Source

本地临时保存

master
王泽彦 11 months ago
parent
commit
0444b2f3b7
  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. 50
      admin/src/app/lang/zh-cn/communication_records.communication_records.json
  6. 38
      admin/src/app/lang/zh-cn/student_courses.student_courses.json
  7. 69
      admin/src/app/views/campus_person_role/campus_person_role.vue
  8. 582
      admin/src/app/views/communication_records/communication_records.vue
  9. 584
      admin/src/app/views/communication_records/components/communication-records-edit.vue
  10. 331
      admin/src/app/views/setting/pay.vue
  11. 467
      admin/src/app/views/student_courses/components/student-courses-edit.vue
  12. 468
      admin/src/app/views/student_courses/student_courses.vue
  13. 212
      admin/src/app/views/xsyj/xsyj.vue
  14. 37
      niucloud/app/adminapi/controller/communication_records/CommunicationRecords.php
  15. 22
      niucloud/app/adminapi/controller/student_courses/StudentCourses.php
  16. 14
      niucloud/app/adminapi/controller/sys/System.php
  17. 3
      niucloud/app/adminapi/route/communication_records.php
  18. 6
      niucloud/app/adminapi/route/student_courses.php
  19. 5
      niucloud/app/adminapi/route/sys.php
  20. 130
      niucloud/app/model/communication_records/CommunicationRecords.php
  21. 20
      niucloud/app/model/course/Course.php
  22. 55
      niucloud/app/model/student/Student.php
  23. 74
      niucloud/app/model/student_courses/StudentCourses.php
  24. 29
      niucloud/app/service/admin/communication_records/CommunicationRecordsService.php
  25. 45
      niucloud/app/service/admin/student_courses/StudentCoursesService.php
  26. 20
      niucloud/app/service/admin/sys/SystemService.php
  27. 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')
}

50
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": "请选择结束时间"
}

38
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
} }

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

@ -1,351 +1,231 @@
<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> </template>
<el-button @click="resetForm(searchFormRef)">{{
t('reset') <script lang="ts" setup>
}}</el-button> import { reactive, ref, watch } from 'vue'
</el-form-item> import { t } from '@/lang'
</el-form> import { useDictionary } from '@/app/api/dict'
</el-card> import { getCommunicationRecordsList, deleteCommunicationRecords, getWithCustomerResourcesList } from '@/app/api/communication_records'
import { img } from '@/utils/common'
<div class="mt-[10px]"> import { ElMessageBox,FormInstance } from 'element-plus'
<el-table import Edit from '@/app/views/communication_records/components/communication-records-edit.vue'
:data="communicationRecordsTable.data" import { useRoute } from 'vue-router'
size="large" const route = useRoute()
v-loading="communicationRecordsTable.loading" const pageName = route.meta.title;
>
<template #empty> let communicationRecordsTable = reactive({
<span>{{ page: 1,
!communicationRecordsTable.loading ? t('emptyData') : '' limit: 10,
}}</span> total: 0,
</template> loading: true,
<el-table-column data: [],
prop="staff_id" searchParam:{
:label="t('staffId')" "resource_id":"",
min-width="120" "communication_type":""
:show-overflow-tooltip="true" }
/> })
<el-table-column const searchFormRef = ref<FormInstance>()
prop="resource_id"
:label="t('resourceId')" //
min-width="120" const selectData = ref<any[]>([])
:show-overflow-tooltip="true"
/> //
const communication_typeList = ref([] as any[])
<el-table-column const communication_typeDictList = async () => {
prop="resource_type" communication_typeList.value = await (await useDictionary('communication_type')).data.dictionary
:label="t('resourceType')" }
min-width="120" communication_typeDictList();
:show-overflow-tooltip="true" const communication_resultList = ref([] as any[])
/> const communication_resultDictList = async () => {
communication_resultList.value = await (await useDictionary('communication_result')).data.dictionary
<el-table-column }
prop="communication_type" communication_resultDictList();
:label="t('communicationType')" const tagList = ref([] as any[])
min-width="120" const tagDictList = async () => {
:show-overflow-tooltip="true" tagList.value = await (await useDictionary('tag')).data.dictionary
/> }
tagDictList();
<el-table-column
prop="communication_result" /**
:label="t('communicationResult')" * 获取沟通记录列表
min-width="120" */
:show-overflow-tooltip="true" const loadCommunicationRecordsList = (page: number = 1) => {
/> communicationRecordsTable.loading = true
communicationRecordsTable.page = page
<el-table-column
prop="communication_time" getCommunicationRecordsList({
:label="t('communicationTime')" page: communicationRecordsTable.page,
min-width="120" limit: communicationRecordsTable.limit,
:show-overflow-tooltip="true" ...communicationRecordsTable.searchParam
/> }).then(res => {
communicationRecordsTable.loading = false
<el-table-column communicationRecordsTable.data = res.data.data
prop="remarks" communicationRecordsTable.total = res.data.total
:label="t('remarks')" }).catch(() => {
min-width="120" communicationRecordsTable.loading = false
:show-overflow-tooltip="true" })
/> }
loadCommunicationRecordsList()
<el-table-column
prop="tag" const editCommunicationRecordsDialog: Record<string, any> | null = ref(null)
:label="t('tag')"
min-width="120" /**
:show-overflow-tooltip="true" * 添加沟通记录
/> */
const addEvent = () => {
<el-table-column editCommunicationRecordsDialog.value.setFormData()
prop="business_id" editCommunicationRecordsDialog.value.showDialog = true
:label="t('businessId')" }
min-width="120"
:show-overflow-tooltip="true" /**
/> * 编辑沟通记录
* @param data
<el-table-column */
prop="created_at" const editEvent = (data: any) => {
:label="t('createdAt')" editCommunicationRecordsDialog.value.setFormData(data)
min-width="120" editCommunicationRecordsDialog.value.showDialog = true
:show-overflow-tooltip="true" }
/>
/**
<el-table-column * 删除沟通记录
prop="updated_at" */
:label="t('updatedAt')" const deleteEvent = (id: number) => {
min-width="120" ElMessageBox.confirm(t('communicationRecordsDeleteTips'), t('warning'),
:show-overflow-tooltip="true" {
/> confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
<el-table-column type: 'warning',
:label="t('operation')" }
fixed="right" ).then(() => {
min-width="120" deleteCommunicationRecords(id).then(() => {
> loadCommunicationRecordsList()
<template #default="{ row }"> }).catch(() => {
<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> const resourceIdList = ref([])
</template> const setResourceIdList = async () => {
</el-table-column> resourceIdList.value = await (await getWithCustomerResourcesList({})).data
</el-table> }
<div class="mt-[16px] flex justify-end"> setResourceIdList()
<el-pagination
v-model:current-page="communicationRecordsTable.page" const resetForm = (formEl: FormInstance | undefined) => {
v-model:page-size="communicationRecordsTable.limit" if (!formEl) return
layout="total, sizes, prev, pager, next, jumper" formEl.resetFields()
:total="communicationRecordsTable.total" loadCommunicationRecordsList()
@size-change="loadCommunicationRecordsList()" }
@current-change="loadCommunicationRecordsList" </script>
/>
</div> <style lang="scss" scoped>
</div> /* 多行超出隐藏 */
.multi-hidden {
<edit word-break: break-all;
ref="editCommunicationRecordsDialog" text-overflow: ellipsis;
@complete="loadCommunicationRecordsList" overflow: hidden;
/> display: -webkit-box;
</el-card> -webkit-line-clamp: 2;
</div> -webkit-box-orient: vertical;
</template> }
</style>
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
import {
getCommunicationRecordsList,
deleteCommunicationRecords,
} from '@/app/api/communication_records'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import Edit from '@/app/views/communication_records/components/communication-records-edit.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
let communicationRecordsTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
staff_id: '',
resource_id: '',
resource_type: '',
communication_type: '',
communication_result: '',
communication_time: '',
remarks: '',
tag: '',
business_id: '',
created_at: '',
updated_at: '',
},
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
/**
* 获取沟通记录列表
*/
const loadCommunicationRecordsList = (page: number = 1) => {
communicationRecordsTable.loading = true
communicationRecordsTable.page = page
getCommunicationRecordsList({
page: communicationRecordsTable.page,
limit: communicationRecordsTable.limit,
...communicationRecordsTable.searchParam,
})
.then((res) => {
communicationRecordsTable.loading = false
communicationRecordsTable.data = res.data.data
communicationRecordsTable.total = res.data.total
})
.catch(() => {
communicationRecordsTable.loading = false
})
}
loadCommunicationRecordsList()
const editCommunicationRecordsDialog: Record<string, any> | null = ref(null)
/**
* 添加沟通记录
*/
const addEvent = () => {
editCommunicationRecordsDialog.value.setFormData()
editCommunicationRecordsDialog.value.showDialog = true
}
/**
* 编辑沟通记录
* @param data
*/
const editEvent = (data: any) => {
editCommunicationRecordsDialog.value.setFormData(data)
editCommunicationRecordsDialog.value.showDialog = true
}
/**
* 删除沟通记录
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('communicationRecordsDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteCommunicationRecords(id)
.then(() => {
loadCommunicationRecordsList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadCommunicationRecordsList()
}
</script>
<style lang="scss" scoped>
/* 多行超出隐藏 */
.multi-hidden {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>

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

@ -1,322 +1,262 @@
<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" </template>
clearable
:placeholder="t('remarksPlaceholder')" <script lang="ts" setup>
class="input-width" import { ref, reactive, computed, watch } from 'vue'
/> import { useDictionary } from '@/app/api/dict'
</el-form-item> import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
<el-form-item :label="t('tag')"> import { addCommunicationRecords, editCommunicationRecords, getCommunicationRecordsInfo, getWithCustomerResourcesList } from '@/app/api/communication_records'
<el-input
v-model="formData.tag" let showDialog = ref(false)
clearable const loading = ref(false)
:placeholder="t('tagPlaceholder')"
class="input-width" /**
/> * 表单数据
</el-form-item> */
const initialFormData = {
<el-form-item :label="t('businessId')"> id: '',
<el-input resource_id: '',
v-model="formData.business_id" resource_type: '',
clearable communication_type: '',
:placeholder="t('businessIdPlaceholder')" communication_result: '',
class="input-width" communication_time: '',
/> remarks: '',
</el-form-item> tag: '',
}
<el-form-item :label="t('createdAt')"> const formData: Record<string, any> = reactive({ ...initialFormData })
<el-input
v-model="formData.created_at" const formRef = ref<FormInstance>()
clearable
:placeholder="t('createdAtPlaceholder')" //
class="input-width" const formRules = computed(() => {
/> return {
</el-form-item> resource_id: [
{ required: true, message: t('resourceIdPlaceholder'), trigger: 'blur' },
<el-form-item :label="t('updatedAt')">
<el-input ]
v-model="formData.updated_at" ,
clearable resource_type: [
:placeholder="t('updatedAtPlaceholder')" { required: true, message: t('resourceTypePlaceholder'), trigger: 'blur' },
class="input-width"
/> ]
</el-form-item> ,
</el-form> communication_type: [
{ required: true, message: t('communicationTypePlaceholder'), trigger: 'blur' },
<template #footer>
<span class="dialog-footer"> ]
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button> ,
<el-button communication_result: [
type="primary" { required: true, message: t('communicationResultPlaceholder'), trigger: 'blur' },
:loading="loading"
@click="confirm(formRef)" ]
>{{ t('confirm') }}</el-button ,
> communication_time: [
</span> { required: true, message: t('communicationTimePlaceholder'), trigger: 'blur' },
</template>
</el-dialog> ]
</template> ,
remarks: [
<script lang="ts" setup> { required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict' ]
import { t } from '@/lang' ,
import type { FormInstance } from 'element-plus' tag: [
import { { required: true, message: t('tagPlaceholder'), trigger: 'blur' },
addCommunicationRecords,
editCommunicationRecords, ]
getCommunicationRecordsInfo, ,
} from '@/app/api/communication_records' }
})
let showDialog = ref(false)
const loading = ref(false) const emit = defineEmits(['complete'])
/** /**
* 表单数据 * 确认
*/ * @param formEl
const initialFormData = { */
id: '', const confirm = async (formEl: FormInstance | undefined) => {
staff_id: '', if (loading.value || !formEl) return
resource_id: '', let save = formData.id ? editCommunicationRecords : addCommunicationRecords
resource_type: '',
communication_type: '', await formEl.validate(async (valid) => {
communication_result: '', if (valid) {
communication_time: '', loading.value = true
remarks: '',
tag: '', let data = formData
business_id: '',
created_at: '', save(data).then(res => {
updated_at: '', loading.value = false
} showDialog.value = false
const formData: Record<string, any> = reactive({ ...initialFormData }) emit('complete')
}).catch(err => {
const formRef = ref<FormInstance>() loading.value = false
})
// }
const formRules = computed(() => { })
return { }
staff_id: [
{ required: true, message: t('staffIdPlaceholder'), trigger: 'blur' }, //
], let communication_typeList = ref([])
resource_id: [ const communication_typeDictList = async () => {
{ required: true, message: t('resourceIdPlaceholder'), trigger: 'blur' }, communication_typeList.value = await (await useDictionary('communication_type')).data.dictionary
], }
resource_type: [ communication_typeDictList();
{ watch(() => communication_typeList.value, () => { formData.communication_type = communication_typeList.value[0].value })
required: true, let communication_resultList = ref([])
message: t('resourceTypePlaceholder'), const communication_resultDictList = async () => {
trigger: 'blur', communication_resultList.value = await (await useDictionary('communication_result')).data.dictionary
}, }
], communication_resultDictList();
communication_type: [ watch(() => communication_resultList.value, () => { formData.communication_result = communication_resultList.value[0].value })
{ let tagList = ref([])
required: true, const tagDictList = async () => {
message: t('communicationTypePlaceholder'), tagList.value = await (await useDictionary('tag')).data.dictionary
trigger: 'blur', }
}, tagDictList();
], watch(() => tagList.value, () => { formData.tag = tagList.value[0].value })
communication_result: [
{
required: true, const resourceIdList = ref([] as any[])
message: t('communicationResultPlaceholder'), const setResourceIdList = async () => {
trigger: 'blur', resourceIdList.value = await (await getWithCustomerResourcesList({})).data
}, }
], setResourceIdList()
communication_time: [ const setFormData = async (row: any = null) => {
{ Object.assign(formData, initialFormData)
required: true, loading.value = true
message: t('communicationTimePlaceholder'), if(row){
trigger: 'blur', const data = await (await getCommunicationRecordsInfo(row.id)).data
}, if (data) Object.keys(formData).forEach((key: string) => {
], if (data[key] != undefined) formData[key] = data[key]
remarks: [ })
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' }, }
], loading.value = false
tag: [{ required: true, message: t('tagPlaceholder'), trigger: 'blur' }], }
business_id: [
{ required: true, message: t('businessIdPlaceholder'), trigger: 'blur' }, //
], const mobileVerify = (rule: any, value: any, callback: any) => {
created_at: [ if (value && !/^1[3-9]\d{9}$/.test(value)) {
{ required: true, message: t('createdAtPlaceholder'), trigger: 'blur' }, callback(new Error(t('generateMobile')))
], } else {
updated_at: [ callback()
{ required: true, message: t('updatedAtPlaceholder'), trigger: 'blur' }, }
], }
}
}) //
const idCardVerify = (rule: any, value: any, callback: any) => {
const emit = defineEmits(['complete']) 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)) {
callback(new Error(t('generateIdCard')))
/** } else {
* 确认 callback()
* @param formEl }
*/ }
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return //
let save = formData.id ? editCommunicationRecords : addCommunicationRecords const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
await formEl.validate(async (valid) => { callback(new Error(t('generateEmail')))
if (valid) { } else {
loading.value = true callback()
}
let data = formData }
save(data) //
.then((res) => { const numberVerify = (rule: any, value: any, callback: any) => {
loading.value = false if (!Number.isInteger(value)) {
showDialog.value = false callback(new Error(t('generateNumber')))
emit('complete') } else {
}) callback()
.catch((err) => { }
loading.value = false }
})
} defineExpose({
}) showDialog,
} setFormData
})
// </script>
const setFormData = async (row: any = null) => { <style lang="scss" scoped></style>
Object.assign(formData, initialFormData) <style lang="scss">
loading.value = true .diy-dialog-wrap .el-form-item__label{
if (row) { height: auto !important;
const data = await (await getCommunicationRecordsInfo(row.id)).data }
if (data) </style>
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
}
loading.value = false
}
//
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
} else {
callback()
}
}
//
const idCardVerify = (rule: any, value: any, callback: any) => {
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
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

331
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>
<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>
<div
class="flex items-center justify-between p-[10px] table-item-border bg" <div>
> <div 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-[230px]">{{ t('payType') }}</span>
<span class="text-base w-[110px] text-center">{{ <span class="text-base w-[110px] text-center">{{ t('onState') }}</span>
t('onState') <span class="text-base w-[80px] text-center" v-if="isEdit">{{ t('templateName') }}</span>
}}</span> </div>
<span class="text-base w-[80px] text-center" v-if="isEdit">{{
t('templateName')
}}</span>
</div>
<div ref="fieldBoxRefs" :data-key="payKey"> <div ref="fieldBoxRefs" :data-key="payKey">
<div <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">
class="flex items-center justify-between p-[10px] table-item-border" <div class="flex w-[230px] flex-shrink-0">
v-for="(childrenItem, childrenIndex) in payItems.pay_type" <span v-if="isEdit" class="iconfont icontuodong mr-2 handle cursor-pointer"></span>
:key="childrenItem.redio_key" <div class="flex items-center select-none">
:id="payKey + '_' + childrenIndex" <div class="mr-[15px] w-[30px] h-[30px] flex-shrink-0">
> <img class="w-[30px]" :src="img(childrenItem.icon)" />
<div class="flex w-[230px] flex-shrink-0"> </div>
<span <span class="text-base text-[#666]">{{ childrenItem.name }}</span>
v-if="isEdit" </div>
class="iconfont icontuodong mr-2 handle cursor-pointer" </div>
></span> <div class="flex items-center justify-center w-[110px] select-none">
<div class="flex items-center 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 class="mr-[15px] w-[30px] h-[30px] flex-shrink-0"> <div v-else>
<img class="w-[30px]" :src="img(childrenItem.icon)" /> <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>
<div class="flex items-center justify-center w-[110px] select-none"> </el-card>
<el-switch
v-if="isEdit" <div class="fixed-footer-wrap" v-if="isEdit">
v-model="childrenItem.status" <div class="fixed-footer">
:active-value="1" <el-button type="primary" :loading="loading" @click="cancelFn">{{ t('cancel') }}</el-button>
:inactive-value="0" <el-button type="primary" :loading="loading" @click="saveFn(formRef)">{{ t('save') }}</el-button>
: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> </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>

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

@ -1,236 +1,231 @@
<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" </template>
/>
</el-form-item> <script lang="ts" setup>
</el-form> import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict'
<template #footer> import { t } from '@/lang'
<span class="dialog-footer"> import type { FormInstance } from 'element-plus'
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button> import { addStudentCourses, editStudentCourses, getStudentCoursesInfo, getWithStudentList, getWithCourseList } from '@/app/api/student_courses'
<el-button
type="primary" let showDialog = ref(false)
:loading="loading" const loading = ref(false)
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button /**
> * 表单数据
</span> */
</template> const initialFormData = {
</el-dialog> id: '',
</template> student_id: '',
course_id: '',
<script lang="ts" setup> total_hours: '',
import { ref, reactive, computed, watch } from 'vue' gift_hours: '',
import { useDictionary } from '@/app/api/dict' start_date: '',
import { t } from '@/lang' end_date: '',
import type { FormInstance } from 'element-plus' }
import { const formData: Record<string, any> = reactive({ ...initialFormData })
addStudentCourses,
editStudentCourses, const formRef = ref<FormInstance>()
getStudentCoursesInfo,
} from '@/app/api/student_courses' //
const formRules = computed(() => {
let showDialog = ref(false) return {
const loading = ref(false) student_id: [
{ required: true, message: t('studentIdPlaceholder'), trigger: 'blur' },
/**
* 表单数据 ]
*/ ,
const initialFormData = { course_id: [
id: '', { required: true, message: t('courseIdPlaceholder'), trigger: 'blur' },
student_id: '',
course_id: '', ]
total_hours: '', ,
gift_hours: '', total_hours: [
start_date: '', { required: true, message: t('totalHoursPlaceholder'), trigger: 'blur' },
end_date: '',
} ]
const formData: Record<string, any> = reactive({ ...initialFormData }) ,
gift_hours: [
const formRef = ref<FormInstance>() { required: true, message: t('giftHoursPlaceholder'), trigger: 'blur' },
// ]
const formRules = computed(() => { ,
return { start_date: [
student_id: [ { required: true, message: t('startDatePlaceholder'), trigger: 'blur' },
{ required: true, message: t('studentIdPlaceholder'), trigger: 'blur' },
], ]
course_id: [ ,
{ required: true, message: t('courseIdPlaceholder'), trigger: 'blur' }, end_date: [
], { required: true, message: t('endDatePlaceholder'), trigger: 'blur' },
total_hours: [
{ required: true, message: t('totalHoursPlaceholder'), trigger: 'blur' }, ]
], ,
gift_hours: [ }
{ required: true, message: t('giftHoursPlaceholder'), trigger: 'blur' }, })
],
start_date: [ const emit = defineEmits(['complete'])
{ required: true, message: t('startDatePlaceholder'), trigger: 'blur' },
], /**
end_date: [ * 确认
{ required: true, message: t('endDatePlaceholder'), trigger: 'blur' }, * @param formEl
], */
} const confirm = async (formEl: FormInstance | undefined) => {
}) if (loading.value || !formEl) return
let save = formData.id ? editStudentCourses : addStudentCourses
const emit = defineEmits(['complete'])
await formEl.validate(async (valid) => {
/** if (valid) {
* 确认 loading.value = true
* @param formEl
*/ let data = formData
const confirm = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return save(data).then(res => {
let save = formData.id ? editStudentCourses : addStudentCourses loading.value = false
showDialog.value = false
await formEl.validate(async (valid) => { emit('complete')
if (valid) { }).catch(err => {
loading.value = true loading.value = false
})
let data = formData }
})
save(data) }
.then((res) => {
loading.value = false //
showDialog.value = false
emit('complete')
})
.catch((err) => { const studentIdList = ref([] as any[])
loading.value = false const setStudentIdList = async () => {
}) studentIdList.value = await (await getWithStudentList({})).data
} }
}) setStudentIdList()
} const courseIdList = ref([] as any[])
const setCourseIdList = async () => {
// courseIdList.value = await (await getWithCourseList({})).data
}
const setFormData = async (row: any = null) => { setCourseIdList()
Object.assign(formData, initialFormData) const setFormData = async (row: any = null) => {
loading.value = true Object.assign(formData, initialFormData)
if (row) { loading.value = true
const data = await (await getStudentCoursesInfo(row.id)).data if(row){
if (data) const data = await (await getStudentCoursesInfo(row.id)).data
Object.keys(formData).forEach((key: string) => { if (data) 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) => {
} if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
} callback(new Error(t('generateEmail')))
} else {
// callback()
const emailVerify = (rule: any, value: any, callback: any) => { }
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { }
callback(new Error(t('generateEmail')))
} else { //
callback() const numberVerify = (rule: any, value: any, callback: any) => {
} if (!Number.isInteger(value)) {
} callback(new Error(t('generateNumber')))
} else {
// callback()
const numberVerify = (rule: any, value: any, callback: any) => { }
if (!Number.isInteger(value)) { }
callback(new Error(t('generateNumber')))
} else { defineExpose({
callback() showDialog,
} setFormData
} })
</script>
defineExpose({
showDialog, <style lang="scss" scoped></style>
setFormData, <style lang="scss">
}) .diy-dialog-wrap .el-form-item__label{
</script> height: auto !important;
}
<style lang="scss" scoped></style> </style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

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

@ -1,267 +1,201 @@
<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" </template>
:label="t('studentId')"
min-width="120" <script lang="ts" setup>
:show-overflow-tooltip="true" import { reactive, ref, watch } from 'vue'
/> import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
<el-table-column import { getStudentCoursesList, deleteStudentCourses, getWithStudentList, getWithCourseList } from '@/app/api/student_courses'
prop="course_id" import { img } from '@/utils/common'
:label="t('courseId')" import { ElMessageBox,FormInstance } from 'element-plus'
min-width="120" import Edit from '@/app/views/student_courses/components/student-courses-edit.vue'
:show-overflow-tooltip="true" import { useRoute } from 'vue-router'
/> const route = useRoute()
const pageName = route.meta.title;
<el-table-column
prop="total_hours" let studentCoursesTable = reactive({
:label="t('totalHours')" page: 1,
min-width="120" limit: 10,
:show-overflow-tooltip="true" total: 0,
/> loading: true,
data: [],
<el-table-column searchParam:{
prop="gift_hours" "student_id":"",
:label="t('giftHours')" "course_id":""
min-width="120" }
:show-overflow-tooltip="true" })
/>
const searchFormRef = ref<FormInstance>()
<el-table-column
prop="start_date" //
:label="t('startDate')" const selectData = ref<any[]>([])
min-width="120"
:show-overflow-tooltip="true" //
/>
<el-table-column /**
prop="end_date" * 获取学员课程列表
:label="t('endDate')" */
min-width="120" const loadStudentCoursesList = (page: number = 1) => {
:show-overflow-tooltip="true" studentCoursesTable.loading = true
/> studentCoursesTable.page = page
<el-table-column getStudentCoursesList({
:label="t('operation')" page: studentCoursesTable.page,
fixed="right" limit: studentCoursesTable.limit,
min-width="120" ...studentCoursesTable.searchParam
> }).then(res => {
<template #default="{ row }"> studentCoursesTable.loading = false
<el-button type="primary" link @click="editEvent(row)">{{ studentCoursesTable.data = res.data.data
t('edit') studentCoursesTable.total = res.data.total
}}</el-button> }).catch(() => {
<el-button type="primary" link @click="deleteEvent(row.id)">{{ studentCoursesTable.loading = false
t('delete') })
}}</el-button> }
</template> loadStudentCoursesList()
</el-table-column>
</el-table> const editStudentCoursesDialog: Record<string, any> | null = ref(null)
<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" const addEvent = () => {
:total="studentCoursesTable.total" editStudentCoursesDialog.value.setFormData()
@size-change="loadStudentCoursesList()" editStudentCoursesDialog.value.showDialog = true
@current-change="loadStudentCoursesList" }
/>
</div> /**
</div> * 编辑学员课程
* @param data
<edit ref="editStudentCoursesDialog" @complete="loadStudentCoursesList" /> */
</el-card> const editEvent = (data: any) => {
</div> editStudentCoursesDialog.value.setFormData(data)
</template> editStudentCoursesDialog.value.showDialog = true
}
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue' /**
import { t } from '@/lang' * 删除学员课程
import { useDictionary } from '@/app/api/dict' */
import { const deleteEvent = (id: number) => {
getStudentCoursesList, ElMessageBox.confirm(t('studentCoursesDeleteTips'), t('warning'),
deleteStudentCourses, {
} from '@/app/api/student_courses' confirmButtonText: t('confirm'),
import { img } from '@/utils/common' cancelButtonText: t('cancel'),
import { ElMessageBox, FormInstance } from 'element-plus' type: 'warning',
import Edit from '@/app/views/student_courses/components/student-courses-edit.vue' }
import { useRoute } from 'vue-router' ).then(() => {
const route = useRoute() deleteStudentCourses(id).then(() => {
const pageName = route.meta.title loadStudentCoursesList()
}).catch(() => {
let studentCoursesTable = reactive({ })
page: 1, })
limit: 10, }
total: 0,
loading: true,
data: [], const studentIdList = ref([])
searchParam: { const setStudentIdList = async () => {
student_id: '', studentIdList.value = await (await getWithStudentList({})).data
course_id: '', }
total_hours: '', setStudentIdList()
gift_hours: '', const courseIdList = ref([])
start_date: '', const setCourseIdList = async () => {
end_date: '', courseIdList.value = await (await getWithCourseList({})).data
}, }
}) setCourseIdList()
const searchFormRef = ref<FormInstance>() const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
// formEl.resetFields()
const selectData = ref<any[]>([]) loadStudentCoursesList()
}
// </script>
/** <style lang="scss" scoped>
* 获取学员课程列表 /* 多行超出隐藏 */
*/ .multi-hidden {
const loadStudentCoursesList = (page: number = 1) => { word-break: break-all;
studentCoursesTable.loading = true text-overflow: ellipsis;
studentCoursesTable.page = page overflow: hidden;
display: -webkit-box;
getStudentCoursesList({ -webkit-line-clamp: 2;
page: studentCoursesTable.page, -webkit-box-orient: vertical;
limit: studentCoursesTable.limit, }
...studentCoursesTable.searchParam, </style>
})
.then((res) => {
studentCoursesTable.loading = false
studentCoursesTable.data = res.data.data
studentCoursesTable.total = res.data.total
})
.catch(() => {
studentCoursesTable.loading = false
})
}
loadStudentCoursesList()
const editStudentCoursesDialog: Record<string, any> | null = ref(null)
/**
* 添加学员课程
*/
const addEvent = () => {
editStudentCoursesDialog.value.setFormData()
editStudentCoursesDialog.value.showDialog = true
}
/**
* 编辑学员课程
* @param data
*/
const editEvent = (data: any) => {
editStudentCoursesDialog.value.setFormData(data)
editStudentCoursesDialog.value.showDialog = true
}
/**
* 删除学员课程
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('studentCoursesDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteStudentCourses(id)
.then(() => {
loadStudentCoursesList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadStudentCoursesList()
}
</script>
<style lang="scss" scoped>
/* 多行超出隐藏 */
.multi-hidden {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</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>

37
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');
} }
/** /**
@ -109,5 +92,9 @@ class CommunicationRecords extends BaseAdminController
return success('DELETE_SUCCESS'); return success('DELETE_SUCCESS');
} }
public function getCustomerResourcesAll(){
return success(( new CommunicationRecordsService())->getCustomerResourcesAll());
}
} }

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,

130
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);
}
}
/**
* 搜索器:沟通记录修改时间
* @param $value
* @param $data
*/
public function searchUpdatedAtAttr($query, $value, $data)
{
if ($value) {
$query->where("updated_at", $value);
}
}
public function customerResources(){
return $this->hasOne(CustomerResources::class, 'id', 'resource_id')->joinType('left')->withField('name,id')->bind(['resource_id_name'=>'name']);
}
} }

20
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;
@ -21,7 +21,7 @@ use think\model\relation\HasOne;
* Class Campus * Class Campus
* @package app\model\campus * @package app\model\campus
*/ */
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';
/** /**
* 定义软删除字段的默认值. * 定义软删除字段的默认值.
@ -61,7 +61,7 @@ class Campus extends BaseModel
$query->where("campus_name", "like", "%".$value."%"); $query->where("campus_name", "like", "%".$value."%");
} }
} }
/** /**
* 搜索器:校区校区地址 * 搜索器:校区校区地址
* @param $value * @param $value
@ -73,7 +73,7 @@ class Campus extends BaseModel
$query->where("campus_address", $value); $query->where("campus_address", $value);
} }
} }
/** /**
* 搜索器:校区校区状态 * 搜索器:校区校区状态
* @param $value * @param $value
@ -85,10 +85,10 @@ class Campus extends BaseModel
$query->where("campus_status", $value); $query->where("campus_status", $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;
}

74
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);
}
}
/** public function student(){
* 搜索器:学员课程课程结束日期 return $this->hasOne(Student::class, 'id', 'student_id')->joinType('left')->withField('name,id')->bind(['student_id_name'=>'name']);
* @param $value
* @param $data
*/
public function searchEndDateAttr($query, $value, $data)
{
if ($value) {
$query->where("end_date", $value);
}
} }
public function course(){
return $this->hasOne(Course::class, 'id', 'course_id')->joinType('left')->withField('course_name,id')->bind(['course_id_name'=>'course_name']);
}
} }

29
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("操作成功");
} }
/** /**
@ -94,6 +108,11 @@ class CommunicationRecordsService extends BaseAdminService
return $res; return $res;
} }
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