Browse Source

Merge remote-tracking branch 'origin/master'

master
liutong 10 months ago
parent
commit
db49106eef
  1. 12
      admin/src/app/api/customer_resources.ts
  2. 8
      admin/src/app/views/customer_resource_changes/customer_resource_changes.vue
  3. 69
      admin/src/app/views/customer_resources/components/UserProfile.vue
  4. 122
      admin/src/app/views/customer_resources/components/log.vue
  5. 118
      admin/src/app/views/customer_resources/components/order_table.vue
  6. 131
      admin/src/app/views/customer_resources/components/student_courses.vue
  7. 29
      niucloud/app/adminapi/controller/customer_resources/CustomerResources.php
  8. 5
      niucloud/app/adminapi/route/customer_resources.php
  9. 19
      niucloud/app/common.php
  10. 1
      niucloud/app/model/customer_resources/CustomerResources.php
  11. 156
      niucloud/app/service/admin/customer_resources/CustomerResourcesService.php

12
admin/src/app/api/customer_resources.ts

@ -76,3 +76,15 @@ export function fpEdit(params: Record<string, any>) {
export function getWithCoachList(params: Record<string, any>) {
return request.get('customer_resources/coach_person', { params })
}
export function getLogList(params: Record<string, any>) {
return request.get('customer_resources/log_list', { params })
}
export function getStudentCoursesList(params: Record<string, any>) {
return request.get('customer_resources/student_courses', { params })
}
export function getOrderTableList(params: Record<string, any>) {
return request.get('customer_resources/order_table', { params })
}

8
admin/src/app/views/customer_resource_changes/customer_resource_changes.vue

@ -76,6 +76,12 @@ import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
const props = defineProps({
value: Number
})
console.log(props.value);
let customerResourceChangesTable = reactive({
page: 1,
limit: 10,
@ -83,7 +89,7 @@ let customerResourceChangesTable = reactive({
loading: true,
data: [],
searchParam: {
customer_resource_id: route.query.id,
customer_resource_id: props.value ? props.value : route.query.id,
},
})

69
admin/src/app/views/customer_resources/components/UserProfile.vue

@ -10,11 +10,14 @@
<!-- 顶部用户信息 -->
<el-card class="mb-4">
<div class="flex items-center">
<el-avatar :size="60" class="mr-4" />
<el-avatar :size="60" class="mr-4" :src="user.headimg"/>
<div>
<h2 class="text-xl font-bold">{{ user.name }}</h2>
<p>ID: {{ user.id }}</p>
<p>电话: {{ user.phone }}</p>
<h2 class="text-xl font-bold">{{ user.nickname }}</h2>
<div style="display: flex; gap: 200px;margin-top: 10px;">
<p>ID: U{{ user.member_id }}</p>
<p>电话: {{ user.mobile }}</p>
<p>住址: {{ user.address }}</p>
</div>
</div>
</div>
</el-card>
@ -25,25 +28,28 @@
<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-col :span="12">需求购买力<el-tag>{{ info.purchasePower }}</el-tag></el-col>
<el-col :span="12">认知理念<el-tag>{{ info.knowledge }}</el-tag></el-col>
</el-row>
<el-row :gutter="20" class="mb-2">
<el-col :span="12">距离<span>{{ info.distance }}</span></el-col>
<el-col :span="12">可谈上课时间<span>{{ info.canTalkDate }}</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-col :span="12">承诺到访时间<span>{{ info.promisedVisitDate }}</span></el-col>
<el-col :span="12">实际到访时间<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-col :span="12">电话后的意向程度<el-tag>{{ info.phoneIntention }}</el-tag></el-col>
<el-col :span="12">是否关单
<el-tag :type="info.isClosed ? 'success' : 'danger'">{{ info.isClosed ? '是' : '否' }}</el-tag>
</el-col>
</el-row>
@ -61,22 +67,41 @@
</el-form-item>
</el-form>
</el-card>
<el-card v-if="activeTab === 'log'">
<log :value="user.id"/>
</el-card>
<el-card v-if="activeTab === 'student'">
<student :value="user.id"/>
</el-card>
<el-card v-if="activeTab === 'orders'">
<orders :value="user.id"/>
</el-card>
</el-dialog>
</template>
<script setup>
import { useDictionary } from '@/app/api/dict'
import { ref, reactive, computed, watch } from 'vue'
import Log from '@/app/views/customer_resources/components/log.vue'
import Student from '@/app/views/customer_resources/components/student_courses.vue'
import Orders from '@/app/views/customer_resources/components/order_table.vue'
let showDialog = ref(false)
const activeTab = ref('six-elements')
const user = reactive({
name: '王五1',
id: 'U001',
phone: '13914556707'
id: '',
member_id:'',
nickname: '',
headimg: '',
mobile:'',
address:''
})
const info = reactive({
@ -110,9 +135,15 @@
await useDictionary('cognitive_concept')
).data.dictionary
user.name = row.name;
user.id = row.id;
user.phone = row.phone_number;
user.nickname = row.member_info.nickname;
user.member_id = row.member_info.member_id;
user.headimg = row.member_info.headimg;
user.mobile = row.member_info.mobile;
user.address = row.member_info.address;
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;

122
admin/src/app/views/customer_resources/components/log.vue

@ -0,0 +1,122 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
</div>
<div class="mt-[10px]">
<el-table
:data="customerResourceChangesTable.data"
size="large"
v-loading="customerResourceChangesTable.loading"
>
<template #empty>
<span>{{
!customerResourceChangesTable.loading ? t('emptyData') : ''
}}</span>
</template>
<el-table-column prop="created_at" label="时间" />
<el-table-column prop="name" label="操作人员" />
<el-table-column prop="type" label="操作类型">
<template #default="scope">
<div v-html="scope.row.type"></div>
</template>
</el-table-column>
<el-table-column prop="values" label="详细信息">
<template #default="scope">
<div v-html="scope.row.values"></div>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="customerResourceChangesTable.page"
v-model:page-size="customerResourceChangesTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="customerResourceChangesTable.total"
@size-change="loadCustomerResourceChangesList()"
@current-change="loadCustomerResourceChangesList"
/>
</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 {
getLogList,
} from '@/app/api/customer_resources'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRoute } from 'vue-router'
const route = useRoute()
const pageName = route.meta.title
const props = defineProps({
value: Number
})
let customerResourceChangesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
customer_resource_id: props.value,
},
})
/**
* 获取客户资源表变更记录列表
*/
const loadCustomerResourceChangesList = (page: number = 1) => {
customerResourceChangesTable.loading = true
customerResourceChangesTable.page = page
getLogList({
page: customerResourceChangesTable.page,
limit: customerResourceChangesTable.limit,
...customerResourceChangesTable.searchParam,
})
.then((res) => {
customerResourceChangesTable.loading = false
customerResourceChangesTable.data = res.data.data
customerResourceChangesTable.total = res.data.total
})
.catch(() => {
customerResourceChangesTable.loading = false
})
}
loadCustomerResourceChangesList()
</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>

118
admin/src/app/views/customer_resources/components/order_table.vue

@ -0,0 +1,118 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
</div>
<div class="mt-[10px]">
<el-table
:data="customerResourceChangesTable.data"
size="large"
v-loading="customerResourceChangesTable.loading"
>
<template #empty>
<span>{{
!customerResourceChangesTable.loading ? t('emptyData') : ''
}}</span>
</template>
<el-table-column prop="payment_id" label="订单号" />
<el-table-column prop="course_name" label="课程名称" />
<el-table-column prop="order_amount" label="金额" />
<el-table-column prop="order_status" label="状态" />
<el-table-column prop="created_at" label="下单时间" />
<el-table-column prop="type" label="支付方式" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="customerResourceChangesTable.page"
v-model:page-size="customerResourceChangesTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="customerResourceChangesTable.total"
@size-change="loadCustomerResourceChangesList()"
@current-change="loadCustomerResourceChangesList"
/>
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import {
getOrderTableList,
} from '@/app/api/customer_resources'
import { useRoute } from 'vue-router'
const route = useRoute()
const props = defineProps({
value: Number
})
let customerResourceChangesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
customer_resource_id: props.value,
},
})
const getProgress = (row) => {
if (!row.total) return 0;
return Math.round((row.used / row.total) * 100);
}
/**
* 获取客户资源表变更记录列表
*/
const loadCustomerResourceChangesList = (page: number = 1) => {
customerResourceChangesTable.loading = true
customerResourceChangesTable.page = page
getOrderTableList({
page: customerResourceChangesTable.page,
limit: customerResourceChangesTable.limit,
...customerResourceChangesTable.searchParam,
})
.then((res) => {
customerResourceChangesTable.loading = false
customerResourceChangesTable.data = res.data.data
customerResourceChangesTable.total = res.data.total
})
.catch(() => {
customerResourceChangesTable.loading = false
})
}
loadCustomerResourceChangesList()
</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>

131
admin/src/app/views/customer_resources/components/student_courses.vue

@ -0,0 +1,131 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
</div>
<div class="mt-[10px]">
<el-table
:data="customerResourceChangesTable.data"
size="large"
v-loading="customerResourceChangesTable.loading"
>
<template #empty>
<span>{{
!customerResourceChangesTable.loading ? t('emptyData') : ''
}}</span>
</template>
<el-table-column prop="course_name" label="课程名称" />
<el-table-column label="学习进度">
<template #default="{ row }">
<div style="display: flex; align-items: center; justify-content: space-between;">
<el-progress
:percentage="getProgress(row)"
:text-inside="true"
stroke-width="18"
status="success"
style="flex: 1; max-width: 80%;"
/>
<span style="margin-left: 2px; white-space: nowrap; font-size: 14px; color: #666;">
{{ row.used }}/{{ row.total }}
</span>
</div>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" />
<el-table-column prop="start_date" label="开始时间" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="customerResourceChangesTable.page"
v-model:page-size="customerResourceChangesTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="customerResourceChangesTable.total"
@size-change="loadCustomerResourceChangesList()"
@current-change="loadCustomerResourceChangesList"
/>
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import {
getStudentCoursesList,
} from '@/app/api/customer_resources'
import { useRoute } from 'vue-router'
const route = useRoute()
const props = defineProps({
value: Number
})
let customerResourceChangesTable = reactive({
page: 1,
limit: 10,
total: 0,
loading: true,
data: [],
searchParam: {
customer_resource_id: props.value,
},
})
const getProgress = (row) => {
if (!row.total) return 0;
return Math.round((row.used / row.total) * 100);
}
/**
* 获取客户资源表变更记录列表
*/
const loadCustomerResourceChangesList = (page: number = 1) => {
customerResourceChangesTable.loading = true
customerResourceChangesTable.page = page
getStudentCoursesList({
page: customerResourceChangesTable.page,
limit: customerResourceChangesTable.limit,
...customerResourceChangesTable.searchParam,
})
.then((res) => {
customerResourceChangesTable.loading = false
customerResourceChangesTable.data = res.data.data
customerResourceChangesTable.total = res.data.total
})
.catch(() => {
customerResourceChangesTable.loading = false
})
}
loadCustomerResourceChangesList()
</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>

29
niucloud/app/adminapi/controller/customer_resources/CustomerResources.php

@ -41,6 +41,8 @@ class CustomerResources extends BaseAdminController
return success((new CustomerResourcesService())->getPage($data));
}
/**
* 客户资源详情
* @param int $id
@ -196,4 +198,31 @@ class CustomerResources extends BaseAdminController
{
return success((new CustomerResourcesService())->getResourceByCourse($schedule));
}
public function log_list()
{
$data = $this->request->params([
["customer_resource_id", ""]
]);
return success((new CustomerResourcesService())->log_list($data));
}
public function student_courses()
{
$data = $this->request->params([
["customer_resource_id", ""]
]);
return success((new CustomerResourcesService())->student_courses($data));
}
public function order_table()
{
$data = $this->request->params([
["customer_resource_id", ""]
]);
return success((new CustomerResourcesService())->order_table($data));
}
}

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

@ -47,6 +47,11 @@ Route::group('customer_resources', function () {
Route::get('getResourceByCourse/:schedule/students', 'customer_resources.CustomerResources/getResourceByCourse');
Route::get('log_list','customer_resources.CustomerResources/log_list');
Route::get('student_courses','customer_resources.CustomerResources/student_courses');
Route::get('order_table','customer_resources.CustomerResources/order_table');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,

19
niucloud/app/common.php

@ -1214,3 +1214,22 @@ function calculateChildHealthScore($age, $gender, $height, $weight) {
// 四舍五入取整
return round($total_score);
}
function get_dict_value($key,$value){
$dict = new \app\model\dict\Dict();
$field = 'id,name,key,dictionary,memo,create_time,update_time';
$info = $dict->field($field)->where([['key', '=', $key]])->findOrEmpty()->toArray();
if($info['dictionary'] == null)
{
$info['dictionary'] = [];
}
$map = [];
foreach ($info['dictionary'] as $item) {
$map[$item['value']] = $item['name'];
}
return $map[$value];
}

1
niucloud/app/model/customer_resources/CustomerResources.php

@ -89,6 +89,7 @@ class CustomerResources extends BaseModel
'updated_at' => '更新时间',
'deleted_at' => '逻辑删除时间',
'status' => '客户状态',
'member_label' => '资源标签'
];
public function orderTable()

156
niucloud/app/service/admin/customer_resources/CustomerResourcesService.php

@ -15,6 +15,8 @@ use app\model\campus_person_role\CampusPersonRole;
use app\model\course_schedule\CourseSchedule;
use app\model\customer_resource_changes\CustomerResourceChanges;
use app\model\customer_resources\CustomerResources;
use app\model\member\Member;
use app\model\member\MemberLabel;
use app\model\order_table\OrderTable;
use app\model\personnel\Personnel;
use app\model\campus\Campus;
@ -22,6 +24,7 @@ use app\model\campus\Campus;
use app\model\resource_sharing\ResourceSharing;
use app\model\six_speed\SixSpeed;
use app\model\six_speed_modification_log\SixSpeedModificationLog;
use app\model\student_courses\StudentCourses;
use app\service\admin\member\MemberLabelService;
use core\base\BaseAdminService;
@ -99,9 +102,17 @@ class CustomerResourcesService extends BaseAdminService
$sixSpeed = new SixSpeed();
$data = $sixSpeed->where(['resource_id' => $item['id']])->findOrEmpty()->toArray();
$item['six'] = $data;
$member = new Member();
$member_info = $member->where(['member_id' => $item['member_id']])->findOrEmpty()->toArray();
$item['member_info'] = $member_info;
});
}
/**
* 获取客户资源信息
* @param int $id
@ -419,4 +430,149 @@ class CustomerResourcesService extends BaseAdminService
return $all;
}
public function log_list(array $data = [])
{
$customer_resource_changes = new CustomerResourceChanges();
$where = [];
if ($data['customer_resource_id']) {
$where[] = ['a.customer_resource_id', '=', $data['customer_resource_id']];
}
$search_model = $customer_resource_changes
->alias("a")
->join(['school_personnel' => 'b'], 'a.operator_id = b.id', 'left')
->where($where)
->field("a.*,b.name")
->order("a.id asc");
return $this->pageQuery($search_model, function ($item, $key) {
$fieldZhArr = CustomerResources::FieldZh;
$modified_fields = json_decode($item['modified_fields'], true);
$type = [];
foreach ($modified_fields as $key => $value) {
$type[] = $fieldZhArr[$value];
}
$item['type'] = implode("<br>",$type);
$old_values = json_decode($item['old_values'], true);
$new_values = json_decode($item['new_values'], true);
$values = [];
foreach ($old_values as $key => $value) {
$old_value = $this->fields($key,$value);
$new_value = $this->fields($key,$new_values[$key]);
$values[] = $old_value.'->'.$new_value;
}
$item['values'] = implode("<br>",$values);
});
}
public function student_courses(array $data = [])
{
$studentCourses = new StudentCourses();
$where = [];
if ($data['customer_resource_id']) {
$where[] = ['b.user_id', '=', $data['customer_resource_id']];
}
$search_model = $studentCourses
->alias("a")
->join(['school_student' => 'b'], 'a.student_id = b.id', 'left')
->join(['school_course' => 'c'], 'a.course_id = c.id', 'left')
->where($where)
->field("a.*,c.course_name")
->order("a.id desc");
return $this->pageQuery($search_model, function ($item, $key) {
$item['total'] = $item['total_hours'] + $item['gift_hours'];
$item['used'] = $item['use_total_hours'] + $item['use_gift_hours'];
$now = date('Y-m-d'); // 当前日期
if ($now < $item['start_date']) {
$item['status'] = '未进行';
} elseif ($now > $item['end_date']) {
$item['status'] = '已结束';
} else {
$item['status'] = '进行中';
}
});
}
public function order_table(array $data = [])
{
$orderTable = new OrderTable();
$where = [];
if ($data['customer_resource_id']) {
$where[] = ['a.resource_id', '=', $data['customer_resource_id']];
}
$search_model = $orderTable
->alias("a")
->join(['school_course' => 'b'], 'a.course_id = b.id', 'left')
->where($where)
->field("a.*,b.course_name")
->order("a.id desc");
return $this->pageQuery($search_model, function ($item, $key) {
$arr = ['cash' => '现金支付','scan_code' => '扫码支付','subscription' => '订阅支付'];
$item['type'] = $arr[$item['payment_type']];
$item['order_status'] = $item['order_status'] == 'pending' ? '待支付' : '已支付';
});
}
public function fields($filed,$value){
if(!$value){
return '空';
}
$campus = new Campus();
$member_label = new MemberLabel();
switch ($filed) {
case 'initial_intent':
return get_dict_value('preliminarycustomerintention',$value);
break;
case 'source':
return get_dict_value('source',$value);
break;
case 'purchasing_power':
return get_dict_value('customer_purchasing_power',$value);
break;
case 'cognitive_idea':
return get_dict_value('cognitive_concept',$value);
break;
case 'source_channel':
return get_dict_value('SourceChannel',$value);
break;
case 'status':
return get_dict_value('kh_status',$value);
break;
case 'member_label':
$label_name = $member_label->where('label_id','in',$value)->column('label_name');
return implode("-",$label_name);
break;
case 'campus':
return $campus->where(['id' => $value])->value("campus_name");
break;
default:
return $value;
}
}
}

Loading…
Cancel
Save