mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-11-04 20:28:45 +08:00 
			
		
		
		
	@@ -67,3 +67,8 @@ export const exportCustomer = async (params) => {
 | 
				
			|||||||
export const queryAllList = async () => {
 | 
					export const queryAllList = async () => {
 | 
				
			||||||
  return await request.get({ url: `/crm/customer/query-all-list` })
 | 
					  return await request.get({ url: `/crm/customer/query-all-list` })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 查询客户操作日志
 | 
				
			||||||
 | 
					export const getOperateLogPage = async (params) => {
 | 
				
			||||||
 | 
					  return await request.get({ url: '/crm/customer/operate-log-page', params })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,32 @@ export type OperateLogVO = {
 | 
				
			|||||||
  resultData: string
 | 
					  resultData: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type OperateLogV2VO = {
 | 
				
			||||||
 | 
					  id: number
 | 
				
			||||||
 | 
					  userNickname: string
 | 
				
			||||||
 | 
					  traceId: string
 | 
				
			||||||
 | 
					  userType: number
 | 
				
			||||||
 | 
					  userId: number
 | 
				
			||||||
 | 
					  userName: string
 | 
				
			||||||
 | 
					  type: string
 | 
				
			||||||
 | 
					  subType: string
 | 
				
			||||||
 | 
					  bizId: number
 | 
				
			||||||
 | 
					  action: string
 | 
				
			||||||
 | 
					  extra: string
 | 
				
			||||||
 | 
					  requestMethod: string
 | 
				
			||||||
 | 
					  requestUrl: string
 | 
				
			||||||
 | 
					  userIp: string
 | 
				
			||||||
 | 
					  userAgent: string
 | 
				
			||||||
 | 
					  creator: string
 | 
				
			||||||
 | 
					  creatorName: string
 | 
				
			||||||
 | 
					  createTime: Date
 | 
				
			||||||
 | 
					  // 数据扩展-渲染时使用
 | 
				
			||||||
 | 
					  title: string // 操作标题(如果为空则取 name 值)
 | 
				
			||||||
 | 
					  colSize: number // 变更记录行数
 | 
				
			||||||
 | 
					  contentStrList: string[]
 | 
				
			||||||
 | 
					  tagsContentList: string[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 查询操作日志列表
 | 
					// 查询操作日志列表
 | 
				
			||||||
export const getOperateLogPage = (params: PageParam) => {
 | 
					export const getOperateLogPage = (params: PageParam) => {
 | 
				
			||||||
  return request.get({ url: '/system/operate-log/page', params })
 | 
					  return request.get({ url: '/system/operate-log/page', params })
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								src/components/OperateLogV2/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/components/OperateLogV2/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					import OperateLogV2 from './src/OperateLogV2.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { OperateLogV2 }
 | 
				
			||||||
							
								
								
									
										170
									
								
								src/components/OperateLogV2/src/OperateLogV2.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/components/OperateLogV2/src/OperateLogV2.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,170 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="p-20px">
 | 
				
			||||||
 | 
					    <el-timeline>
 | 
				
			||||||
 | 
					      <el-timeline-item
 | 
				
			||||||
 | 
					        v-for="(log, index) in logDataList"
 | 
				
			||||||
 | 
					        :key="index"
 | 
				
			||||||
 | 
					        :timestamp="formatDate(log.createTime)"
 | 
				
			||||||
 | 
					        placement="top"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <div class="el-timeline-right-content">
 | 
				
			||||||
 | 
					          <el-row>
 | 
				
			||||||
 | 
					            <el-col :span="24" class="mb-10px">
 | 
				
			||||||
 | 
					              =======================
 | 
				
			||||||
 | 
					              <el-tag class="mr-10px" type="success">{{ log.userName }}</el-tag>
 | 
				
			||||||
 | 
					              <span>{{ log.title }}</span>
 | 
				
			||||||
 | 
					              =======================
 | 
				
			||||||
 | 
					            </el-col>
 | 
				
			||||||
 | 
					            <!-- 先处理一下有几行-->
 | 
				
			||||||
 | 
					            <template v-for="colNum in log.colSize" :key="colNum + 'col'">
 | 
				
			||||||
 | 
					              <el-col :span="24" class="mb-10px">
 | 
				
			||||||
 | 
					                <!-- 处理每一行-->
 | 
				
			||||||
 | 
					                <template
 | 
				
			||||||
 | 
					                  v-for="(tagVal, index2) in log.tagsContentList.slice(
 | 
				
			||||||
 | 
					                    (colNum - 1) * 3,
 | 
				
			||||||
 | 
					                    3 * colNum
 | 
				
			||||||
 | 
					                  )"
 | 
				
			||||||
 | 
					                  :key="index2"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <el-tag class="mx-10px"> {{ tagVal }}</el-tag>
 | 
				
			||||||
 | 
					                  <span>{{ log.contentStrList[index2] }}</span>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					              </el-col>
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
 | 
					          </el-row>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <template #dot>
 | 
				
			||||||
 | 
					          <span :style="{ backgroundColor: getUserTypeColor(log.userType) }" class="dot-node-style">
 | 
				
			||||||
 | 
					            {{ getDictLabel(DICT_TYPE.USER_TYPE, log.userType)[0] }}
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        </template>
 | 
				
			||||||
 | 
					      </el-timeline-item>
 | 
				
			||||||
 | 
					    </el-timeline>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { OperateLogV2VO } from '@/api/system/operatelog'
 | 
				
			||||||
 | 
					import { formatDate } from '@/utils/formatTime'
 | 
				
			||||||
 | 
					import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
 | 
				
			||||||
 | 
					import { ElTag } from 'element-plus'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps<{
 | 
				
			||||||
 | 
					  logList: OperateLogV2VO[] // 操作日志列表
 | 
				
			||||||
 | 
					}>()
 | 
				
			||||||
 | 
					defineOptions({ name: 'OperateLogV2' })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** 获得 userType 颜色 */
 | 
				
			||||||
 | 
					const getUserTypeColor = (type: number) => {
 | 
				
			||||||
 | 
					  const dict = getDictObj(DICT_TYPE.USER_TYPE, type)
 | 
				
			||||||
 | 
					  switch (dict?.colorType) {
 | 
				
			||||||
 | 
					    case 'success':
 | 
				
			||||||
 | 
					      return '#67C23A'
 | 
				
			||||||
 | 
					    case 'info':
 | 
				
			||||||
 | 
					      return '#909399'
 | 
				
			||||||
 | 
					    case 'warning':
 | 
				
			||||||
 | 
					      return '#E6A23C'
 | 
				
			||||||
 | 
					    case 'danger':
 | 
				
			||||||
 | 
					      return '#F56C6C'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return '#409EFF'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const logDataList = ref<OperateLogV2VO[]>([]) // 操作日志列表
 | 
				
			||||||
 | 
					// 提取 tag 所需内容和位置
 | 
				
			||||||
 | 
					const renderTags = (content: string) => {
 | 
				
			||||||
 | 
					  let newStr = unref(content).slice() // 去掉引用
 | 
				
			||||||
 | 
					  newStr = newStr.replaceAll('【】', '【空】').replaceAll(';', '') // 处理掉分号 特殊:处理一下空的情况
 | 
				
			||||||
 | 
					  const regex = /【([^【】]+)】/g
 | 
				
			||||||
 | 
					  const fg = '|' // 原始位置替换符号
 | 
				
			||||||
 | 
					  let match: any[] | null
 | 
				
			||||||
 | 
					  let matchStr: string[] = []
 | 
				
			||||||
 | 
					  let oldStr: string[] = []
 | 
				
			||||||
 | 
					  while ((match = regex.exec(newStr)) !== null) {
 | 
				
			||||||
 | 
					    matchStr.push(match[1]) // 提取值
 | 
				
			||||||
 | 
					    oldStr.push(match[0]) // 原值
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // 为什么重新循环不放在 while 中一起是因为替换重新赋值过后 match 值就不准确了
 | 
				
			||||||
 | 
					  oldStr.forEach((item) => {
 | 
				
			||||||
 | 
					    newStr = newStr.replace(item, fg)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  return [newStr.split(fg), matchStr]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					const initLog = () => {
 | 
				
			||||||
 | 
					  logDataList.value = props.logList.map((logItem) => {
 | 
				
			||||||
 | 
					    const keyValue = renderTags(logItem.action)
 | 
				
			||||||
 | 
					    // 挂载数据
 | 
				
			||||||
 | 
					    logItem.contentStrList = keyValue[0]
 | 
				
			||||||
 | 
					    if (keyValue[0][0] === '从') {
 | 
				
			||||||
 | 
					      logItem.title = logItem.subType
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      logItem.title = keyValue[0][0]
 | 
				
			||||||
 | 
					      logItem.contentStrList.splice(0, 1)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    logItem.colSize = keyValue[0].length / 3 // 变更记录行数
 | 
				
			||||||
 | 
					    logItem.tagsContentList = keyValue[1]
 | 
				
			||||||
 | 
					    return logItem
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					  () => props.logList.length,
 | 
				
			||||||
 | 
					  (newObj) => {
 | 
				
			||||||
 | 
					    if (newObj) {
 | 
				
			||||||
 | 
					      initLog()
 | 
				
			||||||
 | 
					      console.log(logDataList.value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    immediate: true,
 | 
				
			||||||
 | 
					    deep: true
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
 | 
					// 时间线样式调整
 | 
				
			||||||
 | 
					:deep(.el-timeline) {
 | 
				
			||||||
 | 
					  margin: 10px 0 0 160px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .el-timeline-item__wrapper {
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    top: -20px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .el-timeline-item__timestamp {
 | 
				
			||||||
 | 
					      position: absolute !important;
 | 
				
			||||||
 | 
					      top: 10px;
 | 
				
			||||||
 | 
					      left: -150px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .el-timeline-right-content {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    min-height: 30px;
 | 
				
			||||||
 | 
					    padding: 10px;
 | 
				
			||||||
 | 
					    background-color: #fff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &::before {
 | 
				
			||||||
 | 
					      position: absolute;
 | 
				
			||||||
 | 
					      top: 10px;
 | 
				
			||||||
 | 
					      left: 13px; /* 将伪元素水平居中 */
 | 
				
			||||||
 | 
					      border-color: transparent #fff transparent transparent; /* 尖角颜色,左侧朝向 */
 | 
				
			||||||
 | 
					      border-style: solid;
 | 
				
			||||||
 | 
					      border-width: 8px; /* 调整尖角大小 */
 | 
				
			||||||
 | 
					      content: ''; /* 必须设置 content 属性 */
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.dot-node-style {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  left: -5px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  width: 20px;
 | 
				
			||||||
 | 
					  height: 20px;
 | 
				
			||||||
 | 
					  font-size: 10px;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  border-radius: 50%;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <Dialog :title="dialogTitle" v-model="dialogVisible">
 | 
					  <Dialog v-model="dialogVisible" :title="dialogTitle">
 | 
				
			||||||
    <el-form
 | 
					    <el-form
 | 
				
			||||||
      ref="formRef"
 | 
					      ref="formRef"
 | 
				
			||||||
 | 
					      v-loading="formLoading"
 | 
				
			||||||
      :model="formData"
 | 
					      :model="formData"
 | 
				
			||||||
      :rules="formRules"
 | 
					      :rules="formRules"
 | 
				
			||||||
      label-width="100px"
 | 
					      label-width="100px"
 | 
				
			||||||
      v-loading="formLoading"
 | 
					 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <el-row>
 | 
					      <el-row>
 | 
				
			||||||
        <el-col :span="12">
 | 
					        <el-col :span="12">
 | 
				
			||||||
@@ -17,7 +17,7 @@
 | 
				
			|||||||
          <el-form-item label="所属行业" prop="industryId">
 | 
					          <el-form-item label="所属行业" prop="industryId">
 | 
				
			||||||
            <el-select v-model="formData.industryId" placeholder="请选择所属行业">
 | 
					            <el-select v-model="formData.industryId" placeholder="请选择所属行业">
 | 
				
			||||||
              <el-option
 | 
					              <el-option
 | 
				
			||||||
                v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
 | 
					                v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
 | 
				
			||||||
                :key="dict.value"
 | 
					                :key="dict.value"
 | 
				
			||||||
                :label="dict.label"
 | 
					                :label="dict.label"
 | 
				
			||||||
                :value="dict.value"
 | 
					                :value="dict.value"
 | 
				
			||||||
@@ -31,7 +31,7 @@
 | 
				
			|||||||
          <el-form-item label="客户来源" prop="source">
 | 
					          <el-form-item label="客户来源" prop="source">
 | 
				
			||||||
            <el-select v-model="formData.source" placeholder="请选择客户来源">
 | 
					            <el-select v-model="formData.source" placeholder="请选择客户来源">
 | 
				
			||||||
              <el-option
 | 
					              <el-option
 | 
				
			||||||
                v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
 | 
					                v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
 | 
				
			||||||
                :key="dict.value"
 | 
					                :key="dict.value"
 | 
				
			||||||
                :label="dict.label"
 | 
					                :label="dict.label"
 | 
				
			||||||
                :value="dict.value"
 | 
					                :value="dict.value"
 | 
				
			||||||
@@ -43,7 +43,7 @@
 | 
				
			|||||||
          <el-form-item label="客户等级" prop="level">
 | 
					          <el-form-item label="客户等级" prop="level">
 | 
				
			||||||
            <el-select v-model="formData.level" placeholder="请选择客户等级">
 | 
					            <el-select v-model="formData.level" placeholder="请选择客户等级">
 | 
				
			||||||
              <el-option
 | 
					              <el-option
 | 
				
			||||||
                v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
 | 
					                v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
 | 
				
			||||||
                :key="dict.value"
 | 
					                :key="dict.value"
 | 
				
			||||||
                :label="dict.label"
 | 
					                :label="dict.label"
 | 
				
			||||||
                :value="dict.value"
 | 
					                :value="dict.value"
 | 
				
			||||||
@@ -120,9 +120,9 @@
 | 
				
			|||||||
          <el-form-item label="下次联系时间" prop="contactNextTime">
 | 
					          <el-form-item label="下次联系时间" prop="contactNextTime">
 | 
				
			||||||
            <el-date-picker
 | 
					            <el-date-picker
 | 
				
			||||||
              v-model="formData.contactNextTime"
 | 
					              v-model="formData.contactNextTime"
 | 
				
			||||||
 | 
					              placeholder="选择下次联系时间"
 | 
				
			||||||
              type="date"
 | 
					              type="date"
 | 
				
			||||||
              value-format="x"
 | 
					              value-format="x"
 | 
				
			||||||
              placeholder="选择下次联系时间"
 | 
					 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          </el-form-item>
 | 
					          </el-form-item>
 | 
				
			||||||
        </el-col>
 | 
					        </el-col>
 | 
				
			||||||
@@ -139,13 +139,13 @@
 | 
				
			|||||||
      </el-col>
 | 
					      </el-col>
 | 
				
			||||||
    </el-form>
 | 
					    </el-form>
 | 
				
			||||||
    <template #footer>
 | 
					    <template #footer>
 | 
				
			||||||
      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
 | 
					      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
 | 
				
			||||||
      <el-button @click="dialogVisible = false">取 消</el-button>
 | 
					      <el-button @click="dialogVisible = false">取 消</el-button>
 | 
				
			||||||
    </template>
 | 
					    </template>
 | 
				
			||||||
  </Dialog>
 | 
					  </Dialog>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 | 
					import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 | 
				
			||||||
import * as CustomerApi from '@/api/crm/customer'
 | 
					import * as CustomerApi from '@/api/crm/customer'
 | 
				
			||||||
import * as AreaApi from '@/api/system/area'
 | 
					import * as AreaApi from '@/api/system/area'
 | 
				
			||||||
import { defaultProps } from '@/utils/tree'
 | 
					import { defaultProps } from '@/utils/tree'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
        <el-button v-hasPermi="['crm:customer:update']" @click="openForm(customer.id)">
 | 
					        <el-button v-hasPermi="['crm:customer:update']" @click="openForm(customer.id)">
 | 
				
			||||||
          编辑
 | 
					          编辑
 | 
				
			||||||
        </el-button>
 | 
					        </el-button>
 | 
				
			||||||
 | 
					        <el-button @click="transfer">转移</el-button>
 | 
				
			||||||
        <el-button>更改成交状态</el-button>
 | 
					        <el-button>更改成交状态</el-button>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
@@ -26,22 +27,24 @@
 | 
				
			|||||||
      <el-descriptions-item label="成交状态">
 | 
					      <el-descriptions-item label="成交状态">
 | 
				
			||||||
        {{ customer.dealStatus ? '已成交' : '未成交' }}
 | 
					        {{ customer.dealStatus ? '已成交' : '未成交' }}
 | 
				
			||||||
      </el-descriptions-item>
 | 
					      </el-descriptions-item>
 | 
				
			||||||
      <el-descriptions-item label="负责人">{{ customer.ownerUserName }} </el-descriptions-item>
 | 
					      <el-descriptions-item label="负责人">{{ customer.ownerUserName }}</el-descriptions-item>
 | 
				
			||||||
      <!-- TODO wanwan 首要联系人? -->
 | 
					      <!-- TODO wanwan 首要联系人? -->
 | 
				
			||||||
      <el-descriptions-item label="首要联系人" />
 | 
					      <el-descriptions-item label="首要联系人" />
 | 
				
			||||||
      <!-- TODO wanwan 首要联系人电话? -->
 | 
					      <!-- TODO wanwan 首要联系人电话? -->
 | 
				
			||||||
      <el-descriptions-item label="首要联系人电话">{{ customer.mobile }} </el-descriptions-item>
 | 
					      <el-descriptions-item label="首要联系人电话">{{ customer.mobile }}</el-descriptions-item>
 | 
				
			||||||
    </el-descriptions>
 | 
					    </el-descriptions>
 | 
				
			||||||
  </ContentWrap>
 | 
					  </ContentWrap>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <!-- 表单弹窗:添加/修改 -->
 | 
					  <!-- 表单弹窗:添加/修改 -->
 | 
				
			||||||
  <CustomerForm ref="formRef" @success="emit('refresh')" />
 | 
					  <CustomerForm ref="formRef" @success="emit('refresh')" />
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { DICT_TYPE } from '@/utils/dict'
 | 
					import { DICT_TYPE } from '@/utils/dict'
 | 
				
			||||||
import * as CustomerApi from '@/api/crm/customer'
 | 
					import * as CustomerApi from '@/api/crm/customer'
 | 
				
			||||||
import CustomerForm from '../CustomerForm.vue'
 | 
					import CustomerForm from '../CustomerForm.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineOptions({ name: 'CustomerDetailsHeader' })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { customer, loading } = defineProps<{
 | 
					const { customer, loading } = defineProps<{
 | 
				
			||||||
  customer: CustomerApi.CustomerVO // 客户信息
 | 
					  customer: CustomerApi.CustomerVO // 客户信息
 | 
				
			||||||
  loading: boolean // 加载中
 | 
					  loading: boolean // 加载中
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <ContentWrap>
 | 
					  <ContentWrap>
 | 
				
			||||||
    <el-collapse class="" v-model="activeNames">
 | 
					    <el-collapse v-model="activeNames" class="">
 | 
				
			||||||
      <el-collapse-item name="basicInfo">
 | 
					      <el-collapse-item name="basicInfo">
 | 
				
			||||||
        <template #title>
 | 
					        <template #title>
 | 
				
			||||||
          <span class="text-base font-bold">基本信息</span>
 | 
					          <span class="text-base font-bold">基本信息</span>
 | 
				
			||||||
@@ -20,11 +20,11 @@
 | 
				
			|||||||
          </el-descriptions-item>
 | 
					          </el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="手机">{{ customer.mobile }}</el-descriptions-item>
 | 
					          <el-descriptions-item label="手机">{{ customer.mobile }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="电话">{{ customer.telephone }}</el-descriptions-item>
 | 
					          <el-descriptions-item label="电话">{{ customer.telephone }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="邮箱">{{ customer.email }} </el-descriptions-item>
 | 
					          <el-descriptions-item label="邮箱">{{ customer.email }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="QQ">{{ customer.qq }} </el-descriptions-item>
 | 
					          <el-descriptions-item label="QQ">{{ customer.qq }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="微信">{{ customer.wechat }} </el-descriptions-item>
 | 
					          <el-descriptions-item label="微信">{{ customer.wechat }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="网址">{{ customer.website }} </el-descriptions-item>
 | 
					          <el-descriptions-item label="网址">{{ customer.website }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="所在地">{{ customer.areaName }} </el-descriptions-item>
 | 
					          <el-descriptions-item label="所在地">{{ customer.areaName }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="详细地址"
 | 
					          <el-descriptions-item label="详细地址"
 | 
				
			||||||
            >{{ customer.detailAddress }}
 | 
					            >{{ customer.detailAddress }}
 | 
				
			||||||
          </el-descriptions-item>
 | 
					          </el-descriptions-item>
 | 
				
			||||||
@@ -38,8 +38,8 @@
 | 
				
			|||||||
          </el-descriptions-item>
 | 
					          </el-descriptions-item>
 | 
				
			||||||
        </el-descriptions>
 | 
					        </el-descriptions>
 | 
				
			||||||
        <el-descriptions :column="1">
 | 
					        <el-descriptions :column="1">
 | 
				
			||||||
          <el-descriptions-item label="客户描述">{{ customer.description }} </el-descriptions-item>
 | 
					          <el-descriptions-item label="客户描述">{{ customer.description }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="备注">{{ customer.remark }} </el-descriptions-item>
 | 
					          <el-descriptions-item label="备注">{{ customer.remark }}</el-descriptions-item>
 | 
				
			||||||
        </el-descriptions>
 | 
					        </el-descriptions>
 | 
				
			||||||
      </el-collapse-item>
 | 
					      </el-collapse-item>
 | 
				
			||||||
      <el-collapse-item name="systemInfo">
 | 
					      <el-collapse-item name="systemInfo">
 | 
				
			||||||
@@ -47,8 +47,8 @@
 | 
				
			|||||||
          <span class="text-base font-bold">系统信息</span>
 | 
					          <span class="text-base font-bold">系统信息</span>
 | 
				
			||||||
        </template>
 | 
					        </template>
 | 
				
			||||||
        <el-descriptions :column="2">
 | 
					        <el-descriptions :column="2">
 | 
				
			||||||
          <el-descriptions-item label="负责人">{{ customer.ownerUserName }} </el-descriptions-item>
 | 
					          <el-descriptions-item label="负责人">{{ customer.ownerUserName }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="创建人">{{ customer.creatorName }} </el-descriptions-item>
 | 
					          <el-descriptions-item label="创建人">{{ customer.creatorName }}</el-descriptions-item>
 | 
				
			||||||
          <el-descriptions-item label="创建时间">
 | 
					          <el-descriptions-item label="创建时间">
 | 
				
			||||||
            {{ customer.createTime ? formatDate(customer.createTime) : '空' }}
 | 
					            {{ customer.createTime ? formatDate(customer.createTime) : '空' }}
 | 
				
			||||||
          </el-descriptions-item>
 | 
					          </el-descriptions-item>
 | 
				
			||||||
@@ -60,15 +60,16 @@
 | 
				
			|||||||
    </el-collapse>
 | 
					    </el-collapse>
 | 
				
			||||||
  </ContentWrap>
 | 
					  </ContentWrap>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import * as CustomerApi from '@/api/crm/customer'
 | 
					import * as CustomerApi from '@/api/crm/customer'
 | 
				
			||||||
import { DICT_TYPE } from '@/utils/dict'
 | 
					import { DICT_TYPE } from '@/utils/dict'
 | 
				
			||||||
import { formatDate } from '@/utils/formatTime'
 | 
					import { formatDate } from '@/utils/formatTime'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineOptions({ name: 'CustomerDetailsInfo' })
 | 
				
			||||||
const { customer } = defineProps<{
 | 
					const { customer } = defineProps<{
 | 
				
			||||||
  customer: CustomerApi.CustomerVO // 客户明细
 | 
					  customer: CustomerApi.CustomerVO // 客户明细
 | 
				
			||||||
}>()
 | 
					}>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const activeNames = ref(['basicInfo', 'systemInfo']) // 展示的折叠面板
 | 
					const activeNames = ref(['basicInfo', 'systemInfo']) // 展示的折叠面板
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<style scoped lang="scss"></style>
 | 
					<style lang="scss" scoped></style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,9 @@
 | 
				
			|||||||
      <el-tab-pane label="详细资料">
 | 
					      <el-tab-pane label="详细资料">
 | 
				
			||||||
        <CustomerDetailsInfo :customer="customer" />
 | 
					        <CustomerDetailsInfo :customer="customer" />
 | 
				
			||||||
      </el-tab-pane>
 | 
					      </el-tab-pane>
 | 
				
			||||||
      <el-tab-pane label="操作日志" lazy>TODO 待开发</el-tab-pane>
 | 
					      <el-tab-pane label="操作日志">
 | 
				
			||||||
 | 
					        <OperateLogV2 :log-list="logList" />
 | 
				
			||||||
 | 
					      </el-tab-pane>
 | 
				
			||||||
      <el-tab-pane label="联系人" lazy>
 | 
					      <el-tab-pane label="联系人" lazy>
 | 
				
			||||||
        <ContactList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
 | 
					        <ContactList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
 | 
				
			||||||
      </el-tab-pane>
 | 
					      </el-tab-pane>
 | 
				
			||||||
@@ -38,24 +40,39 @@ import ReceivableList from '@/views/crm/receivable/components/ReceivableList.vue
 | 
				
			|||||||
import ReceivablePlanList from '@/views/crm/receivable/plan/components/ReceivablePlanList.vue' // 回款计划列表
 | 
					import ReceivablePlanList from '@/views/crm/receivable/plan/components/ReceivablePlanList.vue' // 回款计划列表
 | 
				
			||||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
 | 
					import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
 | 
				
			||||||
import { BizTypeEnum } from '@/api/crm/permission'
 | 
					import { BizTypeEnum } from '@/api/crm/permission'
 | 
				
			||||||
 | 
					import { OperateLogV2VO } from '@/api/system/operatelog'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'CrmCustomerDetail' })
 | 
					defineOptions({ name: 'CrmCustomerDetail' })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const route = useRoute()
 | 
					const route = useRoute()
 | 
				
			||||||
const id = Number(route.params.id) // 客户编号
 | 
					const id = Number(route.params.id) // 客户编号
 | 
				
			||||||
const loading = ref(true) // 加载中
 | 
					const loading = ref(true) // 加载中
 | 
				
			||||||
 | 
					 | 
				
			||||||
/** 获取详情 */
 | 
					/** 获取详情 */
 | 
				
			||||||
const customer = ref<CustomerApi.CustomerVO>({} as CustomerApi.CustomerVO) // 客户详情
 | 
					const customer = ref<CustomerApi.CustomerVO>({} as CustomerApi.CustomerVO) // 客户详情
 | 
				
			||||||
const getCustomer = async (id: number) => {
 | 
					const getCustomer = async (id: number) => {
 | 
				
			||||||
  loading.value = true
 | 
					  loading.value = true
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    customer.value = await CustomerApi.getCustomer(id)
 | 
					    customer.value = await CustomerApi.getCustomer(id)
 | 
				
			||||||
 | 
					    await getOperateLog(id)
 | 
				
			||||||
  } finally {
 | 
					  } finally {
 | 
				
			||||||
    loading.value = false
 | 
					    loading.value = false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					const logList = ref<OperateLogV2VO[]>([]) // 操作日志列表
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 获取操作日志
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const getOperateLog = async (customerId: number) => {
 | 
				
			||||||
 | 
					  if (!customerId) {
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const data = await CustomerApi.getOperateLogPage({
 | 
				
			||||||
 | 
					    pageNo: 1,
 | 
				
			||||||
 | 
					    pageSize: 10,
 | 
				
			||||||
 | 
					    bizId: customerId
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  logList.value = data.list
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
/** 初始化 */
 | 
					/** 初始化 */
 | 
				
			||||||
const { delView } = useTagsViewStore() // 视图操作
 | 
					const { delView } = useTagsViewStore() // 视图操作
 | 
				
			||||||
const { currentRoute } = useRouter() // 路由
 | 
					const { currentRoute } = useRouter() // 路由
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user