优化线索、商机、项目相关开发
This commit is contained in:
1 parent
9c07af2b59
commit
840673bda5
55 files changed
+4866
-625
No files matched your search
@@ -28,6 +28,19 @@
|
||||
<el-option v-for="o in relatedOptions" :key="o.id" :label="o.name" :value="o.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="回访阶段" prop="stage">
|
||||
<el-select v-model="form.stage" style="width: 100%">
|
||||
<el-option
|
||||
v-for="i in PIPELINE_RELATED_TYPE_OPTIONS"
|
||||
:key="i.value"
|
||||
:label="i.label"
|
||||
:value="i.value"
|
||||
/>
|
||||
</el-select>
|
||||
<div style="font-size: 12px; color: var(--el-text-color-secondary); line-height: 1.4; margin-top: 2px">
|
||||
按该次沟通实际发生的阶段记录,默认跟随关联类型
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="回访方式" prop="follow_type">
|
||||
<el-select v-model="form.follow_type" style="width: 100%">
|
||||
<el-option v-for="i in FOLLOW_TYPE_OPTIONS" :key="i.value" :label="i.label" :value="i.value" />
|
||||
@@ -90,6 +103,8 @@ const defaultForm = () => ({
|
||||
related_type: 1,
|
||||
related_id: "",
|
||||
related_name: "",
|
||||
// 所属阶段:1线索/2商机/3项目,默认跟随关联类型,可手动调整
|
||||
stage: 1,
|
||||
follow_type: "1",
|
||||
follow_time: "",
|
||||
content: "",
|
||||
@@ -131,6 +146,8 @@ watch(
|
||||
if (props.editData) {
|
||||
isEdit.value = true;
|
||||
Object.assign(form, props.editData);
|
||||
// 编辑:默认保留记录原本的阶段(缺失时按关联类型回退),可手动修正
|
||||
form.stage = Number(props.editData.stage || props.editData.related_type || 1);
|
||||
searchRelated("");
|
||||
} else {
|
||||
isEdit.value = false;
|
||||
@@ -148,6 +165,8 @@ watch(
|
||||
function handleTypeChange() {
|
||||
form.related_id = "";
|
||||
form.related_name = "";
|
||||
// 切换关联类型时,阶段默认跟随新类型(仍可手动调整)
|
||||
form.stage = Number(form.related_type);
|
||||
relatedOptions.value = [];
|
||||
searchRelated("");
|
||||
}
|
||||
@@ -189,6 +208,8 @@ async function handleSubmit() {
|
||||
const payload = {
|
||||
...form,
|
||||
related_name: form.related_name || selected?.name || "",
|
||||
// 回访阶段:允许与关联类型不同(如补录早期阶段的沟通),默认跟随关联类型
|
||||
stage: Number(form.stage) || Number(form.related_type),
|
||||
};
|
||||
if (form.id) {
|
||||
await updateFollow(payload);
|
||||
|
||||
@@ -49,7 +49,16 @@
|
||||
<el-table :data="tableData" stripe border row-key="id">
|
||||
<el-table-column label="关联类型" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" effect="plain">{{ pipelineRelatedTypeText(row.related_type) }}</el-tag>
|
||||
<el-tag size="small" effect="plain" :type="pipelineStageTag(row.related_type)">
|
||||
{{ pipelineRelatedTypeText(row.related_type) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="阶段" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" effect="plain" :type="pipelineStageTag(row.stage || row.related_type)">
|
||||
{{ pipelineStageText(row.stage || row.related_type) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="related_name" label="关联对象" min-width="180" show-overflow-tooltip />
|
||||
@@ -59,8 +68,12 @@
|
||||
<el-table-column label="回访时间" width="160" align="center">
|
||||
<template #default="{ row }">{{ formatDateTime(row.follow_time) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="回访内容" min-width="240" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ stripHtml(row.content) }}</template>
|
||||
<el-table-column label="回访内容" min-width="240">
|
||||
<template #default="{ row }">
|
||||
<div class="content-cell" :title="stripHtml(row.content)" @click="openDetail(row)">
|
||||
{{ stripHtml(row.content) }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下次联系时间" width="160" align="center">
|
||||
<template #default="{ row }">{{ formatDateTime(row.next_contact_time) }}</template>
|
||||
@@ -89,12 +102,46 @@
|
||||
</div>
|
||||
|
||||
<FollowEdit v-model:visible="editVisible" :edit-data="currentRow" @success="fetchList" />
|
||||
|
||||
<!-- 回访详情 -->
|
||||
<el-dialog v-model="detailVisible" :title="detailData?.related_name || '回访详情'" width="680px">
|
||||
<el-descriptions v-if="detailData" :column="2" border size="small">
|
||||
<el-descriptions-item label="关联类型">
|
||||
<el-tag size="small" effect="plain" :type="pipelineStageTag(detailData.related_type)">
|
||||
{{ pipelineRelatedTypeText(detailData.related_type) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="关联对象">{{ detailData.related_name || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="回访方式">{{ followTypeText(detailData.follow_type) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="回访时间">{{ formatDateTime(detailData.follow_time) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="回访人">{{ detailData.owner_user_name || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="下次联系时间">
|
||||
{{ formatDateTime(detailData.next_contact_time) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="回访内容" :span="2">
|
||||
<div class="detail-content" v-html="detailData.content" @click="handleContentClick"></div>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #footer>
|
||||
<el-button @click="detailVisible = false">关闭</el-button>
|
||||
<el-button type="primary" @click="openEditFromDetail">编辑</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 回访内容图片放大预览 -->
|
||||
<el-image-viewer
|
||||
v-if="previewVisible"
|
||||
:url-list="previewUrls"
|
||||
:initial-index="previewIndex"
|
||||
:z-index="5000"
|
||||
@close="previewVisible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { ElMessage, ElMessageBox, ElImageViewer } from "element-plus";
|
||||
import { Plus, Search, Refresh } from "@element-plus/icons-vue";
|
||||
import { getFollowList, deleteFollow } from "@/api/crmPipeline";
|
||||
import FollowEdit from "./components/edit.vue";
|
||||
@@ -103,6 +150,8 @@ import {
|
||||
FOLLOW_TYPE_OPTIONS,
|
||||
followTypeText,
|
||||
pipelineRelatedTypeText,
|
||||
pipelineStageText,
|
||||
pipelineStageTag,
|
||||
formatDateTime,
|
||||
stripHtml,
|
||||
} from "../dict";
|
||||
@@ -112,6 +161,15 @@ const tableData = ref([]);
|
||||
const editVisible = ref(false);
|
||||
const currentRow = ref(null);
|
||||
|
||||
// 详情弹窗
|
||||
const detailVisible = ref(false);
|
||||
const detailData = ref(null);
|
||||
|
||||
// 图片预览
|
||||
const previewVisible = ref(false);
|
||||
const previewUrls = ref([]);
|
||||
const previewIndex = ref(0);
|
||||
|
||||
const filters = reactive({ keyword: "", related_type: "", follow_type: "" });
|
||||
const pagination = reactive({ page: 1, pageSize: 20, total: 0 });
|
||||
|
||||
@@ -160,6 +218,33 @@ function openEdit(row) {
|
||||
editVisible.value = true;
|
||||
}
|
||||
|
||||
function openDetail(row) {
|
||||
detailData.value = { ...row };
|
||||
detailVisible.value = true;
|
||||
}
|
||||
|
||||
function openEditFromDetail() {
|
||||
if (!detailData.value) return;
|
||||
currentRow.value = { ...detailData.value };
|
||||
detailVisible.value = false;
|
||||
editVisible.value = true;
|
||||
}
|
||||
|
||||
/** 点击详情里的图片 → 放大预览(支持同一条记录内多图切换) */
|
||||
function handleContentClick(e) {
|
||||
const target = e.target;
|
||||
if (!target || String(target.tagName || "").toUpperCase() !== "IMG") return;
|
||||
const container = e.currentTarget;
|
||||
if (!container) return;
|
||||
const imgs = Array.from(container.querySelectorAll("img"));
|
||||
const urls = imgs.map((img) => img.getAttribute("src")).filter(Boolean);
|
||||
if (!urls.length) return;
|
||||
const index = imgs.indexOf(target);
|
||||
previewUrls.value = urls;
|
||||
previewIndex.value = index < 0 ? 0 : index;
|
||||
previewVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm("确定删除该回访记录吗?", "删除确认", { type: "warning" });
|
||||
@@ -173,3 +258,31 @@ async function handleDelete(row) {
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped src="../styles/crm-page.less"></style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.content-cell {
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
word-break: break-word;
|
||||
|
||||
:deep(img) {
|
||||
max-width: 220px;
|
||||
max-height: 160px;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
cursor: zoom-in;
|
||||
vertical-align: middle;
|
||||
margin: 2px 4px 2px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user