21 changed files with 850 additions and 145 deletions
@ -0,0 +1,13 @@ |
|||
import request from '@/utils/request' |
|||
|
|||
|
|||
// USER_CODE_BEGIN -- service_logs
|
|||
/** |
|||
* |
|||
* @param params |
|||
* @returns |
|||
*/ |
|||
export function getServiceLogsList(params) { |
|||
return request.get(`service_logs/service_logs`, {params}) |
|||
} |
|||
|
|||
@ -0,0 +1,141 @@ |
|||
<template> |
|||
<el-dialog |
|||
v-model="showDialog" |
|||
title="客户详情" |
|||
width="80%" |
|||
class="user-profile p-6" |
|||
:destroy-on-close="true" |
|||
> |
|||
|
|||
<!-- 顶部用户信息 --> |
|||
<el-card class="mb-4"> |
|||
<div class="flex items-center"> |
|||
<el-avatar :size="60" class="mr-4" /> |
|||
<div> |
|||
<h2 class="text-xl font-bold">{{ user.name }}</h2> |
|||
<p>ID: {{ user.id }}</p> |
|||
<p>电话: {{ user.phone }}</p> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
|
|||
<!-- 标签导航 --> |
|||
<el-tabs v-model="activeTab" class="mb-4"> |
|||
<el-tab-pane label="六要素" name="six-elements" /> |
|||
<el-tab-pane label="修改日志" name="log" /> |
|||
<el-tab-pane label="学生情况" name="student" /> |
|||
<el-tab-pane label="订单列表" name="orders" /> |
|||
<el-tab-pane label="课程列表" name="courses" /> |
|||
</el-tabs> |
|||
|
|||
<!-- 六要素信息卡片 --> |
|||
<el-card v-if="activeTab === 'six-elements'"> |
|||
<h3 class="text-lg font-bold mb-4">六要素信息</h3> |
|||
<el-row :gutter="20" class="mb-2"> |
|||
<el-col :span="8">需求购买力:<el-tag>{{ info.purchasePower }}</el-tag></el-col> |
|||
<el-col :span="8">认知理念:<el-tag>{{ info.knowledge }}</el-tag></el-col> |
|||
<el-col :span="8">距离:<span>{{ info.distance }}</span></el-col> |
|||
</el-row> |
|||
<el-row :gutter="20" class="mb-2"> |
|||
<el-col :span="8">可谈上课时间:<span>{{ info.canTalkDate }}</span></el-col> |
|||
<el-col :span="8">承诺到访时间:<span>{{ info.promisedVisitDate }}</span></el-col> |
|||
<el-col :span="8">实际到访时间:<span>{{ info.actualVisitDate }}</span></el-col> |
|||
</el-row> |
|||
<el-row :gutter="20" class="mb-2"> |
|||
<el-col :span="8">电话后的意向程度:<el-tag>{{ info.phoneIntention }}</el-tag></el-col> |
|||
<el-col :span="8">是否关单: |
|||
<el-tag :type="info.isClosed ? 'success' : 'danger'">{{ info.isClosed ? '是' : '否' }}</el-tag> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<!-- 备注与访问情况 --> |
|||
<el-form label-position="top" class="mt-4"> |
|||
<el-form-item label="沟通备注"> |
|||
<el-input v-model="info.notes" placeholder="填写备注" disabled/> |
|||
</el-form-item> |
|||
<el-form-item label="一访情况"> |
|||
<el-input v-model="info.visit1" placeholder="填写一访情况" disabled/> |
|||
</el-form-item> |
|||
<el-form-item label="二访情况"> |
|||
<el-input v-model="info.visit2" placeholder="填写二访情况" disabled/> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-card> |
|||
</el-dialog> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { useDictionary } from '@/app/api/dict' |
|||
import { ref, reactive, computed, watch } from 'vue' |
|||
|
|||
let showDialog = ref(false) |
|||
|
|||
|
|||
const activeTab = ref('six-elements') |
|||
|
|||
const user = reactive({ |
|||
name: '王五1', |
|||
id: 'U001', |
|||
phone: '13914556707' |
|||
}) |
|||
|
|||
const info = reactive({ |
|||
purchasePower: '', |
|||
knowledge: '', |
|||
distance: '', |
|||
canTalkDate: '', |
|||
promisedVisitDate: '', |
|||
actualVisitDate: '', |
|||
phoneIntention: '', |
|||
isClosed: '', |
|||
notes: '', |
|||
visit1: '', |
|||
visit2: '' |
|||
}) |
|||
|
|||
let call_intentList = ref([]) |
|||
let purchase_powerList = ref([]) |
|||
let concept_awarenessList = ref([]) |
|||
|
|||
const setFormData = async (row) => { |
|||
call_intentList.value = await ( |
|||
await useDictionary('preliminarycustomerintention') |
|||
).data.dictionary |
|||
|
|||
purchase_powerList.value = await ( |
|||
await useDictionary('customer_purchasing_power') |
|||
).data.dictionary |
|||
|
|||
concept_awarenessList.value = await ( |
|||
await useDictionary('cognitive_concept') |
|||
).data.dictionary |
|||
|
|||
user.name = row.name; |
|||
user.id = row.id; |
|||
user.phone = row.phone_number; |
|||
info.purchasePower = purchase_powerList.value.flat().find(item => item.value == row.six.purchase_power)?.name || ''; |
|||
info.knowledge = concept_awarenessList.value.flat().find(item => item.value == row.six.concept_awareness)?.name || ''; |
|||
info.distance = row.six.distance; |
|||
info.canTalkDate = row.six.preferred_class_time; |
|||
info.promisedVisitDate = row.six.promised_visit_time; |
|||
info.actualVisitDate = row.six.actual_visit_time; |
|||
info.phoneIntention = call_intentList.value.flat().find(item => item.value == row.six.call_intent)?.name || ''; |
|||
info.isClosed = row.six.is_closed == 1 ? true : false; |
|||
info.notes = row.six.communication; |
|||
info.visit1 = row.six.first_visit_status; |
|||
info.visit2 = row.six.second_visit_status; |
|||
console.log(call_intentList.value); |
|||
} |
|||
|
|||
defineExpose({ |
|||
showDialog, |
|||
setFormData |
|||
}) |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.user-profile { |
|||
|
|||
margin: 0 auto; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,165 @@ |
|||
<template> |
|||
<div class="main-container"> |
|||
<el-card class="box-card !border-none" shadow="never"> |
|||
<div class="flex justify-between items-center"> |
|||
<span class="text-lg">{{ pageName }}</span> |
|||
</div> |
|||
|
|||
<el-card |
|||
class="box-card !border-none my-[10px] table-search-wrap" |
|||
shadow="never" |
|||
> |
|||
<el-form |
|||
:inline="true" |
|||
:model="studentCourseUsageTable.searchParam" |
|||
ref="searchFormRef" |
|||
> |
|||
<el-form-item label="家长姓名" prop="name"> |
|||
<el-input |
|||
v-model="studentCourseUsageTable.searchParam.name" |
|||
placeholder="请输入家长姓名" |
|||
/> |
|||
</el-form-item> |
|||
|
|||
|
|||
<el-form-item label="评分" prop="score"> |
|||
<el-input |
|||
v-model="studentCourseUsageTable.searchParam.score" |
|||
placeholder="请输入评分" |
|||
/> |
|||
</el-form-item> |
|||
|
|||
|
|||
<el-form-item> |
|||
<el-button type="primary" @click="loadServiceLogsList()">{{ |
|||
t('search') |
|||
}}</el-button> |
|||
<el-button @click="resetForm(searchFormRef)">{{ |
|||
t('reset') |
|||
}}</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-card> |
|||
|
|||
<div class="mt-[10px]"> |
|||
<el-table |
|||
:data="studentCourseUsageTable.data" |
|||
size="large" |
|||
v-loading="studentCourseUsageTable.loading" |
|||
> |
|||
<template #empty> |
|||
<span>{{ |
|||
!studentCourseUsageTable.loading ? t('emptyData') : '' |
|||
}}</span> |
|||
</template> |
|||
|
|||
<el-table-column prop="name" label="家长姓名" min-width="120" :show-overflow-tooltip="true"/> |
|||
|
|||
<el-table-column prop="staff_name" label="教练姓名" min-width="120" :show-overflow-tooltip="true"/> |
|||
|
|||
<el-table-column prop="service_name" label="服务内容" min-width="120" :show-overflow-tooltip="true"/> |
|||
|
|||
|
|||
<el-table-column prop="customer_confirmation" label="是否需要反馈" min-width="120" :show-overflow-tooltip="true"> |
|||
<template #default="{ row }"> |
|||
<div v-if="row.customer_confirmation == 1">是</div> |
|||
<div v-if="row.customer_confirmation == 0">否</div> |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
|
|||
<el-table-column prop="score" label="反馈分数" min-width="120" :show-overflow-tooltip="true"/> |
|||
|
|||
|
|||
|
|||
</el-table> |
|||
<div class="mt-[16px] flex justify-end"> |
|||
<el-pagination |
|||
v-model:current-page="studentCourseUsageTable.page" |
|||
v-model:page-size="studentCourseUsageTable.limit" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
:total="studentCourseUsageTable.total" |
|||
@size-change="loadServiceLogsList()" |
|||
@current-change="loadServiceLogsList" |
|||
/> |
|||
</div> |
|||
</div> |
|||
|
|||
</el-card> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { reactive, ref, watch } from 'vue' |
|||
import { t } from '@/lang' |
|||
import { useDictionary } from '@/app/api/dict' |
|||
import { |
|||
getServiceLogsList, |
|||
} from '@/app/api/service_logs' |
|||
|
|||
import { img } from '@/utils/common' |
|||
import { ElMessageBox, FormInstance } from 'element-plus' |
|||
import { useRoute } from 'vue-router' |
|||
const route = useRoute() |
|||
const pageName = route.meta.title |
|||
|
|||
let studentCourseUsageTable = reactive({ |
|||
page: 1, |
|||
limit: 10, |
|||
total: 0, |
|||
loading: true, |
|||
data: [], |
|||
searchParam: { |
|||
name: '', |
|||
score: '', |
|||
}, |
|||
}) |
|||
|
|||
const searchFormRef = ref<FormInstance>() |
|||
|
|||
// 选中数据 |
|||
const selectData = ref<any[]>([]) |
|||
|
|||
// 字典数据 |
|||
|
|||
/** |
|||
* 获取学员课时消费记录列表 |
|||
*/ |
|||
const loadServiceLogsList = (page: number = 1) => { |
|||
studentCourseUsageTable.loading = true |
|||
studentCourseUsageTable.page = page |
|||
|
|||
getServiceLogsList({ |
|||
page: studentCourseUsageTable.page, |
|||
limit: studentCourseUsageTable.limit, |
|||
...studentCourseUsageTable.searchParam, |
|||
}) |
|||
.then((res) => { |
|||
studentCourseUsageTable.loading = false |
|||
studentCourseUsageTable.data = res.data.data |
|||
studentCourseUsageTable.total = res.data.total |
|||
}) |
|||
.catch(() => { |
|||
studentCourseUsageTable.loading = false |
|||
}) |
|||
} |
|||
loadServiceLogsList() |
|||
|
|||
|
|||
const resetForm = (formEl: FormInstance | undefined) => { |
|||
if (!formEl) return |
|||
formEl.resetFields() |
|||
} |
|||
</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> |
|||
@ -0,0 +1,39 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\adminapi\controller\service_logs; |
|||
|
|||
use app\service\admin\service_logs\ServiceLogsService; |
|||
use core\base\BaseAdminController; |
|||
use app\service\admin\service\ServiceService; |
|||
|
|||
|
|||
/** |
|||
* 服务控制器 |
|||
* Class Service |
|||
* @package app\adminapi\controller\service |
|||
*/ |
|||
class ServiceLogs extends BaseAdminController |
|||
{ |
|||
/** |
|||
* 获取服务列表 |
|||
* @return \think\Response |
|||
*/ |
|||
public function lists(){ |
|||
|
|||
$data = $this->request->params([ |
|||
["name",""], |
|||
["score",""] |
|||
]); |
|||
return success((new ServiceLogsService())->getPage($data)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
use think\facade\Route; |
|||
|
|||
use app\adminapi\middleware\AdminCheckRole; |
|||
use app\adminapi\middleware\AdminCheckToken; |
|||
use app\adminapi\middleware\AdminLog; |
|||
|
|||
|
|||
|
|||
// USER_CODE_BEGIN -- service |
|||
|
|||
Route::group('service_logs', function () { |
|||
|
|||
Route::get('service_logs', 'service_logs.ServiceLogs/lists'); |
|||
|
|||
})->middleware([ |
|||
AdminCheckToken::class, |
|||
AdminCheckRole::class, |
|||
AdminLog::class |
|||
]); |
|||
// USER_CODE_END -- service |
|||
@ -0,0 +1,57 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\model\class_personnel_rel; |
|||
|
|||
use core\base\BaseModel; |
|||
use think\model\concern\SoftDelete; |
|||
use think\model\relation\HasMany; |
|||
use think\model\relation\HasOne; |
|||
use app\model\student\Student; |
|||
use app\model\student_courses\StudentCourses; |
|||
use app\model\class_grade\ClassGrade; |
|||
|
|||
use app\model\customer_resources\CustomerResources; |
|||
|
|||
use app\model\campus\Campus; |
|||
|
|||
class ClassPersonnelRel extends BaseModel |
|||
{ |
|||
|
|||
|
|||
|
|||
/** |
|||
* 数据表主键 |
|||
* @var string |
|||
*/ |
|||
protected $pk = 'id'; |
|||
|
|||
/** |
|||
* 模型名称 |
|||
* @var string |
|||
*/ |
|||
protected $name = 'class_personnel_rel'; |
|||
|
|||
|
|||
public function student(){ |
|||
return $this->hasOne(Student::class, 'id', 'source_id'); |
|||
} |
|||
|
|||
public function studentCourses(){ |
|||
return $this->hasOne(StudentCourses::class, 'student_id', 'source_id')->joinType('left')->withField('end_date,student_id')->bind(['end_date'=>'end_date']); |
|||
} |
|||
|
|||
public function studentCoursesInfo(){ |
|||
return $this->hasOne(StudentCourses::class, 'student_id', 'source_id'); |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\model\service_logs; |
|||
|
|||
use core\base\BaseModel; |
|||
use think\model\concern\SoftDelete; |
|||
use think\model\relation\HasMany; |
|||
use think\model\relation\HasOne; |
|||
|
|||
/** |
|||
* 服务模型 |
|||
* Class Service |
|||
* @package app\model\service |
|||
*/ |
|||
class ServiceLogs extends BaseModel |
|||
{ |
|||
|
|||
/** |
|||
* 数据表主键 |
|||
* @var string |
|||
*/ |
|||
protected $pk = 'id'; |
|||
|
|||
/** |
|||
* 模型名称 |
|||
* @var string |
|||
*/ |
|||
protected $name = 'service_logs'; |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\service\admin\service_logs; |
|||
|
|||
|
|||
use app\model\service_logs\ServiceLogs; |
|||
use core\base\BaseAdminService; |
|||
|
|||
|
|||
/** |
|||
* 学员课时消费记录服务层 |
|||
* Class StudentCourseUsageService |
|||
* @package app\service\admin\student_course_usage |
|||
*/ |
|||
class ServiceLogsService extends BaseAdminService |
|||
{ |
|||
public function __construct() |
|||
{ |
|||
parent::__construct(); |
|||
$this->model = new ServiceLogs(); |
|||
} |
|||
|
|||
/** |
|||
* 获取学员课时消费记录列表 |
|||
* @return array |
|||
*/ |
|||
public function getPage(array $data = []) |
|||
{ |
|||
$where = []; |
|||
if($data['name']){ |
|||
$where[] = ['b.name','like','%'.$data['name'].'%']; |
|||
} |
|||
|
|||
if($data['score']){ |
|||
$where[] = ['a.score','=',$data['score']]; |
|||
} |
|||
|
|||
$search_model = $this->model |
|||
->alias("a") |
|||
->join(['school_customer_resources' => 'b'],'a.resource_id = b.id','left') |
|||
->join(['school_personnel' => 'c'],'a.staff_id = c.id','left') |
|||
->join(['school_service' => 'd'],'a.service_id = d.id','left') |
|||
->where($where) |
|||
->field("a.*,b.name,c.name as staff_name,d.service_name,d.customer_confirmation") |
|||
->order("a.id desc"); |
|||
$list = $this->pageQuery($search_model); |
|||
return $list; |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
<?php |
|||
// +---------------------------------------------------------------------- |
|||
// | Niucloud-admin 企业快速开发的多应用管理平台 |
|||
// +---------------------------------------------------------------------- |
|||
// | 官方网址:https://www.niucloud.com |
|||
// +---------------------------------------------------------------------- |
|||
// | niucloud团队 版权所有 开源版本可自由商用 |
|||
// +---------------------------------------------------------------------- |
|||
// | Author: Niucloud Team |
|||
// +---------------------------------------------------------------------- |
|||
|
|||
namespace app\service\api\apiService; |
|||
|
|||
use app\model\class_grade\ClassGrade; |
|||
use core\base\BaseApiService; |
|||
|
|||
/** |
|||
* 考勤管理服务层 |
|||
* Class MemberService |
|||
* @package app\service\api\member |
|||
*/ |
|||
class jlClassService extends BaseApiService |
|||
{ |
|||
public function __construct() |
|||
{ |
|||
parent::__construct(); |
|||
$this->model = (new ClassGrade()); |
|||
} |
|||
|
|||
//列表 |
|||
public function list($id,$data) |
|||
{ |
|||
$order = 'id desc'; |
|||
$where = []; |
|||
if ($data['name'] !== '') { |
|||
$where[] = ['name','like','%'.$data['name'].'%']; |
|||
} |
|||
$search_model = $this->model->where('head_coach', $id)->where($where)->order($order) |
|||
->with(['classPersonnelRel' => function($query) { |
|||
$query->with(['student' => function($query) { |
|||
$query->with(['customerResources' => function($query) { |
|||
$query->with(['member' => function($query) { |
|||
$query->select(); |
|||
}]); |
|||
}]); |
|||
},'studentCourses']); |
|||
|
|||
},'personnelAll']); |
|||
$list = $this->pageQuery($search_model); |
|||
foreach ($list['data'] as &$v){ |
|||
if (count($v['classPersonnelRel']) > 0) { |
|||
$now = time(); |
|||
$count = 0; |
|||
foreach ($v['classPersonnelRel'] as $item) { |
|||
if (isset($item['end_date'])) { |
|||
$endTime = strtotime($item['end_date']); |
|||
if ($endTime > $now && $endTime <= $now + 7 * 86400) { |
|||
$count++; |
|||
} |
|||
} |
|||
} |
|||
$v['end_count'] = $count; |
|||
} else { |
|||
$v['end_count'] = 0; |
|||
} |
|||
} |
|||
return $list; |
|||
} |
|||
|
|||
|
|||
public function info($data) |
|||
{ |
|||
$search_model = $this->model->where('id', $data) |
|||
->with(['classPersonnelRel' => function($query) { |
|||
$query->with(['student' => function($query) { |
|||
$query->with(['customerResources' => function($query) { |
|||
$query->with(['member' => function($query) { |
|||
$query->select(); |
|||
}]); |
|||
}]); |
|||
},'studentCourses','studentCoursesInfo']); |
|||
},'personnelAll', 'personnelName']); |
|||
$list = $search_model->find(); |
|||
if (count($list['classPersonnelRel']) > 0) { |
|||
$now = time(); |
|||
$count = 0; |
|||
foreach ($list['classPersonnelRel'] as $item) { |
|||
if (isset($item['end_date'])) { |
|||
$endTime = strtotime($item['end_date']); |
|||
if ($endTime > $now && $endTime <= $now + 7 * 86400) { |
|||
$count++; |
|||
} |
|||
} |
|||
} |
|||
$v['end_count'] = $count; |
|||
} else { |
|||
$v['end_count'] = 0; |
|||
} |
|||
return $list; |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue