CRM-合同:新增合同详情
							parent
							
								
									90e0baf5bc
								
							
						
					
					
						commit
						668da2fa86
					
				| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
import request from '@/config/axios'
 | 
			
		||||
import { ProductExpandVO } from '@/api/crm/product'
 | 
			
		||||
import { TransferReqVO } from '@/api/crm/customer'
 | 
			
		||||
 | 
			
		||||
export interface ContractVO {
 | 
			
		||||
  id: number
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +22,12 @@ export interface ContractVO {
 | 
			
		|||
  status: number
 | 
			
		||||
  remark: string
 | 
			
		||||
  productItems: ProductExpandVO[]
 | 
			
		||||
  creatorName: string
 | 
			
		||||
  updateTime?: Date
 | 
			
		||||
  createTime?: Date
 | 
			
		||||
  customerName: string
 | 
			
		||||
  contactName: string
 | 
			
		||||
  ownerUserName: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 查询 CRM 合同列表
 | 
			
		||||
| 
						 | 
				
			
			@ -62,3 +69,8 @@ export const exportContract = async (params) => {
 | 
			
		|||
export const handleApprove = async (id: number) => {
 | 
			
		||||
  return await request.put({ url: `/crm/contract/approve?id=${id}` })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 合同转移
 | 
			
		||||
export const transfer = async (data: TransferReqVO) => {
 | 
			
		||||
  return await request.put({ url: '/crm/contract/transfer', data })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -507,6 +507,17 @@ const remainingRouter: AppRouteRecordRaw[] = [
 | 
			
		|||
        },
 | 
			
		||||
        component: () => import('@/views/crm/customer/detail/index.vue')
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: 'contract/detail/:id',
 | 
			
		||||
        name: 'CrmContractDetail',
 | 
			
		||||
        meta: {
 | 
			
		||||
          title: '合同详情',
 | 
			
		||||
          noCache: true,
 | 
			
		||||
          hidden: true,
 | 
			
		||||
          activeMenu: '/crm/contract'
 | 
			
		||||
        },
 | 
			
		||||
        component: () => import('@/views/crm/contract/detail/index.vue')
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: 'contact/detail/:id',
 | 
			
		||||
        name: 'CrmContactDetail',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@
 | 
			
		|||
    </el-tabs>
 | 
			
		||||
  </el-col>
 | 
			
		||||
  <!-- 表单弹窗:添加/修改 -->
 | 
			
		||||
  <ContactForm ref="formRef" @success="getContactData" />
 | 
			
		||||
  <ContactForm ref="formRef" @success="getContactData(contact.id)" />
 | 
			
		||||
  <CrmTransferForm ref="crmTransferFormRef" @success="close" />
 | 
			
		||||
</template>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +53,7 @@ import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
 | 
			
		|||
defineOptions({ name: 'CrmContactDetail' })
 | 
			
		||||
 | 
			
		||||
const route = useRoute()
 | 
			
		||||
const message = useMessage()
 | 
			
		||||
const id = Number(route.params.id) // 联系人编号
 | 
			
		||||
const loading = ref(true) // 加载中
 | 
			
		||||
const contact = ref<ContactApi.ContactVO>({} as ContactApi.ContactVO) // 联系人详情
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +103,7 @@ const { delView } = useTagsViewStore() // 视图操作
 | 
			
		|||
const { currentRoute } = useRouter() // 路由
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
  if (!id) {
 | 
			
		||||
    ElMessage.warning('参数错误,联系人不能为空!')
 | 
			
		||||
    message.warning('参数错误,联系人不能为空!')
 | 
			
		||||
    close()
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <div class="flex items-start justify-between">
 | 
			
		||||
      <div>
 | 
			
		||||
        <el-col>
 | 
			
		||||
          <el-row>
 | 
			
		||||
            <span class="text-xl font-bold">{{ contract.name }}</span>
 | 
			
		||||
          </el-row>
 | 
			
		||||
        </el-col>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div>
 | 
			
		||||
        <!-- 右上:按钮 -->
 | 
			
		||||
        <slot></slot>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <ContentWrap class="mt-10px">
 | 
			
		||||
    <el-descriptions :column="5" direction="vertical">
 | 
			
		||||
      <el-descriptions-item label="客户">
 | 
			
		||||
        {{ contract.customerName }}
 | 
			
		||||
      </el-descriptions-item>
 | 
			
		||||
      <el-descriptions-item label="客户签约人">
 | 
			
		||||
        {{ contract.contactName }}
 | 
			
		||||
      </el-descriptions-item>
 | 
			
		||||
      <el-descriptions-item label="合同金额">
 | 
			
		||||
        {{ contract.productPrice }}
 | 
			
		||||
      </el-descriptions-item>
 | 
			
		||||
      <el-descriptions-item label="创建时间">
 | 
			
		||||
        {{ contract.createTime ? formatDate(contract.createTime) : '空' }}
 | 
			
		||||
      </el-descriptions-item>
 | 
			
		||||
    </el-descriptions>
 | 
			
		||||
  </ContentWrap>
 | 
			
		||||
</template>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import * as ContractApi from '@/api/crm/contract'
 | 
			
		||||
import { formatDate } from '@/utils/formatTime'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'ContractDetailsHeader' })
 | 
			
		||||
defineProps<{ contract: ContractApi.ContractVO }>()
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <ContentWrap>
 | 
			
		||||
    <el-collapse v-model="activeNames">
 | 
			
		||||
      <el-collapse-item name="contractInfo">
 | 
			
		||||
        <template #title>
 | 
			
		||||
          <span class="text-base font-bold">基本信息</span>
 | 
			
		||||
        </template>
 | 
			
		||||
        <!-- TODO puhui999: 先出详情样式后补全 -->
 | 
			
		||||
        <el-descriptions :column="4">
 | 
			
		||||
          <el-descriptions-item label="合同名称">
 | 
			
		||||
            {{ contract.name }}
 | 
			
		||||
          </el-descriptions-item>
 | 
			
		||||
          <el-descriptions-item label="备注">
 | 
			
		||||
            {{ contract.remark }}
 | 
			
		||||
          </el-descriptions-item>
 | 
			
		||||
        </el-descriptions>
 | 
			
		||||
      </el-collapse-item>
 | 
			
		||||
      <el-collapse-item name="systemInfo">
 | 
			
		||||
        <template #title>
 | 
			
		||||
          <span class="text-base font-bold">系统信息</span>
 | 
			
		||||
        </template>
 | 
			
		||||
        <el-descriptions :column="2">
 | 
			
		||||
          <el-descriptions-item label="负责人">
 | 
			
		||||
            {{ contract.ownerUserName }}
 | 
			
		||||
          </el-descriptions-item>
 | 
			
		||||
          <el-descriptions-item label="创建人">
 | 
			
		||||
            {{ contract.creatorName }}
 | 
			
		||||
          </el-descriptions-item>
 | 
			
		||||
          <el-descriptions-item label="创建时间">
 | 
			
		||||
            {{ contract.createTime ? formatDate(contract.createTime) : '空' }}
 | 
			
		||||
          </el-descriptions-item>
 | 
			
		||||
          <el-descriptions-item label="更新时间">
 | 
			
		||||
            {{ contract.updateTime ? formatDate(contract.updateTime) : '空' }}
 | 
			
		||||
          </el-descriptions-item>
 | 
			
		||||
        </el-descriptions>
 | 
			
		||||
      </el-collapse-item>
 | 
			
		||||
    </el-collapse>
 | 
			
		||||
  </ContentWrap>
 | 
			
		||||
</template>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import * as ContractApi from '@/api/crm/contract'
 | 
			
		||||
import { formatDate } from '@/utils/formatTime'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'ContractDetailsInfo' })
 | 
			
		||||
defineProps<{
 | 
			
		||||
  contract: ContractApi.ContractVO
 | 
			
		||||
}>()
 | 
			
		||||
 | 
			
		||||
// 展示的折叠面板
 | 
			
		||||
const activeNames = ref(['contractInfo', 'systemInfo'])
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,111 @@
 | 
			
		|||
<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>
 | 
			
		||||
  </ContractDetailsHeader>
 | 
			
		||||
  <el-col>
 | 
			
		||||
    <el-tabs>
 | 
			
		||||
      <el-tab-pane label="详细资料">
 | 
			
		||||
        <ContractDetailsInfo :contract="contract" />
 | 
			
		||||
      </el-tab-pane>
 | 
			
		||||
      <el-tab-pane label="操作日志">
 | 
			
		||||
        <OperateLogV2 :log-list="logList" />
 | 
			
		||||
      </el-tab-pane>
 | 
			
		||||
      <el-tab-pane label="团队成员">
 | 
			
		||||
        <PermissionList
 | 
			
		||||
          ref="permissionListRef"
 | 
			
		||||
          :biz-id="contract.id!"
 | 
			
		||||
          :biz-type="BizTypeEnum.CRM_CONTRACT"
 | 
			
		||||
          :show-action="!permissionListRef?.isPool || false"
 | 
			
		||||
          @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" />
 | 
			
		||||
</template>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { useTagsViewStore } from '@/store/modules/tagsView'
 | 
			
		||||
import { OperateLogV2VO } from '@/api/system/operatelog'
 | 
			
		||||
import * as ContractApi from '@/api/crm/contract'
 | 
			
		||||
import ContractDetailsHeader from './ContractDetailsHeader.vue'
 | 
			
		||||
import ContractDetailsInfo from './ContractDetailsInfo.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' })
 | 
			
		||||
 | 
			
		||||
