modified: .env
modified: src/components/OperateLogV2/src/OperateLogV2.vue modified: src/views/crm/contact/components/ContactListModal.vue modified: src/views/crm/contact/detail/index.vue modified: src/views/crm/followup/FollowUpRecordForm.vue modified: src/views/crm/followup/index.vuepull/853/head
parent
68af678655
commit
627b6ea38e
4
.env
4
.env
|
|
@ -1,5 +1,5 @@
|
|||
# 标题
|
||||
VITE_APP_TITLE=芋道管理系统
|
||||
VITE_APP_TITLE=速捷充电管理系统
|
||||
|
||||
# 项目本地运行端口号
|
||||
VITE_PORT=80
|
||||
|
|
@ -11,7 +11,7 @@ VITE_OPEN=true
|
|||
VITE_APP_TENANT_ENABLE=true
|
||||
|
||||
# 验证码的开关
|
||||
VITE_APP_CAPTCHA_ENABLE=true
|
||||
VITE_APP_CAPTCHA_ENABLE=false
|
||||
|
||||
# 文档地址的开关
|
||||
VITE_APP_DOCALERT_ENABLE=true
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ const getUserTypeColor = (type: number) => {
|
|||
<style lang="scss" scoped>
|
||||
// 时间线样式调整
|
||||
:deep(.el-timeline) {
|
||||
margin: 10px 0 0 110px;
|
||||
margin: 10px 0 0 150px;
|
||||
|
||||
.el-timeline-item__wrapper {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -43,8 +43,11 @@
|
|||
:data="list"
|
||||
:show-overflow-tooltip="true"
|
||||
:stripe="true"
|
||||
row-key="id"
|
||||
highlight-current-row
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column type="radio" width="55" />
|
||||
<el-table-column align="center" fixed="left" label="姓名" prop="name">
|
||||
<template #default="scope">
|
||||
<el-link :underline="false" type="primary" @click="openDetail(scope.row.id)">
|
||||
|
|
@ -143,13 +146,13 @@ const openForm = () => {
|
|||
/** 关联联系人提交 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const contactRef = ref()
|
||||
const selectedRow = ref<ContactApi.ContactVO | null>(null)
|
||||
const submitForm = async () => {
|
||||
const contactIds = contactRef.value.getSelectionRows().map((row: ContactApi.ContactVO) => row.id)
|
||||
if (contactIds.length === 0) {
|
||||
if (!selectedRow.value) {
|
||||
return message.error('未选择联系人')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
emit('success', contactIds, contactRef.value.getSelectionRows())
|
||||
emit('success', [selectedRow.value.id], [selectedRow.value])
|
||||
}
|
||||
|
||||
/** 打开联系人详情 */
|
||||
|
|
@ -157,4 +160,9 @@ const { push } = useRouter()
|
|||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmContactDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 单选框变化事件 */
|
||||
const handleCurrentChange = (val: ContactApi.ContactVO | null) => {
|
||||
selectedRow.value = val
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ import { useTagsViewStore } from '@/store/modules/tagsView'
|
|||
import * as ContactApi from '@/api/crm/contact'
|
||||
import ContactDetailsHeader from '@/views/crm/contact/detail/ContactDetailsHeader.vue'
|
||||
import ContactDetailsInfo from '@/views/crm/contact/detail/ContactDetailsInfo.vue'
|
||||
import BusinessList from '@/views/crm/business/components/BusinessList.vue' // 商机列表
|
||||
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
|
||||
import { BizTypeEnum } from '@/api/crm/permission'
|
||||
import { OperateLogVO } from '@/api/system/operatelog'
|
||||
|
|
|
|||
|
|
@ -50,20 +50,11 @@
|
|||
<el-form-item label="关联联系人" prop="contactIds">
|
||||
<el-button @click="handleOpenContact">
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
添加联系人
|
||||
关联联系人
|
||||
</el-button>
|
||||
<FollowUpRecordContactForm :contacts="formData.contacts" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" v-if="formData.bizType == BizTypeEnum.CRM_CUSTOMER">
|
||||
<el-form-item label="关联商机" prop="businessIds">
|
||||
<el-button @click="handleOpenBusiness">
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
添加商机
|
||||
</el-button>
|
||||
<FollowUpRecordBusinessForm :businesses="formData.businesses" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
|
|
@ -155,11 +146,10 @@ const handleOpenContact = () => {
|
|||
contactTableSelectRef.value?.open()
|
||||
}
|
||||
const handleAddContact = (contactId: [], newContacts: ContactApi.ContactVO[]) => {
|
||||
newContacts.forEach((contact) => {
|
||||
if (!formData.value.contacts.some((item) => item.id === contact.id)) {
|
||||
formData.value.contacts.push(contact)
|
||||
}
|
||||
})
|
||||
if (newContacts.length > 0) {
|
||||
// 只保留最新选择的一个联系人
|
||||
formData.value.contacts = [newContacts[0]]
|
||||
}
|
||||
}
|
||||
|
||||
/** 关联商机 */
|
||||
|
|
|
|||
|
|
@ -7,39 +7,25 @@
|
|||
写跟进
|
||||
</el-button>
|
||||
</el-row>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column align="center" label="跟进人" prop="creatorName" />
|
||||
<el-table-column align="center" label="跟进类型" prop="type">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_UP_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="跟进内容" prop="content" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="下次联系时间"
|
||||
prop="nextTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="关联联系人"
|
||||
prop="contactIds"
|
||||
v-if="bizType === BizTypeEnum.CRM_CUSTOMER"
|
||||
>
|
||||
<template #default="scope">
|
||||
<!-- 跟进记录时间线 -->
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id || index"
|
||||
:timestamp="formatDate(item.createTime)"
|
||||
placement="top"
|
||||
>
|
||||
<div class="el-timeline-right-content">
|
||||
<div class="followup-meta mb-5px">
|
||||
<span class="meta-label">跟进人:</span><el-tag class="mr-10px" type="success">{{ item.creatorName }}</el-tag>
|
||||
<span class="meta-label">跟进类型:</span><dict-tag :type="DICT_TYPE.CRM_FOLLOW_UP_TYPE" :value="item.type" class="mr-10px" />
|
||||
</div>
|
||||
<span class="follow-row">跟进内容:{{ item.content }}</span>
|
||||
<span v-if="item.nextTime" class="mb-5px follow-row text-gray-500">下次联系:{{ formatDate(item.nextTime) }}</span>
|
||||
<!-- <span v-if="bizType === BizTypeEnum.CRM_CUSTOMER && item.contacts && item.contacts.length" class="ml-10px">
|
||||
关联联系人:
|
||||
<el-link
|
||||
v-for="contact in scope.row.contacts"
|
||||
v-for="contact in item.contacts"
|
||||
:key="`key-${contact.id}`"
|
||||
:underline="false"
|
||||
type="primary"
|
||||
|
|
@ -48,47 +34,56 @@
|
|||
>
|
||||
{{ contact.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="图片/附件">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" v-if="scope.row.picUrls && scope.row.picUrls.length" @click="openFileDialog(scope.row.picUrls, '图片')">查看图片</el-button>
|
||||
<el-button link type="primary" v-if="scope.row.fileUrls && scope.row.fileUrls.length" @click="openFileDialog(scope.row.fileUrls, '附件')">查看附件</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"> 删除 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</span> -->
|
||||
<!-- 图片直接展示 -->
|
||||
<div v-if="item.picUrls && item.picUrls.length" class="followup-img-list follow-row mt-10px">
|
||||
<el-image
|
||||
v-for="url in item.picUrls"
|
||||
:key="url"
|
||||
:src="url"
|
||||
style="width: 120px; height: 120px; object-fit: contain; border: 1px solid #eee; border-radius: 4px; margin-right: 8px;"
|
||||
:preview-src-list="item.picUrls"
|
||||
/>
|
||||
</div>
|
||||
<!-- 附件链接展示 -->
|
||||
<div v-if="item.fileUrls && item.fileUrls.length" class="followup-file-list follow-row mt-5px">
|
||||
<span style="font-weight: bold;">附件:</span>
|
||||
<el-link
|
||||
v-for="url in item.fileUrls"
|
||||
:key="url"
|
||||
:href="url"
|
||||
target="_blank"
|
||||
type="primary"
|
||||
style="margin-right: 12px;"
|
||||
>
|
||||
{{ url.substring(url.lastIndexOf('/')+1) }}
|
||||
</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<el-button link type="danger" class="mr-20px followup-delete-btn" @click="handleDelete(item.id)">
|
||||
<Icon icon="ep:delete" class="mr-3px" /> 删除
|
||||
</el-button>
|
||||
<template #dot>
|
||||
<span style="background-color: #67C23A;" class="dot-node-style">
|
||||
{{ item.creatorName ? item.creatorName[0] : '' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<FollowUpRecordForm ref="formRef" @success="getList" />
|
||||
|
||||
<!-- 文件查看弹窗 -->
|
||||
<el-dialog v-model="fileDialogVisible" :title="fileDialogTitle" width="600px">
|
||||
<div v-if="fileDialogType === '图片'">
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
||||
<el-image v-for="url in fileDialogList" :key="url" :src="url" style="width: 120px; height: 120px; object-fit: contain; border: 1px solid #eee; border-radius: 4px;" :preview-src-list="fileDialogList" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-link v-for="url in fileDialogList" :key="url" :href="url" target="_blank" type="primary" style="display: block; margin-bottom: 8px;">{{ url.substring(url.lastIndexOf('/')+1) }}</el-link>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { FollowUpRecordApi, FollowUpRecordVO } from '@/api/crm/followup'
|
||||
import FollowUpRecordForm from './FollowUpRecordForm.vue'
|
||||
|
|
@ -176,3 +171,90 @@ watch(
|
|||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 时间线样式参考 OperateLogV2
|
||||
:deep(.el-timeline) {
|
||||
margin: 10px 0 0 30px;
|
||||
}
|
||||
:deep(.el-timeline-item__wrapper) {
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
:deep(.el-timeline-item__timestamp) {
|
||||
position: static !important;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
left: unset;
|
||||
top: unset;
|
||||
text-align: left;
|
||||
}
|
||||
:deep(.el-timeline-right-content) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
min-height: 30px;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.followup-img-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.followup-file-list {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.followup-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.follow-row {
|
||||
margin-bottom: 5px;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
.meta-label {
|
||||
margin-right: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.followup-delete-btn {
|
||||
color: #f56c6c !important;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
transition: background 0.2s, color 0.2s;
|
||||
border: none;
|
||||
background: #fef0f0;
|
||||
border-radius: 4px;
|
||||
padding: 2px 10px;
|
||||
margin: 10px 0 0 0;
|
||||
&:hover {
|
||||
background: #fef0f0;
|
||||
color: #c0392b !important;
|
||||
}
|
||||
.icon {
|
||||
font-size: 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue