Ver código fonte

feat:列表跟进显示

lizhi 3 meses atrás
pai
commit
710222d360

+ 44 - 0
protected/components/FollowSrv.php

@@ -0,0 +1,44 @@
+<?php
+
+class FollowSrv
+{
+    /**
+     * @param array $data
+     * @param string $table
+     * @param array $users [1 => ['username' => 'xxx', ...] ... ]
+     */
+    public static function formatWithFollowList(array $data, string $table, array $users = [])
+    {
+        $followIds = [];
+        foreach ($data as &$record) {
+            $record['follow_ids'] = $record['follow_ids'] ? explode(',', $record['follow_ids']) : [];
+            rsort($record['follow_ids']);
+            $record['follow_ids'] = array_slice($record['follow_ids'], 0, 5);
+            $followIds = Helper::concatArray($followIds, $record['follow_ids']);
+            $record['follow_list'] = [];
+        }
+        if ($followIds) {
+            $follows = Helper::arrayColumn(
+                DB::getListWithCriteria($table, DbCriteria::simpleCompare([
+                    'id' => $followIds
+                ])->setSelect('id, detail, user_id, create_date')),
+                null,
+                'id'
+            );
+            foreach ($data as &$record) {
+                foreach ($record['follow_ids'] as $followId) {
+                    if (isset($follows[$followId])) {
+                        $followInfo = $follows[$followId];
+                        $followInfo['avatar'] = !empty($users[$followInfo['user_id']]['avatar']) ? $users[$followInfo['user_id']]['avatar'] : '/zqcrm/avatar/20250926/9PLsci1dwXLcb8Iw.jpg';
+                        $followInfo['avatar'] = Helper::getImageUrl($followInfo['avatar'], '', 1);
+                        $followInfo['detail'] = strip_tags($followInfo['detail']);
+                        $followInfo['username'] = $users[$followInfo['user_id']]['username'] ?? '';
+                        $record['follow_list'][] = $followInfo;
+                    }
+                }
+                unset($record['follow_ids']);
+            }
+        }
+        return $data;
+    }
+}

+ 11 - 6
protected/controllers/SchoolController.php

@@ -41,10 +41,9 @@ class SchoolController extends Controller
 
     public function actionList()
     {
-        $filter['is_del'] = 0;
         $filter = [
             'is_del' => 0,
-            'id' => $this->getSchoolFilter()
+            't.id' => $this->getSchoolFilter()
         ];
         $address = Helper::getArrParam($_POST, 'address', Helper::PARAM_KEY_TYPE['array_string']);
         $filter['province'] = $address[0]?? null;
@@ -57,16 +56,22 @@ class SchoolController extends Controller
         if ($is_cooperate != -1) {
              $filter['is_cooperate'] = $is_cooperate;
         }
-        $cri = DbCriteria::simpleCompareWithPage($filter)->setOrder('id desc');
+        $cri = DbCriteria::simpleCompareWithPage($filter)->setAlias('t')
+            ->setSelect('t.*, group_concat(sf.id) AS follow_ids')
+            ->setJoin('LEFT JOIN wx_school_follow AS sf ON sf.school_id = t.id')
+            ->setGroup('t.id')
+            ->setOrder('t.id desc');
         $data = DB::getListWithCriteria(self::$table, $cri);
         if (!empty($data['records'])) {
+
             $users = Helper::arrayColumn(
-                DB::getListWithCriteria('useradmin', DbCriteria::simpleCompare([])->setSelect('id, username')),
-                'username',
+                DB::getListWithCriteria('useradmin', DbCriteria::simpleCompare([])->setSelect('id, username, avatar')),
+                null,
                 'id'
             );
+            $data['records'] = FollowSrv::formatWithFollowList($data['records'], 'wx_school_follow', $users);
             $data['records'] = array_map(function ($item) use ($users) {
-                $item['bind_user_name'] = $users[$item['bind_user_id']] ?? '-';
+                $item['bind_user_name'] = $users[$item['bind_user_id']]['username'] ?? '-';
                 return $item;
             }, $data['records']);
         }

+ 4 - 1
protected/controllers/SchoolRelationController.php

@@ -41,8 +41,10 @@ class SchoolRelationController extends Controller
         }
         $cri = DbCriteria::simpleCompareWithPage($filter)
             ->setAlias('r')
-            ->setSelect('r.*, s.name as school_name')
+            ->setSelect('r.*, s.name as school_name, group_concat(sf.id) AS follow_ids')
             ->setJoin('LEFT JOIN wx_school s ON s.id=r.school_id')
+            ->addJoin('LEFT JOIN wx_school_follow AS sf ON sf.school_id = r.id')
+            ->setGroup('r.id')
             ->setOrder('r.id desc');
         if ($date = Helper::getPostDate('date')) {
             $cri->addBetweenCondition('r.create_date', $date, $date . ' 23:59:59');
@@ -54,6 +56,7 @@ class SchoolRelationController extends Controller
                 'username',
                 'id'
             );
+            $data['records'] = FollowSrv::formatWithFollowList($data['records'], 'wx_school_follow');
             $data['records'] = array_map(function ($item) use ($users) {
                 $item['last_user_name'] = $users[$item['last_user_id']] ?? '-';
                 return $item;

+ 2 - 2
protected/include/Helper.php

@@ -44,7 +44,7 @@ class Helper
      * @author lizhi <1458705589@qq.com>
      * @date   2021/5/11
      */
-    public static function getImageUrl($imageUrl, string $default = '', $params = []): string
+    public static function getImageUrl($imageUrl, string $default = '', $isSmall = false): string
     {
         if (empty($imageUrl)) {
             return $default;
@@ -55,7 +55,7 @@ class Helper
         }
 
         $imageUrl = IMAGEDOMAIN . '/' . ltrim($imageUrl, '/');
-        if (self::getArrParam($params, 'from_type', 'string') == 'console') {
+        if ($isSmall) {
             // 把后端用到图片的地方,加个后缀,类似!max200 !width600这种
             $imageUrl .= '!max200';
         }

+ 6 - 1
web/src/components/custom/FollowDialog.vue

@@ -37,7 +37,12 @@
       </ElFormItem>
       <ElFormItem label="跟进详情" prop="detail">
         <!-- 富文本编辑器 -->
-        <ArtWangEditor class="el-top" v-model="formData.detail" height="300px" mode="simple"/>
+        <ArtWangEditor
+            class="el-top"
+            v-model="formData.detail"
+            height="300px"
+            mode="simple"
+        />
       </ElFormItem>
     </ElForm>
     <template #footer>

+ 32 - 0
web/src/components/custom/FollowProver.vue

@@ -0,0 +1,32 @@
+<template>
+  <el-popover
+      v-if="Array.isArray(follow_list) && follow_list.length > 0"
+      placement="top-start"
+      title=""
+      :width="width || 400"
+      trigger="hover"
+      content="this is content, this is content, this is content"
+  >
+    <template #reference>
+      <ElText truncated size="small" v-html="follow_list[0].detail"/>
+    </template>
+    <el-row v-for="item in follow_list" :key="item.id" style="margin-bottom: 5px;border-bottom: 1px solid #f5f1f1;">
+      <el-col :span="10" style="display: flex; align-items: center">
+        <ElAvatar :src="item.avatar.length" />
+        <el-text style="margin-left: 5px;">{{item.username}}</el-text>
+      </el-col>
+      <el-col :span="14"><el-text style="float: right;margin-top: 2px">{{item.create_date}}</el-text></el-col>
+      <el-col :span="24">
+        <ElText size="small" v-html="item.detail" />
+      </el-col>
+    </el-row>
+  </el-popover>
+</template>
+
+<script setup lang="ts">
+  interface Props {
+    follow_list: Api.Common.FollowItem[]
+    width?: number
+  }
+  defineProps<Props>()
+</script>

+ 9 - 0
web/src/typings/api.d.ts

@@ -39,6 +39,14 @@ declare namespace Api {
       total: number
     }
 
+    interface FollowItem {
+      id: number,
+      username: string,
+      avatar: string,
+      detail: string,
+      create_date: string,
+    }
+
     /** 分页响应基础结构 */
     interface PaginatedResponse<T = any> {
       records: T[]
@@ -261,6 +269,7 @@ declare namespace Api {
       qucan_station_distribution: string // 校门口取餐点离宿舍情况,
       out_business_description: string // 校外商圈情况,
       memo: string // 备注,
+      follow_list: Api.Common.FollowItem[]
     }
 
     interface SchoolContactItem {

+ 0 - 1
web/src/utils/http/index.ts

@@ -228,7 +228,6 @@ function getSign(formData: FormData) {
     }
   }).join('&') + 'qwer'
   queryString = queryString.replace(/[\r\n]+/g, '')
-  console.error(`%c queryString == `, 'background:#41b883 ; padding:1px; color:#fff', queryString);
   let sign = CryptoJS.SHA256(queryString).toString().toUpperCase()
   return sign
 }

+ 16 - 38
web/src/views/school/list/index.vue

@@ -35,34 +35,18 @@
           @pagination:size-change="handleSizeChange"
           @pagination:current-change="handleCurrentChange"
       >
-        <template #person_num="scope">
-          <ElInput
-              v-model="scope.row.person_num"
-              placeholder="scope.row.person_num"
-              @blur="doUpdateAttr(scope)"
-          />
-        </template>
-
-        <template #is_eleme_in_school="scope">
-          <ElSwitch
-              v-model="scope.row.is_eleme_in_school"
-              @change="doUpdateAttr(scope)"
-              :active-value="1"
-              :inactive-value="0"`
-          />
-        </template>
 
-        <template #is_eleme_out_school="scope">
-          <ElSwitch v-model="scope.row.is_eleme_out_school" @change="doUpdateAttr(scope)"/>
-        </template>
-
-        <template #is_meituan_in_school="scope">
-          <ElSwitch v-model="scope.row.is_meituan_in_school" @change="doUpdateAttr(scope)"/>
+        <template #follow_list="scope">
+          <el-row>
+            <el-col :sm="20">
+              <FollowProver :follow_list="scope.row.follow_list" />
+            </el-col>
+            <el-col :sm="4">
+              <ElButton size="small" @click="follow(scope.row)" v-ripple v-auth="120301" type="primary">跟进</ElButton>
+            </el-col>
+          </el-row>
         </template>
 
-        <template #is_meituan_out_school="scope">
-          <ElSwitch v-model="scope.row.is_meituan_out_school" @change="doUpdateAttr(scope)"/>
-        </template>
       </ArtTable>
 
       <!-- 用户弹窗 -->
@@ -84,7 +68,7 @@
         <span style="font-size: 20px; font-weight: bold;">{{ currentRow.name }}</span>
       </template>
       <ElRow>
-        <ElCol :sm="10">
+        <el-col :sm="10">
           <ElRow class="detail">
 
             <el-col :sm="12">
@@ -170,10 +154,10 @@
             </el-col>
 
           </ElRow>
-        </ElCol>
-        <ElCol :sm="14">
+        </el-col>
+        <el-col :sm="14">
           <FollowDrawer :first_id="currentRow.id" :second_id="0" type="school" :uid="drawerUid"/>
-        </ElCol>
+        </el-col>
       </ElRow>
     </el-drawer>
   </div>
@@ -193,6 +177,7 @@ import {followApi} from "@/api/followApi";
 import {schoolRelationApi} from "@/api/schoolRelationApi";
 import {commonApi} from "@/api/commonApi";
 import { detectDeviceType } from '@/utils'
+import FollowProver from "@/components/custom/FollowProver.vue";
 
 defineOptions({name: 'User'})
 
@@ -247,6 +232,7 @@ const drawerUid = ref(0)
 const handleDialogSubmit = async () => {
   followDialogVisible.value = false
   drawerUid.value++
+  getData()
 }
 
 const {
@@ -362,15 +348,7 @@ const {
           )
         }
       },
-      {
-        prop: '', label: '跟进记录', formatter: (row) => {
-          return h(ElButton, {
-            type: 'primary',
-            size: 'small',
-            onClick: () => follow(row),
-          }, () => '跟进')
-        }
-      },
+      {prop: 'follow_list', label: '跟进记录', useSlot: true, width: 400},
       { prop: 'dormitory_distribution', label: '宿舍分布情况', showOverflowTooltip: true },
       { prop: 'qucan_station_distribution', label: '校门口取餐点离宿舍情况', showOverflowTooltip: true },
       {prop: 'out_business_description', label: '校外商圈情况', showOverflowTooltip: true},

+ 14 - 9
web/src/views/school/relation/index.vue

@@ -30,6 +30,18 @@
         @pagination:size-change="handleSizeChange"
         @pagination:current-change="handleCurrentChange"
       >
+
+        <template #follow_list="scope">
+          <el-row>
+            <el-col :sm="20">
+              <FollowProver :follow_list="scope.row.follow_list" />
+            </el-col>
+            <el-col :sm="4">
+              <ElButton size="small" @click="follow(scope.row)" v-ripple v-auth="130301" type="primary">跟进</ElButton>
+            </el-col>
+          </el-row>
+        </template>
+
       </ArtTable>
 
       <!-- 学校关系弹窗 -->
@@ -111,6 +123,7 @@
   import {followApi} from "@/api/followApi";
   import {commonApi} from "@/api/commonApi";
   import { detectDeviceType } from '@/utils'
+  import FollowProver from "@/components/custom/FollowProver.vue";
 
   defineOptions({ name: 'schoolRelation' })
 
@@ -180,15 +193,7 @@
         { prop: 'phone', label: '手机号' },
         { prop: 'weixin', label: '微信号' },
         { prop: 'position', label: '职位' },
-        {
-          prop: '', label: '跟进记录', formatter: (row) => {
-            return h(ElButton, {
-              size: 'small',
-              type: 'primary',
-              onClick: () => follow(row),
-            }, () => '跟进')
-          }
-        },
+        {prop: 'follow_list', label: '跟进记录', useSlot: true, width: 400},
         { prop:'last_user_name', label:'最后一次跟进人' },
         { prop:'last_date', label:'最后一次跟进时间' },
         { prop: 'memo', label: '备注', showOverflowTooltip: true },