于宏哲PHP 9 months ago
parent
commit
0baf8c3e13
  1. 4
      admin/src/app/views/campus_person_role/campus_person_role.vue
  2. 4
      admin/src/app/views/campus_person_role/components/campus-person-role-edit.vue
  3. 543
      admin/src/app/views/course/components/course-edit.vue
  4. 4
      admin/src/app/views/departments/departments.vue
  5. 489
      admin/src/app/views/xsyj/xsyj.vue
  6. 5
      niucloud/app/adminapi/controller/course/Course.php
  7. 1
      niucloud/app/adminapi/controller/sys/System.php
  8. 8
      niucloud/app/api/controller/apiController/Personnel.php
  9. 99
      niucloud/app/api/controller/apiController/Statistics.php
  10. 22
      niucloud/app/common.php
  11. 12
      niucloud/app/service/admin/campus_person_role/CampusPersonRoleService.php
  12. 1
      niucloud/app/service/admin/course/CourseService.php
  13. 35
      niucloud/app/service/admin/sys/SystemService.php
  14. 30
      niucloud/app/service/api/apiService/PersonnelService.php
  15. 5
      niucloud/app/service/api/apiService/ResourceSharingService.php

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

@ -218,10 +218,10 @@ let campusPersonRoleTable = reactive({
if(pageName == '市场人员列表'){ if(pageName == '市场人员列表'){
campusPersonRoleTable.searchParam.dept_id = 1; campusPersonRoleTable.searchParam.dept_id = 1;
}else if(pageName == '销售人员列表'){ }else if(pageName == '销售人员列表'){
campusPersonRoleTable.searchParam.dept_id = 2; campusPersonRoleTable.searchParam.dept_id = 3;
// campusPersonRoleTable.searchParam.role_id = 2; // campusPersonRoleTable.searchParam.role_id = 2;
}else if(pageName == '教练管理'){ }else if(pageName == '教练管理'){
campusPersonRoleTable.searchParam.role_id = 5; campusPersonRoleTable.searchParam.dept_id = 2;
} }
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()

4
admin/src/app/views/campus_person_role/components/campus-person-role-edit.vue

@ -160,10 +160,10 @@ const initialFormData = {
if(pageName == '市场人员列表'){ if(pageName == '市场人员列表'){
initialFormData.dept_id = 1; initialFormData.dept_id = 1;
}else if(pageName == '销售人员列表'){ }else if(pageName == '销售人员列表'){
initialFormData.dept_id = 2; initialFormData.dept_id = 3;
// campusPersonRoleTable.searchParam.role_id = 2; // campusPersonRoleTable.searchParam.role_id = 2;
}else if(pageName == '教练管理'){ }else if(pageName == '教练管理'){
initialFormData.dept_id = 3; initialFormData.dept_id = 2;
}else{ }else{
initialFormData.dept_id = 0; initialFormData.dept_id = 0;
} }

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

@ -1,319 +1,290 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateCourse') : t('addCourse')" width="50%"
v-model="showDialog" class="diy-dialog-wrap" :destroy-on-close="true">
:title="formData.id ? t('updateCourse') : t('addCourse')" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form"
width="50%" v-loading="loading">
class="diy-dialog-wrap" <el-form-item :label="t('courseName')" prop="course_name">
:destroy-on-close="true" <el-input v-model="formData.course_name" clearable :placeholder="t('courseNamePlaceholder')"
> class="input-width" />
<el-form </el-form-item>
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form-item :label="t('courseName')" prop="course_name">
<el-input
v-model="formData.course_name"
clearable
:placeholder="t('courseNamePlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('courseType')" prop="course_type"> <el-form-item :label="t('courseType')" prop="course_type">
<el-select <el-select class="input-width" v-model="formData.course_type" :placeholder="t('courseTypePlaceholder')">
class="input-width" <el-option v-for="(item, index) in courseTypeList" :key="index" :label="item.name"
v-model="formData.course_type" :value="item.name" />
:placeholder="t('courseTypePlaceholder')" </el-select>
> </el-form-item>
<el-option <el-form-item :label="t('duration')" prop="duration">
v-for="(item, index) in courseTypeList" <el-input v-model="formData.duration" clearable :placeholder="t('durationPlaceholder')"
:key="index" class="input-width">
:label="item.name" <template #suffix>
:value="item.name" <span></span>
/> </template>
</el-select> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="t('duration')" prop="duration">
<el-input
v-model="formData.duration"
clearable
:placeholder="t('durationPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('sessionCount')" prop="session_count">
<el-input
v-model="formData.session_count"
clearable
:placeholder="t('sessionCountPlaceholder')"
class="input-width"
/>
</el-form-item>
<el-form-item :label="t('giftSessionCount')" prop="gift_session_count"> <el-form-item :label="t('sessionCount')" prop="session_count">
<el-input <el-input v-model="formData.session_count" clearable :placeholder="t('sessionCountPlaceholder')"
v-model="formData.gift_session_count" class="input-width" >
clearable
:placeholder="t('giftSessionCountPlaceholder')" <template #suffix>
class="input-width" <span></span>
/> </template>
</el-form-item> </el-input>
</el-form-item>
<el-form-item <el-form-item :label="t('giftSessionCount')" prop="gift_session_count">
:label="t('singleSessionCount')" <el-input v-model="formData.gift_session_count" clearable
prop="single_session_count" :placeholder="t('giftSessionCountPlaceholder')" class="input-width" >
>
<el-input <template #suffix>
v-model="formData.single_session_count" <span></span>
clearable </template>
:placeholder="t('singleSessionCountPlaceholder')" </el-input>
class="input-width" </el-form-item>
/>
</el-form-item>
<el-form-item :label="t('price')" prop="price"> <el-form-item :label="t('singleSessionCount')" prop="single_session_count">
<el-input <el-input v-model="formData.single_session_count" clearable
v-model="formData.price" :placeholder="t('singleSessionCountPlaceholder')" class="input-width" >
clearable
:placeholder="t('pricePlaceholder')" <template #suffix>
class="input-width" <span></span>
/> </template>
</el-form-item> </el-input>
</el-form-item>
<el-form-item :label="t('internalReminder')" prop="internal_reminder"> <el-form-item :label="t('price')" prop="price">
<el-input <el-input v-model="formData.price" clearable :placeholder="t('pricePlaceholder')" class="input-width" >
v-model="formData.internal_reminder"
clearable <template #suffix>
:placeholder="t('internalReminderPlaceholder')" <span></span>
class="input-width" </template>
/> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="t('customerReminder')" prop="customer_reminder"> <el-form-item :label="t('internalReminder')" prop="internal_reminder">
<el-input <el-input v-model="formData.internal_reminder" clearable :placeholder="t('internalReminderPlaceholder')"
v-model="formData.customer_reminder" class="input-width" >
clearable
:placeholder="t('customerReminderPlaceholder')" <template #suffix>
class="input-width" <span></span>
/> </template>
</el-form-item> </el-input>
</el-form-item>
<el-form-item :label="t('remarks')"> <el-form-item :label="t('customerReminder')" prop="customer_reminder">
<el-input <el-input v-model="formData.customer_reminder" clearable :placeholder="t('customerReminderPlaceholder')"
v-model="formData.remarks" class="input-width" >
clearable
:placeholder="t('remarksPlaceholder')" <template #suffix>
class="input-width" <span></span>
/> </template>
</el-form-item> </el-input>
</el-form> </el-form-item>
<template #footer> <el-form-item :label="t('remarks')">
<span class="dialog-footer"> <el-input v-model="formData.remarks" clearable :placeholder="t('remarksPlaceholder')"
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button> class="input-width" />
<el-button </el-form-item>
type="primary" </el-form>
:loading="loading"
@click="confirm(formRef)" <template #footer>
>{{ t('confirm') }}</el-button <span class="dialog-footer">
> <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
</span> <el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</template> </span>
</el-dialog> </template>
</el-dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue' import { ref, reactive, computed, watch } from 'vue'
import { useDictionary } from '@/app/api/dict' import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang' import { t } from '@/lang'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { addCourse, editCourse, getCourseInfo } from '@/app/api/course' import { addCourse, editCourse, getCourseInfo } from '@/app/api/course'
let showDialog = ref(false) let showDialog = ref(false)
const loading = ref(false) const loading = ref(false)
/** /**
* 表单数据 * 表单数据
*/ */
const initialFormData = { const initialFormData = {
id: '', id: '',
course_name: '', course_name: '',
course_type: '', course_type: '',
duration: '', duration: '',
session_count: '', session_count: '',
single_session_count: '', single_session_count: '',
price: '', price: '',
internal_reminder: '', internal_reminder: '',
customer_reminder: '', customer_reminder: '',
remarks: '', remarks: '',
gift_session_count: '0', gift_session_count: '0',
} }
const formData: Record<string, any> = reactive({ ...initialFormData }) const formData : Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
// //
const formRules = computed(() => { const formRules = computed(() => {
return { return {
course_name: [ course_name: [
{ required: true, message: t('courseNamePlaceholder'), trigger: 'blur' }, { required: true, message: t('courseNamePlaceholder'), trigger: 'blur' },
], ],
course_type: [ course_type: [
{ required: true, message: t('courseTypePlaceholder'), trigger: 'blur' }, { required: true, message: t('courseTypePlaceholder'), trigger: 'blur' },
], ],
duration: [ duration: [
{ required: true, message: t('durationPlaceholder'), trigger: 'blur' }, { required: true, message: t('durationPlaceholder'), trigger: 'blur' },
], ],
session_count: [ session_count: [
{ {
required: true, required: true,
message: t('sessionCountPlaceholder'), message: t('sessionCountPlaceholder'),
trigger: 'blur', trigger: 'blur',
}, },
], ],
gift_session_count: [ gift_session_count: [
{ {
required: false, required: false,
message: t('giftSessionCountPlaceholder'), message: t('giftSessionCountPlaceholder'),
trigger: 'blur', trigger: 'blur',
}, },
], ],
single_session_count: [ single_session_count: [
{ {
required: true, required: true,
message: t('singleSessionCountPlaceholder'), message: t('singleSessionCountPlaceholder'),
trigger: 'blur', trigger: 'blur',
}, },
], ],
price: [ price: [
{ required: true, message: t('pricePlaceholder'), trigger: 'blur' }, { required: true, message: t('pricePlaceholder'), trigger: 'blur' },
], ],
internal_reminder: [ internal_reminder: [
{ {
required: true, required: true,
message: t('internalReminderPlaceholder'), message: t('internalReminderPlaceholder'),
trigger: 'blur', trigger: 'blur',
}, },
], ],
customer_reminder: [ customer_reminder: [
{ {
required: true, required: true,
message: t('customerReminderPlaceholder'), message: t('customerReminderPlaceholder'),
trigger: 'blur', trigger: 'blur',
}, },
], ],
remarks: [ remarks: [
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' }, { required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
], ],
} }
}) })
const emit = defineEmits(['complete']) const emit = defineEmits(['complete'])
const courseTypeList = ref([]) const courseTypeList = ref([])
const getcourseTypeList = async () => { const getcourseTypeList = async () => {
courseTypeList.value = await ( courseTypeList.value = await (
await useDictionary('course_type') await useDictionary('course_type')
).data.dictionary ).data.dictionary
} }
getcourseTypeList() getcourseTypeList()
/** /**
* 确认 * 确认
* @param formEl * @param formEl
*/ */
const confirm = async (formEl: FormInstance | undefined) => { const confirm = async (formEl : FormInstance | undefined) => {
if (loading.value || !formEl) return if (loading.value || !formEl) return
let save = formData.id ? editCourse : addCourse let save = formData.id ? editCourse : addCourse
await formEl.validate(async (valid) => { await formEl.validate(async (valid) => {
if (valid) { if (valid) {
loading.value = true loading.value = true
let data = formData let data = formData
save(data) save(data)
.then((res) => { .then((res) => {
loading.value = false loading.value = false
showDialog.value = false showDialog.value = false
emit('complete') emit('complete')
}) })
.catch((err) => { .catch((err) => {
loading.value = false loading.value = false
}) })
} }
}) })
} }
// //
const setFormData = async (row: any = null) => { const setFormData = async (row : any = null) => {
Object.assign(formData, initialFormData) Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if (row) { if (row) {
const data = await (await getCourseInfo(row.id)).data const data = await (await getCourseInfo(row.id)).data
if (data) if (data)
Object.keys(formData).forEach((key: string) => { Object.keys(formData).forEach((key : string) => {
if (data[key] != undefined) formData[key] = data[key] if (data[key] != undefined) formData[key] = data[key]
}) })
} }
loading.value = false loading.value = false
} }
// //
const mobileVerify = (rule: any, value: any, callback: any) => { const mobileVerify = (rule : any, value : any, callback : any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) { if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile'))) callback(new Error(t('generateMobile')))
} else { } else {
callback() callback()
} }
} }
// //
const idCardVerify = (rule: any, value: any, callback: any) => { const idCardVerify = (rule : any, value : any, callback : any) => {
if ( if (
value && 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( !/^[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'))) callback(new Error(t('generateIdCard')))
} else { } else {
callback() callback()
} }
} }
// //
const emailVerify = (rule: any, value: any, callback: any) => { const emailVerify = (rule : any, value : any, callback : any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail'))) callback(new Error(t('generateEmail')))
} else { } else {
callback() callback()
} }
} }
// //
const numberVerify = (rule: any, value: any, callback: any) => { const numberVerify = (rule : any, value : any, callback : any) => {
if (!Number.isInteger(value)) { if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber'))) callback(new Error(t('generateNumber')))
} else { } else {
callback() callback()
} }
} }
defineExpose({ defineExpose({
showDialog, showDialog,
setFormData, setFormData,
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>
<style lang="scss"> <style lang="scss">
.diy-dialog-wrap .el-form-item__label { .diy-dialog-wrap .el-form-item__label {
height: auto !important; height: auto !important;
} }
</style> </style>

4
admin/src/app/views/departments/departments.vue

@ -91,7 +91,7 @@
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
/> />
<el-table-column <!-- <el-table-column
:label="t('operation')" :label="t('operation')"
fixed="right" fixed="right"
min-width="120" min-width="120"
@ -104,7 +104,7 @@
t('delete') t('delete')
}}</el-button> }}</el-button>
</template> </template>
</el-table-column> </el-table-column> -->
</el-table> </el-table>
<div class="mt-[16px] flex justify-end"> <div class="mt-[16px] flex justify-end">
<el-pagination <el-pagination

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

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

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

@ -68,7 +68,7 @@ class Course extends BaseAdminController
["internal_reminder", 0], ["internal_reminder", 0],
["customer_reminder", 0], ["customer_reminder", 0],
["remarks", ""], ["remarks", ""],
["gift_session_count ", ""], ["gift_session_count", ""],
]); ]);
$this->validate($data, 'app\validate\course\Course.add'); $this->validate($data, 'app\validate\course\Course.add');
@ -94,8 +94,9 @@ class Course extends BaseAdminController
["customer_reminder", 0], ["customer_reminder", 0],
["remarks", ""], ["remarks", ""],
["contract_id",""], ["contract_id",""],
["gift_session_count ",""], ["gift_session_count",0],
]); ]);
$this->validate($data, 'app\validate\course\Course.edit'); $this->validate($data, 'app\validate\course\Course.edit');
(new CourseService())->edit($id, $data); (new CourseService())->edit($id, $data);
return success('EDIT_SUCCESS'); return success('EDIT_SUCCESS');

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

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

8
niucloud/app/api/controller/apiController/Personnel.php

@ -72,12 +72,14 @@ class Personnel extends BaseApiService
$where = [ $where = [
'account_type' => $request->param('account_type', ''),//账号类型|teacher=老师,market=销售 'account_type' => $request->param('account_type', ''),//账号类型|teacher=老师,market=销售
'personnel_id' => $request->param('personnel_id', ''),//员工id 'personnel_id' => $request->param('personnel_id', ''),//员工id
'campus' => $request->param('campus', ''),//校区id
]; ];
$field = 'id,name'; $field = 'id,name';
$res = (new PersonnelService())->getAll($where,$field); $res = (new PersonnelService())->getAll($where,$field);
if(!$res){ // if(!$res){
return fail('账户信息有误'); // return fail('账户信息有误');
} // }
return success($res); return success($res);
} }

99
niucloud/app/api/controller/apiController/Statistics.php

@ -13,6 +13,7 @@ namespace app\api\controller\apiController;
use app\model\market_performance\MarketPerformance; use app\model\market_performance\MarketPerformance;
use app\model\order_table\OrderTable; use app\model\order_table\OrderTable;
use app\model\personnel\PersonnelSummary;
use app\Request; use app\Request;
use app\service\api\apiService\CommonService; use app\service\api\apiService\CommonService;
use core\base\BaseApiService; use core\base\BaseApiService;
@ -249,6 +250,8 @@ class Statistics extends BaseApiService
*/ */
public function marketData(Request $request) public function marketData(Request $request)
{ {
$resourceSharing = new ResourceSharing();
$customerResources = new CustomerResources();
$personnel_id = $request->param('personnel_id','');//员工表id $personnel_id = $request->param('personnel_id','');//员工表id
$role_key_arr = $request->param('role_key_arr','');//array 角色key $role_key_arr = $request->param('role_key_arr','');//array 角色key
$role_key_arr = explode(',',$role_key_arr); $role_key_arr = explode(',',$role_key_arr);
@ -258,59 +261,75 @@ class Statistics extends BaseApiService
// $role_key_arr = ['sale'];//@todo 调试使用 // $role_key_arr = ['sale'];//@todo 调试使用
$assigned = 0;
$unassigned = 0;
$lastWeekNewTotal = 0;
$resource_list = $resourceSharing->where(['shared_by' => $personnel_id])->select();
foreach($resource_list as $item){
$sharedAt = strtotime($item['shared_at']);
$date = date("Y-m-d",$sharedAt);
if(isDateInThisWeek($date)){
$assigned++;
}
}
$customer_list = $customerResources->where(['consultant' => $personnel_id])->select();
foreach($customer_list as $item){
if(!$resourceSharing->where(['resource_id' => $item['id']])->find()){
if(isDateInThisWeek($item['create_date'])){
$unassigned++;
}
}
if(isDateInLastWeek($item['create_date'])){
$lastWeekNewTotal++;
}
}
$staff_list = [];
$personnel_summary = new PersonnelSummary();
$staff_list = $personnel_summary
->alias('a')
->join(['school_campus_person_role' => 'b'],'a.campus_person_role_id = b.id','left')
->join(['school_personnel' => 'c'],'b.person_id = c.id','left')
->field("c.name,a.task_num as goal,a.complete_num as wx_yj")
->select();
if(in_array('market',$role_key_arr) || in_array('market_manager',$role_key_arr)){ if(in_array('market',$role_key_arr) || in_array('market_manager',$role_key_arr)){
//市场人员统计数据(地推拉人头的) //市场人员统计数据(地推拉人头的)
$data = [ $data = [
'role_type'=>'market_type',//角色类型|market_type=市场,sale_type=销售 'role_type'=>'market_type',//角色类型|market_type=市场,sale_type=销售
'num_1' => ($assigned = rand(20, 150)),//本周已分配 'num_1' => $assigned,//本周已分配
'num_2' => ($unassigned = rand(5, 50)),//本周未分配 'num_2' => $unassigned,//本周未分配
'total_1' => ($newTotal = $assigned + $unassigned),//本周拉新总数 'total_1' => ($newTotal = $assigned + $unassigned),//本周拉新总数
'total_2' => ($lastWeekNewTotal = rand(10, 100)),//上周拉新总数 'total_2' => $lastWeekNewTotal,//上周拉新总数
'num_1_rate' => min(100, round($assigned / $newTotal * 100, 2)),//已分配比例 'num_1_rate' => $newTotal ? min(100, round($assigned / $newTotal * 100, 2)) : 0,//已分配比例
'num_2_rate' => min(100, round($unassigned / $newTotal * 100, 2)),//未分配比例 'num_2_rate' => $newTotal ? min(100, round($unassigned / $newTotal * 100, 2)) : 0,//未分配比例
'num_3_rate' => min(100, round($newTotal / max(1, $lastWeekNewTotal) * 100, 2)), // 本周拉新率(对比上周) 'num_3_rate' => $lastWeekNewTotal ? min(100, round($newTotal / max(1, $lastWeekNewTotal) * 100, 2)) : 0, // 本周拉新率(对比上周)
'num_4_rate' => min(100, round($assigned / max(1, $lastWeekNewTotal) * 100, 2)), // 本周分配率(对比上周) 'num_4_rate' => $lastWeekNewTotal ? min(100, round($assigned / max(1, $lastWeekNewTotal) * 100, 2)) : 0, // 本周分配率(对比上周)
'staff_list' => [ 'staff_list' => $staff_list,
['name' => '张三', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '李四', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '王五', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '赵六', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '钱七', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '孙八', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '周九', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '吴十', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '郑十一', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '王十二', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
],
]; ];
}elseif(in_array('sale',$role_key_arr) || in_array('sale_manager',$role_key_arr)){ }elseif(in_array('sale',$role_key_arr) || in_array('sale_manager',$role_key_arr)){
//销售人员统计数据(买课的) //销售人员统计数据(买课的)
$data = [ $data = [
'role_type'=>'sale',//角色类型|market_type=市场,sale_type=销售 'role_type'=>'sale',//角色类型|market_type=市场,sale_type=销售
'num_1' => ($assigned = rand(20, 150)),//本周已分配 'num_1' => $assigned,//本周已分配
'num_2' => ($unassigned = rand(5, 50)),//本周未分配 'num_2' => $unassigned,//本周未分配
'total_1' => ($newTotal = $assigned + $unassigned),//本周拉新总数 'total_1' => ($newTotal = $assigned + $unassigned),//本周拉新总数
'total_2' => ($lastWeekNewTotal = rand(10, 100)),//上周拉新总数 'total_2' => $lastWeekNewTotal,//上周拉新总数
'num_1_rate' => min(100, round($assigned / $newTotal * 100, 2)),//已分配比例 'num_1_rate' => $newTotal ? min(100, round($assigned / $newTotal * 100, 2)) : 0,//已分配比例
'num_2_rate' => min(100, round($unassigned / $newTotal * 100, 2)),//未分配比例 'num_2_rate' => $newTotal ? min(100, round($unassigned / $newTotal * 100, 2)) : 0,//未分配比例
'num_3_rate' => min(100, round($newTotal / max(1, $lastWeekNewTotal) * 100, 2)), // 本周拉新率(对比上周) 'num_3_rate' => $lastWeekNewTotal ? min(100, round($newTotal / max(1, $lastWeekNewTotal) * 100, 2)) : 0, // 本周拉新率(对比上周)
'num_4_rate' => min(100, round($assigned / max(1, $lastWeekNewTotal) * 100, 2)), // 本周分配率(对比上周) 'num_4_rate' => $lastWeekNewTotal ? min(100, round($assigned / max(1, $lastWeekNewTotal) * 100, 2)) : 0, // 本周分配率(对比上周)
'staff_list' => [ 'staff_list' => $staff_list,
['name' => '张三', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '李四', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '王五', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '赵六', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '钱七', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '孙八', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '周九', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '吴十', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '郑十一', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
['name' => '王十二', 'goal' => rand(50, 200), 'wx_yj' => rand(30, 180)],
],
]; ];
}else{ }else{
return fail('角色权限不正确'); return fail('角色权限不正确');

22
niucloud/app/common.php

@ -1271,3 +1271,25 @@ function get_role_type($role_id)
/** /**
* 获取顶级部门 * 获取顶级部门
*/ */
function isDateInThisWeek($date) {
$timestamp = strtotime($date);
// 获取本周开始和结束时间戳(周一到周日)
$monday = strtotime('monday this week');
$sunday = strtotime('sunday this week 23:59:59');
return $timestamp >= $monday && $timestamp <= $sunday;
}
function isDateInLastWeek($date) {
$timestamp = strtotime($date);
// 获取上周的周一(开始)和周日(结束)
$startOfLastWeek = strtotime('monday last week');
$endOfLastWeek = strtotime('sunday last week 23:59:59');
return $timestamp >= $startOfLastWeek && $timestamp <= $endOfLastWeek;
}

12
niucloud/app/service/admin/campus_person_role/CampusPersonRoleService.php

@ -48,9 +48,9 @@ class CampusPersonRoleService extends BaseAdminService
$where[] = ['a.campus_id','=',$data['campus_id']]; $where[] = ['a.campus_id','=',$data['campus_id']];
} }
// if($data['role_id']){ if($data['role_id']){
// $where[] = ['a.role_id','=',$data['role_id']]; $where[] = ['a.role_id','=',$data['role_id']];
// } }
if($data['dept_id']){ if($data['dept_id']){
$where[] = ['a.dept_id','=',$data['dept_id']]; $where[] = ['a.dept_id','=',$data['dept_id']];
@ -96,9 +96,12 @@ class CampusPersonRoleService extends BaseAdminService
*/ */
public function add(array $data) public function add(array $data)
{ {
if($this->model->where(['person_id' => $data['person_id']])->find()){ if($this->model->where(['person_id' => $data['person_id']])->find()){
return fail("重复操作"); return fail("重复操作");
} }
$role = new SysRole();
$data['dept_id'] = $role->where(['role_id' => $data['role_id']])->value("dept_id");
$res = $this->model->create($data); $res = $this->model->create($data);
return success("操作成功"); return success("操作成功");
@ -122,6 +125,9 @@ class CampusPersonRoleService extends BaseAdminService
$personnel_summary->where(['id' => $tasks['id']])->update(['task_num' => $tasks['task_num']]); $personnel_summary->where(['id' => $tasks['id']])->update(['task_num' => $tasks['task_num']]);
} }
$role = new SysRole();
$data['dept_id'] = $role->where(['role_id' => $data['role_id']])->value("dept_id");
$this->model->where([['id', '=', $id]])->update($data); $this->model->where([['id', '=', $id]])->update($data);
return success("操作成功"); return success("操作成功");
} }

1
niucloud/app/service/admin/course/CourseService.php

@ -84,7 +84,6 @@ class CourseService extends BaseAdminService
*/ */
public function edit(int $id, array $data) public function edit(int $id, array $data)
{ {
$this->model->where([['id', '=', $id]])->update($data); $this->model->where([['id', '=', $id]])->update($data);
return true; return true;
} }

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

@ -207,7 +207,15 @@ class SystemService extends BaseAdminService
$config = new SysConfig(); $config = new SysConfig();
$config->where(['config_key' => 'XSYJ'])->update([ $config->where(['config_key' => 'XSYJ'])->update([
'value' => json_encode($data) 'value' => json_encode($data['stages'])
]);
$config->where(['config_key' => 'XSPJ'])->update([
'value' => json_encode($data['form'])
]);
$config->where(['config_key' => 'course_type'])->update([
'value' => json_encode($data['course_type'])
]); ]);
return true; return true;
@ -215,11 +223,34 @@ class SystemService extends BaseAdminService
public function get_xsyj_config(){ public function get_xsyj_config(){
$dict = new \app\model\dict\Dict();
$config = new SysConfig(); $config = new SysConfig();
$data = $config->where(['config_key' => 'XSYJ'])->value("value"); $data = $config->where(['config_key' => 'XSYJ'])->value("value");
return $data; $form = $config->where(['config_key' => 'XSPJ'])->value("value");
$course_type = $config->where(['config_key' => 'course_type'])->value("value");
// $course_type = json_decode($course_type, true);
$dict_course_type = $dict->where(['key' => 'course_type'])->value("dictionary");
$dict_course_type = json_decode($dict_course_type, true);
foreach ($dict_course_type as $k => $v) {
foreach ($course_type as $k1 => $v1) {
if($v['value'] == $v1['value']){
$dict_course_type[$k]['num'] = $v1['num'] ?? 0;
}
}
if(!isset($dict_course_type[$k]['num'])){
$dict_course_type[$k]['num'] = 0;
}
}
return ['data' => $data,'form' => $form,'course_type' => $dict_course_type];
} }

30
niucloud/app/service/api/apiService/PersonnelService.php

@ -192,8 +192,20 @@ class PersonnelService extends BaseApiService
$campus_id = CampusPersonRole::where('person_id', $where['personnel_id']) $campus_id = CampusPersonRole::where('person_id', $where['personnel_id'])
->distinct(true) ->distinct(true)
->column('campus_id'); ->column('campus_id');
if ($campus_id) { if ($campus_id[0]) {
$person_id_arr = CampusPersonRole::whereIn('campus_id', $campus_id)
->where(['dept_id' => 3])
->distinct(true)
->column('person_id');
if ($person_id_arr) {
//根据校区id获取校区下的全部员工
$model = $model->whereIn('id', $person_id_arr);
}
}else{
echo 123;die;
$person_id_arr = CampusPersonRole::whereIn('campus_id', $campus_id) $person_id_arr = CampusPersonRole::whereIn('campus_id', $campus_id)
->where(['dept_id' => 3])
->distinct(true) ->distinct(true)
->column('person_id'); ->column('person_id');
if ($person_id_arr) { if ($person_id_arr) {
@ -207,6 +219,22 @@ class PersonnelService extends BaseApiService
$model = $model->where('account_type', $where['account_type']); $model = $model->where('account_type', $where['account_type']);
} }
if (!empty($where['campus'])) {
// $model = $model->where('campus', $where['campus']);
$person_id_arr = CampusPersonRole::whereIn('campus_id', $where['campus'])
->distinct(true)
->where(['dept_id' => 3])
->column('person_id');
if ($person_id_arr) {
//根据校区id获取校区下的全部员工
$model = $model->whereIn('id', $person_id_arr);
}
}
$res = $model->field($field) $res = $model->field($field)
->select() ->select()
->toArray();//员工信息 ->toArray();//员工信息

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

@ -11,6 +11,7 @@
namespace app\service\api\apiService; namespace app\service\api\apiService;
use app\model\campus\Campus;
use app\model\campus_person_role\CampusPersonRole; use app\model\campus_person_role\CampusPersonRole;
use app\model\customer_resources\CustomerResources; use app\model\customer_resources\CustomerResources;
use app\model\order_table\OrderTable; use app\model\order_table\OrderTable;
@ -101,9 +102,11 @@ class ResourceSharingService extends BaseApiService
'page' => $page, 'page' => $page,
])->toArray(); ])->toArray();
$campus = new Campus();
foreach ($res['data'] as &$item){ foreach ($res['data'] as &$item){
$item['customerResource']['source_channel'] = get_dict_value('source',$item['customerResource']['source_channel']); $item['customerResource']['source_channel'] = get_dict_value('source',$item['customerResource']['source_channel']);
$item['customerResource']['campus_name'] = $campus->where(['id' =>$item['customerResource']['campus']])->value("campus_name");
} }
return $res; return $res;

Loading…
Cancel
Save