puhui999 1 жил өмнө
parent
commit
6d0e242387

+ 2 - 3
src/api/crm/business/index.ts

@@ -73,7 +73,6 @@ export const getBusinessListByIds = async (val: number[]) => {
 }
 
 // 商机转移
-// TODO @puhui999:transferBusiness
-export const transfer = async (data: TransferReqVO) => {
-  return await request.put({ url: '/crm/business/transfer', data })
+export const transferBusiness = async (data: TransferReqVO) => {
+  return await request.put({ url: '/crm/business/transfer-business', data })
 }

+ 6 - 0
src/api/crm/clue/index.ts

@@ -1,4 +1,5 @@
 import request from '@/config/axios'
+import { TransferReqVO } from '@/api/crm/customer'
 
 export interface ClueVO {
   id: number
@@ -44,3 +45,8 @@ export const deleteClue = async (id: number) => {
 export const exportClue = async (params) => {
   return await request.download({ url: `/crm/clue/export-excel`, params })
 }
+
+// 线索转移
+export const transferClue = async (data: TransferReqVO) => {
+  return await request.put({ url: '/crm/clue/transfer-clue', data })
+}

+ 2 - 2
src/api/crm/contact/index.ts

@@ -88,6 +88,6 @@ export const deleteContactBusinessList = async (data: ContactBusinessReqVO) => {
 }
 
 // 联系人转移
-export const transfer = async (data: TransferReqVO) => {
-  return await request.put({ url: '/crm/contact/transfer', data })
+export const transferContact = async (data: TransferReqVO) => {
+  return await request.put({ url: '/crm/contact/transfer-contact', data })
 }

+ 5 - 4
src/api/crm/contract/index.ts

@@ -7,6 +7,7 @@ export interface ContractVO {
   name: string
   customerId: number
   businessId: number
+  businessName: string
   processInstanceId: number
   orderDate: Date
   ownerUserId: number
@@ -18,8 +19,9 @@ export interface ContractVO {
   productPrice: number
   contactId: number
   signUserId: number
+  signUserName: string
   contactLastTime: Date
-  status: number
+  auditStatus: number
   remark: string
   productItems: ProductExpandVO[]
   creatorName: string
@@ -71,7 +73,6 @@ export const handleApprove = async (id: number) => {
 }
 
 // 合同转移
-// TODO @puhui999:transfer 相关方法,这块要补充下;
-export const transfer = async (data: TransferReqVO) => {
-  return await request.put({ url: '/crm/contract/transfer', data })
+export const transferContract = async (data: TransferReqVO) => {
+  return await request.put({ url: '/crm/contract/transfer-contract', data })
 }

+ 2 - 2
src/api/crm/customer/index.ts

@@ -82,8 +82,8 @@ export interface TransferReqVO {
 }
 
 // 客户转移
-export const transfer = async (data: TransferReqVO) => {
-  return await request.put({ url: '/crm/customer/transfer', data })
+export const transferCustomer = async (data: TransferReqVO) => {
+  return await request.put({ url: '/crm/customer/transfer-customer', data })
 }
 
 // 锁定/解锁客户

+ 1 - 1
src/components/Table/src/TableSelectForm.vue

@@ -1,4 +1,4 @@
-<!-- TODO @puhui999:这个最好加个注释哈 -->
+<!-- 列表选择通用组件,参考 ProductList 组件使用 -->
 <template>
   <Dialog v-model="dialogVisible" :appendToBody="true" :scroll="true" :title="title" width="60%">
     <el-table

+ 1 - 1
src/views/crm/contact/detail/index.vue

@@ -79,7 +79,7 @@ const openForm = (type: string, id?: number) => {
 /** 联系人转移 */
 const crmTransferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 联系人转移表单 ref
 const transfer = () => {
-  crmTransferFormRef.value?.open('联系人转移', contact.value.id, ContactApi.transfer)
+  crmTransferFormRef.value?.open('联系人转移', contact.value.id, ContactApi.transferContact)
 }
 
 /** 获取操作日志 */

+ 4 - 3
src/views/crm/contract/ContractForm.vue

@@ -7,7 +7,7 @@
       :rules="formRules"
       label-width="110px"
     >
-      <el-row>
+      <el-row :gutter="20">
         <el-col :span="24" class="mb-10px">
           <CardTitle title="基本信息" />
         </el-col>
@@ -138,7 +138,7 @@
         </el-col>
         <el-col :span="12">
           <el-form-item label="产品总金额(元)" prop="productPrice">
-            <el-input v-model="formData.productPrice" placeholder="请输入产品总金额" />
+            {{ floatToFixed2(formData.productPrice) }}
           </el-form-item>
         </el-col>
         <el-col :span="24">
@@ -172,6 +172,7 @@ import * as ContactApi from '@/api/crm/contact'
 import * as BusinessApi from '@/api/crm/business'
 import ProductList from './components/ProductList.vue'
 import BPMLModel from '@/views/crm/contract/components/BPMLModel.vue'
+import { floatToFixed2 } from '@/utils'
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
@@ -191,7 +192,7 @@ const formRules = reactive({
 const formRef = ref() // 表单 Ref
 const BPMLModelRef = ref<InstanceType<typeof BPMLModel>>() // TODO @puhui999:这个变量不太对;另外,可以不做 bpm model 窗口,而是可以点击跳转到工作流详情里;
 
-// TODO @puhui999:加个注释哈
+// 监听合同商品变化,计算合同商品总价
 watch(
   () => formData.value.productItems,
   (val) => {

+ 35 - 9
src/views/crm/contract/components/ProductList.vue

@@ -66,14 +66,17 @@ import * as ProductApi from '@/api/crm/product'
 import { DICT_TYPE } from '@/utils/dict'
 import { fenToYuanFormat } from '@/utils/formatter'
 import { TableSelectForm } from '@/components/Table/index'
+import { floatToFixed2, yuanToFen } from '@/utils'
 
 defineOptions({ name: 'ProductList' })
-withDefaults(defineProps<{ modelValue: any[] }>(), { modelValue: () => [] })
+const props = withDefaults(defineProps<{ modelValue: ProductApi.ProductExpandVO[] }>(), {
+  modelValue: () => []
+})
 const emits = defineEmits<{
   (e: 'update:modelValue', v: any[]): void
 }>()
 
-const list = ref<ProductApi.ProductExpandVO[]>([]) //  TODO @puhui999
+const list = ref<ProductApi.ProductExpandVO[]>([]) // 列表数量
 const multipleSelection = ref<ProductApi.ProductExpandVO[]>([]) // 多选
 
 /** 处理删除 */
@@ -92,12 +95,30 @@ const openForm = () => {
 
 /** 计算 totalPrice */
 const getTotalPrice = computed(() => (row: ProductApi.ProductExpandVO) => {
-  const totalPrice = (row.price * row.count * row.discountPercent) / 100
-  row.totalPrice = isNaN(totalPrice) ? 0 : totalPrice
-  return isNaN(totalPrice) ? 0 : totalPrice
+  const totalPrice =
+    (Number(row.price) / 100) * Number(row.count) * (1 - Number(row.discountPercent) / 100)
+  row.totalPrice = isNaN(totalPrice) ? 0 : yuanToFen(totalPrice)
+  return isNaN(totalPrice) ? 0 : totalPrice.toFixed(2)
 })
-
-// TODO @puhui999:注释下
+const isSetListValue = ref(false) // 判断是否已经给 list 赋值过,用于编辑表单商品回显
+// 编辑时合同商品回显
+watch(
+  () => props.modelValue,
+  (val) => {
+    if (!val || val.length === 0 || isSetListValue.value) {
+      return
+    }
+    list.value = [
+      ...props.modelValue.map((item) => {
+        item.totalPrice = floatToFixed2(item.totalPrice) as unknown as number
+        return item
+      })
+    ]
+    isSetListValue.value = true
+  },
+  { immediate: true, deep: true }
+)
+// 监听列表变化,动态更新合同商品列表
 watch(
   list,
   (val) => {
@@ -109,15 +130,20 @@ watch(
   { deep: true }
 )
 
-// TODO @puhui999:注释下
+// 监听商品选择结果动态添加商品到列表中,如果商品存在则不放入列表中
 watch(
   multipleSelection,
   (val) => {
     if (!val || val.length === 0) {
       return
     }
+    // 过滤出不在列表中的商品
     const ids = list.value.map((item) => item.id)
-    list.value.push(...multipleSelection.value.filter((item) => ids.indexOf(item.id) === -1))
+    const productList = multipleSelection.value.filter((item) => ids.indexOf(item.id) === -1)
+    if (!productList || productList.length === 0) {
+      return
+    }
+    list.value.push(...productList)
   },
   { deep: true }
 )

+ 12 - 8
src/views/crm/contract/detail/ContractDetailsHeader.vue

@@ -1,4 +1,4 @@
-<!-- TODO @puhui999:这个组件的注释加下,方便大家打开就知道哈 -->
+<!-- 合同详情头部组件-->
 <template>
   <div>
     <div class="flex items-start justify-between">
@@ -17,17 +17,20 @@
   </div>
   <ContentWrap class="mt-10px">
     <el-descriptions :column="5" direction="vertical">
-      <el-descriptions-item label="客户">
+      <el-descriptions-item label="客户名称">
         {{ contract.customerName }}
       </el-descriptions-item>
-      <el-descriptions-item label="客户签约人">
-        {{ contract.contactName }}
+      <el-descriptions-item label="合同金额(元)">
+        {{ floatToFixed2(contract.price) }}
       </el-descriptions-item>
-      <el-descriptions-item label="合同金额">
-        {{ contract.productPrice }}
+      <el-descriptions-item label="下单时间">
+        {{ contract.orderDate ? formatDate(contract.orderDate) : '空' }}
       </el-descriptions-item>
-      <el-descriptions-item label="创建时间">
-        {{ contract.createTime ? formatDate(contract.createTime) : '空' }}
+      <el-descriptions-item label="回款金额(元)">
+        {{ floatToFixed2(contract.price) }}
+      </el-descriptions-item>
+      <el-descriptions-item label="负责人">
+        {{ contract.ownerUserName }}
       </el-descriptions-item>
     </el-descriptions>
   </ContentWrap>
@@ -35,6 +38,7 @@
 <script lang="ts" setup>
 import * as ContractApi from '@/api/crm/contract'
 import { formatDate } from '@/utils/formatTime'
+import { floatToFixed2 } from '@/utils'
 
 defineOptions({ name: 'ContractDetailsHeader' })
 defineProps<{ contract: ContractApi.ContractVO }>()

+ 32 - 3
src/views/crm/contract/detail/ContractDetailsInfo.vue

@@ -1,4 +1,4 @@
-<!-- TODO @puhui999:这个组件的注释加下,方便大家打开就知道哈 -->
+<!-- 合同详情组件 -->
 <template>
   <ContentWrap>
     <el-collapse v-model="activeNames">
@@ -6,14 +6,43 @@
         <template #title>
           <span class="text-base font-bold">基本信息</span>
         </template>
-        <!-- TODO puhui999: 先出详情样式后补全 -->
-        <el-descriptions :column="4">
+        <el-descriptions :column="3">
+          <el-descriptions-item label="合同编号">
+            {{ contract.no }}
+          </el-descriptions-item>
           <el-descriptions-item label="合同名称">
             {{ contract.name }}
           </el-descriptions-item>
+          <el-descriptions-item label="客户名称">
+            {{ contract.customerName }}
+          </el-descriptions-item>
+          <el-descriptions-item label="商机名称">
+            {{ contract.businessName }}
+          </el-descriptions-item>
+          <el-descriptions-item label="合同金额(元)">
+            {{ contract.price }}
+          </el-descriptions-item>
+          <el-descriptions-item label="下单时间">
+            {{ formatDate(contract.orderDate) }}
+          </el-descriptions-item>
+          <el-descriptions-item label="开始时间">
+            {{ formatDate(contract.startTime) }}
+          </el-descriptions-item>
+          <el-descriptions-item label="结束时间">
+            {{ formatDate(contract.endTime) }}
+          </el-descriptions-item>
+          <el-descriptions-item label="客户签约人">
+            {{ contract.contactName }}
+          </el-descriptions-item>
+          <el-descriptions-item label="公司签约人">
+            {{ contract.signUserName }}
+          </el-descriptions-item>
           <el-descriptions-item label="备注">
             {{ contract.remark }}
           </el-descriptions-item>
+          <el-descriptions-item label="合同状态">
+            {{ contract.auditStatus }}
+          </el-descriptions-item>
         </el-descriptions>
       </el-collapse-item>
       <el-collapse-item name="systemInfo">

+ 64 - 0
src/views/crm/contract/detail/ContractProductList.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-table :data="list" :show-overflow-tooltip="true" :stripe="true">
+    <el-table-column align="center" label="产品名称" prop="name" width="160" />
+    <el-table-column align="center" label="产品类型" prop="categoryName" width="160" />
+    <el-table-column align="center" label="产品单位" prop="unit">
+      <template #default="scope">
+        <dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
+      </template>
+    </el-table-column>
+    <el-table-column align="center" label="产品编码" prop="no" />
+    <el-table-column
+      :formatter="fenToYuanFormat"
+      align="center"
+      label="价格(元)"
+      prop="price"
+      width="100"
+    />
+    <el-table-column align="center" label="数量" prop="count" width="200" />
+    <el-table-column align="center" label="折扣(%)" prop="discountPercent" width="200" />
+    <el-table-column align="center" label="合计" prop="totalPrice" width="100">
+      <template #default="{ row }: { row: ProductApi.ProductExpandVO }">
+        {{ getTotalPrice(row) }}
+      </template>
+    </el-table-column>
+  </el-table>
+</template>
+
+<script lang="ts" setup>
+import { DICT_TYPE } from '@/utils/dict'
+import { fenToYuanFormat } from '@/utils/formatter'
+import * as ProductApi from '@/api/crm/product'
+import { floatToFixed2, yuanToFen } from '@/utils'
+
+defineOptions({ name: 'ContractProductList' })
+const props = withDefaults(defineProps<{ modelValue: ProductApi.ProductExpandVO[] }>(), {
+  modelValue: () => []
+})
+const list = ref<ProductApi.ProductExpandVO[]>([]) // 列表数量
+/** 计算 totalPrice */
+const getTotalPrice = computed(() => (row: ProductApi.ProductExpandVO) => {
+  const totalPrice =
+    (Number(row.price) / 100) * Number(row.count) * (1 - Number(row.discountPercent) / 100)
+  row.totalPrice = isNaN(totalPrice) ? 0 : yuanToFen(totalPrice)
+  return isNaN(totalPrice) ? 0 : totalPrice.toFixed(2)
+})
+const isSetListValue = ref(false) // 判断是否已经给 list 赋值过,用于编辑表单商品回显
+// 编辑时合同商品回显
+watch(
+  () => props.modelValue,
+  (val) => {
+    if (!val || val.length === 0 || isSetListValue.value) {
+      return
+    }
+    list.value = [
+      ...props.modelValue.map((item) => {
+        item.totalPrice = floatToFixed2(item.totalPrice) as unknown as number
+        return item
+      })
+    ]
+    isSetListValue.value = true
+  },
+  { immediate: true, deep: true }
+)
+</script>

+ 10 - 15
src/views/crm/contract/detail/index.vue

@@ -1,10 +1,10 @@
-<!-- TODO @puhui999:这个组件的注释加下,方便大家打开就知道哈 -->
+<!-- 合同详情页面组件-->
 <template>
   <ContractDetailsHeader v-loading="loading" :contract="contract">
     <el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', contract.id)">
       编辑
     </el-button>
-    <el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
+    <el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transferContract">
       转移
     </el-button>
   </ContractDetailsHeader>
@@ -13,6 +13,9 @@
       <el-tab-pane label="详细资料">
         <ContractDetailsInfo :contract="contract" />
       </el-tab-pane>
+      <el-tab-pane label="产品">
+        <ContractProductList v-model="contract.productItems" />
+      </el-tab-pane>
       <el-tab-pane label="操作日志">
         <OperateLogV2 :log-list="logList" />
       </el-tab-pane>
@@ -25,18 +28,11 @@
           @quit-team="close"
         />
       </el-tab-pane>
-      <el-tab-pane label="商机" lazy>
-        <BusinessList
-          :biz-id="contract.id!"
-          :biz-type="BizTypeEnum.CRM_CONTRACT"
-          :customer-id="contract.customerId"
-        />
-      </el-tab-pane>
     </el-tabs>
   </el-col>
   <!-- 表单弹窗:添加/修改 -->
   <ContractForm ref="formRef" @success="getContractData" />
-  <CrmTransferForm ref="crmTransferFormRef" @success="close" />
+  <CrmTransferForm ref="transferFormRef" @success="close" />
 </template>
 <script lang="ts" setup>
 import { useTagsViewStore } from '@/store/modules/tagsView'
@@ -44,12 +40,12 @@ import { OperateLogV2VO } from '@/api/system/operatelog'
 import * as ContractApi from '@/api/crm/contract'
 import ContractDetailsHeader from './ContractDetailsHeader.vue'
 import ContractDetailsInfo from './ContractDetailsInfo.vue'
+import ContractProductList from './ContractProductList.vue'
 import { BizTypeEnum } from '@/api/crm/permission'
 import { getOperateLogPage } from '@/api/crm/operateLog'
 import ContractForm from '@/views/crm/contract/ContractForm.vue'
 import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
 import PermissionList from '@/views/crm/permission/components/PermissionList.vue'
-import BusinessList from '@/views/crm/business/components/BusinessList.vue'
 
 defineOptions({ name: 'CrmContractDetail' })
 
@@ -91,10 +87,9 @@ const getOperateLog = async (contractId: number) => {
 }
 
 /** 转移 */
-// TODO @puhui999:transferFormRef 简洁一点哈
-const crmTransferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 合同转移表单 ref
-const transfer = () => {
-  crmTransferFormRef.value?.open('合同转移', contract.value.id, ContractApi.transfer)
+const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 合同转移表单 ref
+const transferContract = () => {
+  transferFormRef.value?.open('合同转移', contract.value.id, ContractApi.transferContract)
 }
 
 /** 关闭 */

+ 14 - 24
src/views/crm/contract/index.vue

@@ -57,54 +57,44 @@
   <!-- TODO 芋艿:各种字段要调整 -->
   <ContentWrap>
     <el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
-      <el-table-column align="center" label="合同编号" prop="id" />
-      <el-table-column align="center" label="合同名称" prop="name" />
-      <el-table-column align="center" label="客户名称" prop="customerId" />
-      <el-table-column align="center" label="商机名称" prop="businessId" />
-      <el-table-column align="center" label="工作流名称" prop="processInstanceId" />
+      <el-table-column align="center" label="合同编号" prop="no" width="130" />
+      <el-table-column align="center" label="合同名称" prop="name" width="130" />
+      <el-table-column align="center" label="合同状态" prop="auditStatus" width="130" />
+      <el-table-column align="center" label="客户名称" prop="customerName" width="130" />
+      <el-table-column align="center" label="商机名称" prop="businessName" width="130" />
+      <el-table-column align="center" label="合同金额(元)" prop="price" width="130" />
       <el-table-column
         :formatter="dateFormatter"
         align="center"
         label="下单时间"
         prop="orderDate"
-        width="180px"
+        width="180"
       />
-      <el-table-column align="center" label="负责人" prop="ownerUserId" />
-      <el-table-column align="center" label="合同编号" prop="no" />
       <el-table-column
         :formatter="dateFormatter"
         align="center"
         label="开始时间"
         prop="startTime"
-        width="180px"
+        width="180"
       />
       <el-table-column
         :formatter="dateFormatter"
         align="center"
         label="结束时间"
         prop="endTime"
-        width="180px"
+        width="180"
       />
-      <el-table-column align="center" label="合同金额" prop="price" />
-      <el-table-column align="center" label="整单折扣" prop="discountPercent" />
-      <el-table-column align="center" label="产品总金额" prop="productPrice" />
-      <el-table-column align="center" label="联系人" prop="contactId" />
-      <el-table-column align="center" label="公司签约人" prop="signUserId" />
+      <el-table-column align="center" label="客户签约人" prop="contactName" width="130" />
+      <el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />
+      <el-table-column align="center" label="备注" prop="remark" width="130" />
+      <el-table-column align="center" label="审核状态" prop="auditStatus" width="130" />
       <el-table-column
         :formatter="dateFormatter"
         align="center"
         label="最后跟进时间"
         prop="contactLastTime"
-        width="180px"
+        width="180"
       />
-      <el-table-column
-        :formatter="dateFormatter"
-        align="center"
-        label="创建时间"
-        prop="createTime"
-        width="180px"
-      />
-      <el-table-column align="center" label="备注" prop="remark" />
       <el-table-column fixed="right" label="操作" width="250">
         <template #default="scope">
           <el-button

+ 1 - 1
src/views/crm/customer/detail/index.vue

@@ -124,7 +124,7 @@ const openForm = () => {
 /** 客户转移 */
 const crmTransferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 客户转移表单 ref
 const transfer = () => {
-  crmTransferFormRef.value?.open('客户转移', customerId.value, CustomerApi.transfer)
+  crmTransferFormRef.value?.open('客户转移', customerId.value, CustomerApi.transferCustomer)
 }
 
 /** 锁定客户 */

+ 3 - 4
src/views/crm/customer/index.vue

@@ -208,7 +208,7 @@
 
   <!-- 表单弹窗:添加/修改 -->
   <CustomerForm ref="formRef" @success="getList" />
-  <CustomerImportForm ref="customerImportFormRef" @success="getList" />
+  <CustomerImportForm ref="importFormRef" @success="getList" />
 </template>
 
 <script lang="ts" setup>
@@ -341,10 +341,9 @@ const handleDelete = async (id: number) => {
 }
 
 /** 导入按钮操作 */
-// TODO @puhui999:importFormRef 简化下
-const customerImportFormRef = ref<InstanceType<typeof CustomerImportForm>>()
+const importFormRef = ref<InstanceType<typeof CustomerImportForm>>()
 const handleImport = () => {
-  customerImportFormRef.value?.open()
+  importFormRef.value?.open()
 }
 
 /** 导出按钮操作 */