增加商机线索管理修复若干bug
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="crm-page">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>线索管理</h2>
|
||||
<p>登记潜在客户线索,跟进后可转化为商机;转化后线索将锁定不可编辑</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button :icon="Refresh" @click="fetchList">刷新</el-button>
|
||||
<el-button type="primary" :icon="Plus" @click="openCreate">新增线索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-bar">
|
||||
<el-form :inline="true" :model="filters" @submit.prevent>
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="filters.keyword"
|
||||
clearable
|
||||
placeholder="搜索线索名称 / 客户名称 / 对接人"
|
||||
:prefix-icon="Search"
|
||||
style="width: 260px"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户来源">
|
||||
<el-select v-model="filters.source" clearable placeholder="全部" style="width: 130px">
|
||||
<el-option v-for="i in CLUE_SOURCE_OPTIONS" :key="i.value" :label="i.label" :value="i.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户级别">
|
||||
<el-select v-model="filters.clue_level" clearable placeholder="全部" style="width: 120px">
|
||||
<el-option v-for="i in PIPELINE_LEVEL_OPTIONS" :key="i.value" :label="i.label" :value="i.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="filters.status" clearable placeholder="全部" style="width: 120px">
|
||||
<el-option v-for="i in CLUE_STATUS_OPTIONS" :key="i.value" :label="i.label" :value="i.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
|
||||
<el-button :icon="Refresh" @click="resetFilters">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="table-container" v-loading="loading">
|
||||
<el-table :data="tableData" stripe border row-key="id">
|
||||
<el-table-column label="线索名称" min-width="170" show-overflow-tooltip fixed>
|
||||
<template #default="{ row }">
|
||||
<span class="name-link" @click="openDetail(row)">{{ row.clue_name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="customer_name" label="客户名称" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="客户来源" width="110" align="center">
|
||||
<template #default="{ row }">{{ clueSourceText(row.source) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户级别" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="pipelineLevelTag(row.clue_level)" size="small">
|
||||
{{ pipelineLevelText(row.clue_level) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="industry" label="客户行业" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="负责人" width="100">
|
||||
<template #default="{ row }">{{ row.owner_user_name || "-" }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="对接人" width="100">
|
||||
<template #default="{ row }">{{ row.contact_person || "-" }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="对接人手机" width="130">
|
||||
<template #default="{ row }">{{ row.contact_phone || "-" }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下次联系时间" width="160" align="center">
|
||||
<template #default="{ row }">{{ formatDateTime(row.next_contact_time) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="clueStatusTag(row.status)" size="small">{{ clueStatusText(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="220" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" :disabled="row.locked === 1" @click="openEdit(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="warning"
|
||||
size="small"
|
||||
:disabled="row.locked === 1 || row.status === 2"
|
||||
@click="openConvert(row)"
|
||||
>
|
||||
转化
|
||||
</el-button>
|
||||
<el-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty><el-empty description="暂无线索" :image-size="80" /></template>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.page"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSearch"
|
||||
@current-change="fetchList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ClueEdit v-model:visible="editVisible" :edit-data="currentEditData" @success="fetchList" />
|
||||
<ClueConvert v-model:visible="convertVisible" :clue="currentEditData" @success="handleConvertSuccess" />
|
||||
<ClueDetail
|
||||
v-model:visible="detailVisible"
|
||||
:clue="currentEditData"
|
||||
@refresh="fetchList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { Plus, Search, Refresh } from "@element-plus/icons-vue";
|
||||
import { getClueList, deleteClue } from "@/api/crmPipeline";
|
||||
import ClueEdit from "./components/edit.vue";
|
||||
import ClueConvert from "./components/convert.vue";
|
||||
import ClueDetail from "./components/detail.vue";
|
||||
import {
|
||||
CLUE_SOURCE_OPTIONS,
|
||||
CLUE_STATUS_OPTIONS,
|
||||
PIPELINE_LEVEL_OPTIONS,
|
||||
clueSourceText,
|
||||
pipelineLevelText,
|
||||
pipelineLevelTag,
|
||||
clueStatusText,
|
||||
clueStatusTag,
|
||||
formatDateTime,
|
||||
} from "../dict";
|
||||
|
||||
const loading = ref(false);
|
||||
const tableData = ref([]);
|
||||
const editVisible = ref(false);
|
||||
const convertVisible = ref(false);
|
||||
const detailVisible = ref(false);
|
||||
const currentEditData = ref(null);
|
||||
|
||||
const filters = reactive({
|
||||
keyword: "",
|
||||
source: "",
|
||||
clue_level: "",
|
||||
status: "",
|
||||
});
|
||||
|
||||
const pagination = reactive({ page: 1, pageSize: 20, total: 0 });
|
||||
|
||||
onMounted(() => {
|
||||
fetchList();
|
||||
});
|
||||
|
||||
async function fetchList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getClueList({
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize,
|
||||
...filters,
|
||||
});
|
||||
tableData.value = res?.data?.list || [];
|
||||
pagination.total = res?.data?.total || 0;
|
||||
} catch (e) {
|
||||
tableData.value = [];
|
||||
pagination.total = 0;
|
||||
ElMessage.error(e.message || "查询失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
pagination.page = 1;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
filters.keyword = "";
|
||||
filters.source = "";
|
||||
filters.clue_level = "";
|
||||
filters.status = "";
|
||||
handleSearch();
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
currentEditData.value = null;
|
||||
editVisible.value = true;
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
currentEditData.value = { ...row };
|
||||
editVisible.value = true;
|
||||
}
|
||||
|
||||
function openConvert(row) {
|
||||
currentEditData.value = { ...row };
|
||||
convertVisible.value = true;
|
||||
}
|
||||
|
||||
function openDetail(row) {
|
||||
currentEditData.value = { ...row };
|
||||
detailVisible.value = true;
|
||||
}
|
||||
|
||||
function handleConvertSuccess() {
|
||||
fetchList();
|
||||
ElMessage.success("线索已转化,可在商机管理中继续跟进");
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除线索「${row.clue_name}」吗?删除后不可恢复。`, "删除确认", {
|
||||
type: "warning",
|
||||
});
|
||||
await deleteClue(row.id);
|
||||
ElMessage.success("删除成功");
|
||||
fetchList();
|
||||
} catch (e) {
|
||||
if (e !== "cancel") ElMessage.error(e.message || "删除失败");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped src="../styles/crm-page.less"></style>
|
||||
Reference in New Issue
Block a user