const route = useRoute()
 | 
			
		||||
const message = useMessage()
 | 
			
		||||
const contractId = ref(0) // 编号
 | 
			
		||||
const loading = ref(true) // 加载中
 | 
			
		||||
const contract = ref<ContractApi.ContractVO>({} as ContractApi.ContractVO) // 详情
 | 
			
		||||
/** 编辑 */
 | 
			
		||||
const formRef = ref()
 | 
			
		||||
const openForm = (type: string, id?: number) => {
 | 
			
		||||
  formRef.value.open(type, id)
 | 
			
		||||
}
 | 
			
		||||
/** 获取详情 */
 | 
			
		||||
const getContractData = async () => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    await getOperateLog(contractId.value)
 | 
			
		||||
    contract.value = await ContractApi.getContract(contractId.value)
 | 
			
		||||
  } finally {
 | 
			
		||||
    loading.value = false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 获取操作日志 */
 | 
			
		||||
const logList = ref<OperateLogV2VO[]>([]) // 操作日志列表
 | 
			
		||||
const getOperateLog = async (contractId: number) => {
 | 
			
		||||
  if (!contractId) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  const data = await getOperateLogPage({
 | 
			
		||||
    bizType: BizTypeEnum.CRM_CONTRACT,
 | 
			
		||||
    bizId: contractId
 | 
			
		||||
  })
 | 
			
		||||
  logList.value = data.list
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const crmTransferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 合同转移表单 ref
 | 
			
		||||
const transfer = () => {
 | 
			
		||||
  crmTransferFormRef.value?.open('合同转移', contract.value.id, ContractApi.transfer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
 | 
			
		||||
/** 初始化 */
 | 
			
		||||
const { delView } = useTagsViewStore() // 视图操作
 | 
			
		||||
const { currentRoute } = useRouter() // 路由
 | 
			
		||||
const close = () => {
 | 
			
		||||
  delView(unref(currentRoute))
 | 
			
		||||
}
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
  const id = route.params.id
 | 
			
		||||
  if (!id) {
 | 
			
		||||
    message.warning('参数错误,合同不能为空!')
 | 
			
		||||
    close()
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  contractId.value = id
 | 
			
		||||
  await getContractData()
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +105,7 @@
 | 
			
		|||
        width="180px"
 | 
			
		||||
      />
 | 
			
		||||
      <el-table-column align="center" label="备注" prop="remark" />
 | 
			
		||||
      <el-table-column label="操作" width="120px">
 | 
			
		||||
      <el-table-column fixed="right" label="操作" width="250">
 | 
			
		||||
        <template #default="scope">
 | 
			
		||||
          <el-button
 | 
			
		||||
            v-hasPermi="['crm:contract:update']"
 | 
			
		||||
| 
						 | 
				
			
			@ -119,10 +119,18 @@
 | 
			
		|||
            v-hasPermi="['crm:contract:update']"
 | 
			
		||||
            link
 | 
			
		||||
            type="primary"
 | 
			
		||||
            @click="handleApprove(scope.row.id)"
 | 
			
		||||
            @click="handleApprove(scope.row)"
 | 
			
		||||
          >
 | 
			
		||||
            提交审核
 | 
			
		||||
          </el-button>
 | 
			
		||||
          <el-button
 | 
			
		||||
            v-hasPermi="['crm:contract:query']"
 | 
			
		||||
            link
 | 
			
		||||
            type="primary"
 | 
			
		||||
            @click="openDetail(scope.row.id)"
 | 
			
		||||
          >
 | 
			
		||||
            详情
 | 
			
		||||
          </el-button>
 | 
			
		||||
          <el-button
 | 
			
		||||
            v-hasPermi="['crm:contract:delete']"
 | 
			
		||||
            link
 | 
			
		||||
| 
						 | 
				
			
			@ -233,11 +241,16 @@ const handleExport = async () => {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/** 提交审核 **/
 | 
			
		||||
const handleApprove = async (id: number) => {
 | 
			
		||||
  await ContractApi.handleApprove(id)
 | 
			
		||||
const handleApprove = async (row: ContractApi.ContractVO) => {
 | 
			
		||||
  await message.confirm(`您确定提交【${row.name}】审核吗?`)
 | 
			
		||||
  await ContractApi.handleApprove(row.id)
 | 
			
		||||
  message.success('提交审核成功!')
 | 
			
		||||
  await getList()
 | 
			
		||||
}
 | 
			
		||||
const { push } = useRouter()
 | 
			
		||||
const openDetail = (id: number) => {
 | 
			
		||||
  push({ name: 'CrmContractDetail', params: { id } })
 | 
			
		||||
}
 | 
			
		||||
/** 初始化 **/
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  getList()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue