Prechádzať zdrojové kódy

提交客户管家代码

Lijy 11 mesiacov pred
rodič
commit
2872195e41

+ 5 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/api.ts

@@ -6,6 +6,11 @@ export const GETPERSONNEL = '/user/getSimpleActiveUserList'
 export const URL_TEMPLALE = `/sys-form/getListByCode${MOD}`
 export const URL_TABLELIST = `${PREFIX}/list`
 export const URL_EDITSAVE = `${PREFIX}/insertAndUpdate`
+export const URL_DETELER = `${PREFIX}/deleter`
+export const URL_RECYCLELIST = `${PREFIX}/deleteList`
+export const URL_ROWBACK = `${PREFIX}/rollback`
+export const URL_THOROUGHLYDETELE = `${PREFIX}/reallyDelete`
+export const URL_CLAIM = `${PREFIX}/claim`
 
 export const stageStatus = [
     { id: 1, name: "赢单", progress: "100%" },

+ 163 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/deteleTables.vue

@@ -0,0 +1,163 @@
+<template>
+    <el-dialog v-model="deteleBusinessDialogVisible" width="1000" :before-close="beForeCancel" :show-close="false"
+        top="10vh">
+        <template #header="{ close, titleId, titleClass }">
+            <div class="flex justify-between items-center border-b pb-3 dialog-header">
+                <h4 :id="titleId">客户回收站</h4>
+                <div>
+                    <el-button type="primary" v-loading="allLoading.batchRecoveryLoading"
+                        :disabled="batchTableData.length == 0" @click="batchOperation('恢复')">批量恢复</el-button>
+                    <el-button type="primary" v-loading="allLoading.batchDeteleLoading"
+                        :disabled="batchTableData.length == 0" @click="batchOperation('删除')">批量删除</el-button>
+                    <el-button @click="cancel()">取消</el-button>
+                </div>
+            </div>
+        </template>
+        <div class="h-[60vh] flex flex-col">
+            <div class="flex-1 w-full overflow-hidden">
+                <el-table ref="deteleTableRef" :data="deteleBusinessTable" border v-loading="allLoading.tableLoading"
+                    @selection-change="changeBatch" style="width: 100%;height: 100%;">
+                    <el-table-column type="selection" width="55" />
+                    <el-table-column prop="customName" label="客户名称" width="180"></el-table-column>
+                    <el-table-column prop="customSourceValue" label="客户来源" width="180"></el-table-column>
+                    <el-table-column prop="companyPhone" label="公司电话" width="180"></el-table-column>
+                    <el-table-column prop="email" label="邮箱" width="200"></el-table-column>
+                    <el-table-column prop="customerIndustryValue" label="客户行业" width="180"></el-table-column>
+                    <el-table-column prop="customerLevelValue" label="客户级别" width="180"></el-table-column>
+                    <el-table-column prop="inchargerName" label="负责人" width="190"></el-table-column>
+                    <el-table-column prop="creatorName" label="创建人" width="180"></el-table-column>
+                    <el-table-column prop="newCreateTime" label="创建时间" width="180"></el-table-column>
+                    <el-table-column label="操作" fixed="right" width="120">
+                        <template #default="scope">
+                            <el-button link type="primary" size="large"
+                                @click="operationItem(scope.row.id, scope.row.name, '恢复')">恢复</el-button>
+                            <el-button link type="danger" size="large"
+                                @click="operationItem(scope.row.id, scope.row.name, '删除')">删除</el-button>
+                        </template>
+                    </el-table-column>
+                </el-table>
+            </div>
+            <div class="flex justify-end pt-3">
+                <el-pagination layout="total, prev, pager, next, sizes" :page-size="tableForm.pageFrom"
+                    @size-change="handleSizeChange" @current-change="handleCurrentChange" :total="businessTotalTable"
+                    :hide-on-single-page="true" />
+            </div>
+        </div>
+    </el-dialog>
+</template>
+<script lang="ts" setup>
+import { post } from '@/utils/request';
+import { ref, reactive, onMounted, watchEffect, watch, inject } from 'vue'
+import { URL_RECYCLELIST, URL_ROWBACK, URL_THOROUGHLYDETELE } from '../api';
+import { ElTable } from 'element-plus';
+import { confirmAction } from '@/utils/tools';
+import { formatDate } from '@/utils/times';
+
+type operationType = '恢复' | '删除'
+
+const emits = defineEmits(['closeVisible']);
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const deteleBusinessTable = ref([])
+const deteleBusinessDialogVisible = ref(false)
+const businessTotalTable = ref(0)
+const batchTableData = ref([])
+const allLoading = reactive({
+    batchRecoveryLoading: false,
+    batchDeteleLoading: false,
+    tableLoading: false
+})
+
+const tableForm = reactive({
+    pageIndex: 1,
+    pageFrom: 10
+})
+
+const deteleTableRef = ref<InstanceType<typeof ElTable>>() // 线索table dom
+
+const props = defineProps<{
+    visibles: boolean
+}>()
+
+watch(() => props.visibles, (newVal) => {
+    deteleBusinessDialogVisible.value = newVal
+    if (newVal) {
+        getTableList()
+    }
+})
+
+function batchOperation(type: operationType) {
+    const value = batchTableData.value.map((item: any) => item.id).join(',')
+    const label = batchTableData.value.map((item: any) => item.name).join(',')
+    operationItem(value, label, type, true)
+}
+
+function operationItem(value: string | number, label: string, type: operationType, batch: boolean = false) {
+    confirmAction(`确定${batch ? '批量' : ''}${type}【${label}】客户吗?`).then(() => {
+        let url = type == '恢复' ? URL_ROWBACK : URL_THOROUGHLYDETELE
+        post(url, { ids: value }).then(res => {
+            if (res.code != 'ok') {
+                globalPopup?.showError(res.msg)
+                return
+            }
+            globalPopup?.showSuccess(`${type}成功`)
+            getTableList()
+            changeBatch(false)
+        }).catch((err) => {
+            globalPopup?.showError(err.message)
+        })
+    })
+}
+
+function changeBatch(flag: boolean = true) {
+    if (flag) {
+        batchTableData.value = deteleTableRef.value && deteleTableRef.value.getSelectionRows()
+    } else {
+        batchTableData.value = []
+        deteleTableRef.value && deteleTableRef.value.clearSelection()
+    }
+}
+
+function getTableList() {
+    allLoading.tableLoading = true
+    post(URL_RECYCLELIST, { ...tableForm }).then((res) => {
+        if (res.code == 'ok') {
+            const { data, total } = res.data
+            deteleBusinessTable.value = data.map((item: any) => {
+                return {
+                    ...item,
+                    expectedTransactionDate: formatDate(new Date(item.expectedTransactionDate))
+                }
+            })
+            businessTotalTable.value = total
+        }
+    }).finally(() => {
+        allLoading.tableLoading = false
+    })
+}
+
+function handleSizeChange(val: number) {
+    tableForm.pageIndex = 1
+    tableForm.pageFrom = val
+    getTableList()
+}
+
+function handleCurrentChange(val: number) {
+    tableForm.pageIndex = val
+    getTableList()
+}
+
+function cancel() {
+    emits('closeVisible', 'deteleCustomerVisible')
+}
+
+function beForeCancel(done: () => void) {
+    emits('closeVisible', 'deteleCustomerVisible')
+    done()
+}
+
+onMounted(() => {
+
+})
+
+</script>
+<style lang="scss" scoped></style>

+ 159 - 19
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/index.vue

@@ -53,15 +53,15 @@
       <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
         <div class="flex justify-end pb-3">
           <el-button type="primary" @click="editCustomer(false)">新建客户</el-button>
-          <el-button type="primary">批量转移</el-button>
-          <el-button type="primary">批量删除</el-button>
-          <el-button type="primary">回收站</el-button>
+          <el-button type="primary" :disabled="batchTableData.length <= 0">批量转移</el-button>
+          <el-button type="primary" @click="batchDeteleItem()" :disabled="batchTableData.length <= 0">批量删除</el-button>
+          <el-button type="primary" @click="showVisible('deteleCustomerVisible')">回收站</el-button>
           <el-button type="primary">导入</el-button>
           <el-button type="primary">导出</el-button>
         </div>
         <div class="flex-1 w-full overflow-hidden">
-          <el-table ref="clueTableRef" :data="customerTable" border v-loading="allLoading.customerTableLading"
-            style="width: 100%;height: 100%;">
+          <el-table ref="customerTableRef" :data="customerTable" border v-loading="allLoading.customerTableLading"
+            style="width: 100%;height: 100%;" @selection-change="changeBatch">
             <el-table-column type="selection" width="55" />
             <el-table-column prop="customName" label="客户名称" width="180">
               <template #default="scope">
@@ -81,15 +81,16 @@
             <el-table-column label="操作" fixed="right" width="200">
               <template #default="scope">
                 <el-button link type="primary" size="large" @click="editCustomer(scope.row)">编辑</el-button>
-                <el-button link type="primary" size="large">新建任务</el-button>
-                <el-button link type="danger" size="large">删除</el-button>
+                <el-button link type="primary" size="large" @click="newTask(scope.row)">新建任务</el-button>
+                <el-button link type="danger" size="large"
+                  @click="customerDeteleItem(scope.row.id, scope.row.customName)">删除</el-button>
               </template>
             </el-table-column>
           </el-table>
         </div>
         <div class="flex justify-end pt-3">
-          <el-pagination layout="total, prev, pager, next, sizes" :total="customerTotalTable"
-            :hide-on-single-page="true" />
+          <el-pagination layout="total, prev, pager, next, sizes" :total="customerTotalTable" :hide-on-single-page="true"
+            @size-change="handleSizeChange" @current-change="handleCurrentChange" />
         </div>
       </div>
     </div>
@@ -100,28 +101,67 @@
         <div class="flex justify-between items-center border-b pb-3 dialog-header">
           <h4 :id="titleId">{{ allText.editCustomerText }}</h4>
           <div>
-            <el-button type="primary" :loading="allLoading.editCustomerSaveLoading" @click="editCustomerSave(true)">保存并新建</el-button>
-            <el-button type="primary" :loading="allLoading.editCustomerSaveLoading" @click="editCustomerSave(false)">保存</el-button>
+            <el-button type="primary" :loading="allLoading.editCustomerSaveLoading"
+              @click="editCustomerSave(true)">保存并新建</el-button>
+            <el-button type="primary" :loading="allLoading.editCustomerSaveLoading"
+              @click="editCustomerSave(false)">保存</el-button>
             <el-button @click="closeVisible('editCustomerVisible')">取消</el-button>
           </div>
         </div>
       </template>
       <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
         <div class="ml-4 mr-4">
-          <GenerateForm ref="customerTemplateRef" :data="customerTemplate" :value="customerTemplateValue" />
+          <GenerateForm ref="customerTemplateRef" :data="customerTemplate" :value="customerTemplateValue"
+            :key="customerTemplateRefKey" v-loading="allLoading.customerTemplateRefLoading" />
         </div>
       </div>
     </el-dialog>
+
+    <!-- 批量转移 -->
+    <el-dialog v-model="allVisible.batchTransferVisible" width="600" :show-close="false" top="10vh">
+      <template #header="{ close, titleId, titleClass }">
+        <div class="flex justify-between items-center border-b pb-3 dialog-header">
+          <h4 :id="titleId">{{ allText.transferText }}</h4>
+          <div>
+            <el-button type="primary" v-loading="allLoading.transferLoading" @click="transferBusiness()">转移</el-button>
+            <el-button @click="allVisible.batchTransferVisible = false">取消</el-button>
+          </div>
+        </div>
+      </template>
+      <div class="scroll-bar m-6">
+        <div class="flex mb-4">
+          <div class="w-20 flex items-center justify-end pr-4">转移至:</div>
+          <el-select v-model="transferPersonnel" placeholder="请选择" class="flex1">
+            <el-option v-for="item in fixedData.Personnel" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </div>
+        <div class="pl-3 text-[#e94a4a]">转移后,将看不到此商机</div>
+      </div>
+    </el-dialog>
+
+
+    <!-- 任务 -->
+    <TaskModal :visible="allVisible.taskModalVisible" :edit-form="taskModalForm" :save-loading="taskLoading"
+      @close="closeVisible('taskModalVisible')" @submit="submitForm" :title="'新建任务'"
+      :disabled-list="['taskType', 'customId']" />
+
+    <!-- 回收站 -->
+    <DeteleBusiness :visibles="allVisible.deteleCustomerVisible" @closeVisible="closeVisible" />
   </div>
 </template>
 
 <script lang="ts" setup>
 import { ref, reactive, onMounted, inject } from "vue";
-import { MOD, GETSYSFILED, GETPERSONNEL, URL_TABLELIST, URL_TEMPLALE, URL_EDITSAVE } from './api.ts'
-import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate } from '@/utils/tools'
+import { MOD, GETSYSFILED, GETPERSONNEL, URL_TABLELIST, URL_TEMPLALE, URL_EDITSAVE, URL_DETELER, URL_CLAIM } from './api.ts'
+import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate, getTemplateKey, createTaskFromType, confirmAction } from '@/utils/tools'
 import { post, get } from "@/utils/request";
 import { useRouter, useRoute } from "vue-router";
 import { GenerateForm } from '@zmjs/form-design';
+import { createTask } from "@/components/TaskModal/taskFunction";
+import { ElTable } from "element-plus";
+
+import TaskModal from '@/components/TaskModal/index.vue'
+import DeteleBusiness from './component/deteleTables.vue'
 
 // 定义类型
 interface fixedDataInterface {
@@ -173,13 +213,19 @@ const customerTable = ref([]) // 线索table数据
 const customerTotalTable = ref(0) // 线索 table 数据总数
 const allLoading = reactive({
   customerTableLading: false,
-  editCustomerSaveLoading: false
+  editCustomerSaveLoading: false,
+  customerTemplateRefLoading: false,
+  transferLoading: false,
 })
 const allVisible = reactive({
   editCustomerVisible: false,
+  taskModalVisible: false,
+  deteleCustomerVisible: false,
+  batchTransferVisible: false,
 })
 const allText = reactive({
   editCustomerText: '新建客户',
+  transferText: '批量转移',
 })
 const fixedData = reactive({
   ClueSources: [] as fixedDataInterface[],
@@ -194,16 +240,79 @@ const customerTemplate = ref({
 })
 const customerTemplateRef = ref<typeof GenerateForm>() // 自定义表单dom
 const customerTemplateValue = ref({})
+const customerTemplateRefKey = ref(1)
+const taskModalForm = ref({})
+const taskLoading = ref<saveLoadingType>('1')
+const batchTableData = ref<any>([])
+const customerTableRef = ref<InstanceType<typeof ElTable>>()
+const transferPersonnel = ref('')
 
 
 // 定义方法
+
+function transferBusiness() {
+  const ids = batchTableData.value.map((item: any) => item.id).join(',')
+  allLoading.transferLoading = true
+  post(URL_CLAIM, { ids, inchargerId: transferPersonnel.value }).then(() => {
+    transferPersonnel.value = ''
+    globalPopup?.showSuccess('转移成功')
+    closeVisible('batchTransferVisible')
+    getCustomerTable()
+  }).finally(() => {
+    allLoading.transferLoading = false
+  })
+}
+
+function batchDeteleItem() {
+  const value = batchTableData.value.map((item: any) => item.id).join(',')
+  const label = batchTableData.value.map((item: any) => item.customName).join(',')
+  customerDeteleItem(value, label, true)
+}
+
+function customerDeteleItem(value: string | number, label: string, batch: boolean = false) {
+  confirmAction(`确定${batch ? '批量' : ''}删除【${label}】客户吗?`).then(() => {
+    post(URL_DETELER, { ids: value }).then(res => {
+      if (res.code != 'ok') {
+        globalPopup?.showError(res.msg)
+        return
+      }
+      globalPopup?.showSuccess('删除成功')
+      changeBatch(false)
+      getCustomerTable()
+    }).catch((err) => {
+      globalPopup?.showError(err.message)
+    })
+  })
+}
+
+function submitForm(submitData: any, isClose: boolean) {
+  taskLoading.value = '2'
+  createTask(submitData, isClose).then((res) => {
+    const { saveLoading, isClose } = res
+    taskLoading.value = saveLoading
+    allVisible.taskModalVisible = isClose
+    globalPopup?.showSuccess('新增成功')
+  }).catch((err) => {
+    const { saveLoading, isClose, message } = err
+    taskLoading.value = saveLoading
+    allVisible.taskModalVisible = isClose
+    globalPopup?.showError(message)
+  })
+}
+
+function newTask(item: any) {
+  const { id } = item
+  taskModalForm.value = { ...createTaskFromType(0), customId: id, }
+  showVisible('taskModalVisible')
+}
+
 function editCustomerSave(flag: boolean) {
   customerTemplateRef.value?.getData().then((res: any) => {
     allLoading.editCustomerSaveLoading = true
     post(URL_EDITSAVE, { ...res }).then((_res) => {
       allVisible.editCustomerVisible = flag
       globalPopup?.showSuccess('保存成功')
-      if(flag) {
+      if (flag) {
         customerTemplateRef.value?.reset()
       }
       getCustomerTable()
@@ -218,14 +327,34 @@ function editCustomerSave(flag: boolean) {
 
 function editCustomer(row: any) { // row 有数据代表编辑
   if (row) {
-    const templateKey = customerTemplate.value.list.map((item: any) => item.model)
-    console.log(templateKey, '<==== key')
+    const { id, companyPhone, customName, inchargerId, createTime, customSourceId, customerIndustryId, customerLevelId, email } = row
+    const formVal = {
+      id, customName, inchargerId, customerIndustryId, customerLevelId, email, customSourceId,
+      createTime: formatDate(new Date(createTime)),
+      telPhone: companyPhone,
+    }
+    allLoading.customerTemplateRefLoading = true
+    setTimeout(() => {
+      customerTemplateRefKey.value++
+      allLoading.customerTemplateRefLoading = false
+    }, 1000);
+    customerTemplateValue.value = formVal
+    allText.editCustomerText = '编辑客户'
   } else { // 没有数据代表新建
-    customerTemplateRef.value?.reset()
+    allText.editCustomerText = '新增客户'
   }
   showVisible('editCustomerVisible')
 }
 
+function changeBatch(flag: boolean = true) {
+  if (flag) {
+    batchTableData.value = customerTableRef.value && customerTableRef.value.getSelectionRows()
+  } else {
+    batchTableData.value = []
+    customerTableRef.value && customerTableRef.value.clearSelection()
+  }
+}
+
 function toCustomerTableDetail(_row: any) {
   console.log('点击跳转详情')
   router.push({ path: `${MOD}/detail`, query: { id: _row.id } })
@@ -289,6 +418,17 @@ function closeVisible(type: keyof typeof allVisible) {
   allVisible[type] = false
 }
 
+function handleSizeChange(val: number) {
+  customerCriteriaForm.pageIndex = 1
+  customerCriteriaForm.pageFrom = val
+  getCustomerTable()
+}
+
+function handleCurrentChange(val: number) {
+  customerCriteriaForm.pageIndex = val
+  getCustomerTable()
+}
+
 onMounted(() => {
   getSystemField()
   getCustomerTable()

+ 4 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/type.d.ts

@@ -21,6 +21,10 @@ type ListByCodeType = (
   | "订单类型"
 )[];
 
+type templateKey = { // 自定义模板键值
+  [key: string]: any;
+}
+
 type saveLoadingType = "1" | "2" | "3" | "4"; //1是没有保存, 2是正在保存, 3是保存成功, 4是保存失败
 
 type TASK_VALUE_TYPE = 0 | 1 | 2 | 3; //0是客户, 1是商机, 2是销售订单 ,3是线索

+ 22 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/tools.ts

@@ -215,3 +215,25 @@ export function downloadTemplate(mod: any, fileName: string) {
     downloadFile(res.data, fileName)
   });
 }
+
+/**
+ * 取出自定义模板需要的 key
+ * @param list 自定义模板数据(list)
+ * @returns 
+ */
+export function getTemplateKey(list: Array<any>) {
+  const parsedList = list;
+  const models: Array<string> = [];
+  parsedList.forEach((item: any) => {
+    if (item.type === 'grid') {
+      item.columns.forEach((column: any) => {
+        column.list.forEach((subItem: any) => {
+          models.push(subItem.model);
+        });
+      });
+    } else {
+      models.push(item.model);
+    }
+  });
+  return models;
+}