From 26eaf1eddbbf15d1b3e850b5062057ba837597ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=BF=97=E5=BC=BA?= <357099073@qq.com> Date: Thu, 10 Sep 2026 20:18:11 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=B4=E6=97=B6=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/api/crm.js | 97 ++ .../apps/crm/contact/components/edit.vue | 293 ++++ backend/src/views/apps/crm/contact/index.vue | 285 ++++ .../apps/crm/customer/components/edit.vue | 288 ++++ backend/src/views/apps/crm/customer/index.vue | 604 +++++++++ .../src/views/apps/crm/dashboard/index.vue | 1173 +++++++++++++++++ backend/src/views/apps/crm/dict.js | 137 ++ backend/src/views/apps/crm/index.vue | 7 + backend/src/views/apps/crm/pool/index.vue | 511 +++++++ .../apps/crm/supplier/components/edit.vue | 284 ++++ backend/src/views/apps/crm/supplier/index.vue | 533 ++++++++ 11 files changed, 4212 insertions(+) create mode 100644 backend/src/api/crm.js create mode 100644 backend/src/views/apps/crm/contact/components/edit.vue create mode 100644 backend/src/views/apps/crm/contact/index.vue create mode 100644 backend/src/views/apps/crm/customer/components/edit.vue create mode 100644 backend/src/views/apps/crm/customer/index.vue create mode 100644 backend/src/views/apps/crm/dashboard/index.vue create mode 100644 backend/src/views/apps/crm/dict.js create mode 100644 backend/src/views/apps/crm/index.vue create mode 100644 backend/src/views/apps/crm/pool/index.vue create mode 100644 backend/src/views/apps/crm/supplier/components/edit.vue create mode 100644 backend/src/views/apps/crm/supplier/index.vue diff --git a/backend/src/api/crm.js b/backend/src/api/crm.js new file mode 100644 index 0000000..b3af036 --- /dev/null +++ b/backend/src/api/crm.js @@ -0,0 +1,97 @@ +import request from "@/utils/request"; + +/** + * CRM 接口 + * + * 说明: + * - 客户 / 供应商沿用已有 ERP 接口(数据模型 TenantCrmCustomer / TenantCrmSupplier,租户隔离); + * - 联系人沿用 /backend/crm/contact 系列接口(见 api/contact.js); + * - 客户公海为 CRM 新增能力,需后端提供 /backend/crm/pool 系列接口。 + */ + +/* --------------------------------- 客户 --------------------------------- */ + +export function getCrmCustomerList(params) { + return request({ url: "/backend/erp/customer/list", method: "get", params }); +} + +export function getCrmCustomerDetail(id) { + return request({ url: `/backend/erp/customer/${id}`, method: "get" }); +} + +export function createCrmCustomer(data) { + return request({ url: "/backend/erp/customer", method: "post", data }); +} + +export function updateCrmCustomer(id, data) { + return request({ url: `/backend/erp/customer/${id}`, method: "put", data }); +} + +export function deleteCrmCustomer(id) { + return request({ url: `/backend/erp/customer/${id}`, method: "delete" }); +} + +export function toggleCrmCustomerStatus(id, status) { + return request({ + url: `/backend/erp/customer/${id}/status`, + method: "post", + data: { status }, + }); +} + +/* -------------------------------- 供应商 -------------------------------- */ + +export function getCrmSupplierList(params) { + return request({ url: "/backend/erp/supplier/list", method: "get", params }); +} + +export function getCrmSupplierDetail(id) { + return request({ url: `/backend/erp/supplier/${id}`, method: "get" }); +} + +export function createCrmSupplier(data) { + return request({ url: "/backend/erp/supplier", method: "post", data }); +} + +export function updateCrmSupplier(id, data) { + return request({ url: `/backend/erp/supplier/${id}`, method: "put", data }); +} + +export function deleteCrmSupplier(id) { + return request({ url: `/backend/erp/supplier/${id}`, method: "delete" }); +} + +export function toggleCrmSupplierStatus(id, status) { + return request({ + url: `/backend/erp/supplier/${id}/status`, + method: "post", + data: { status }, + }); +} + +/* ------------------------------- 客户公海 ------------------------------- */ + +/** 公海列表:租户内共享,所有成员可见 */ +export function getPoolList(params) { + return request({ url: "/backend/crm/pool/list", method: "get", params }); +} + +/** 领取客户:ids 客户ID集合,领取后负责人为当前登录用户 */ +export function claimPoolCustomer(data) { + return request({ url: "/backend/crm/pool/claim", method: "post", data }); +} + +/** 分配客户:ids 客户ID集合,owner_user_id 负责人 */ +export function assignPoolCustomer(data) { + return request({ url: "/backend/crm/pool/assign", method: "post", data }); +} + +/** 移出公海:ids 客户ID集合 */ +export function removeFromPool(data) { + return request({ url: "/backend/crm/pool/remove", method: "post", data }); +} + +/** 移入公海:ids 客户ID集合,reason 移入原因 */ +export function moveToPool(data) { + return request({ url: "/backend/crm/pool/move", method: "post", data }); +} diff --git a/backend/src/views/apps/crm/contact/components/edit.vue b/backend/src/views/apps/crm/contact/components/edit.vue new file mode 100644 index 0000000..c0de645 --- /dev/null +++ b/backend/src/views/apps/crm/contact/components/edit.vue @@ -0,0 +1,293 @@ + + + diff --git a/backend/src/views/apps/crm/contact/index.vue b/backend/src/views/apps/crm/contact/index.vue new file mode 100644 index 0000000..af6fc5d --- /dev/null +++ b/backend/src/views/apps/crm/contact/index.vue @@ -0,0 +1,285 @@ + + + + + diff --git a/backend/src/views/apps/crm/customer/components/edit.vue b/backend/src/views/apps/crm/customer/components/edit.vue new file mode 100644 index 0000000..06dd5bf --- /dev/null +++ b/backend/src/views/apps/crm/customer/components/edit.vue @@ -0,0 +1,288 @@ + + + diff --git a/backend/src/views/apps/crm/customer/index.vue b/backend/src/views/apps/crm/customer/index.vue new file mode 100644 index 0000000..fc8a4a5 --- /dev/null +++ b/backend/src/views/apps/crm/customer/index.vue @@ -0,0 +1,604 @@ + + + + + diff --git a/backend/src/views/apps/crm/dashboard/index.vue b/backend/src/views/apps/crm/dashboard/index.vue new file mode 100644 index 0000000..6ed6b35 --- /dev/null +++ b/backend/src/views/apps/crm/dashboard/index.vue @@ -0,0 +1,1173 @@ + + + + + diff --git a/backend/src/views/apps/crm/dict.js b/backend/src/views/apps/crm/dict.js new file mode 100644 index 0000000..a5b9f7d --- /dev/null +++ b/backend/src/views/apps/crm/dict.js @@ -0,0 +1,137 @@ +/** + * CRM 模块通用字典与格式化方法 + * kind: customer(客户) | supplier(供应商) + */ + +export const CUSTOMER_TYPE_OPTIONS = [ + { label: "企业", value: "1" }, + { label: "政府机构", value: "2" }, + { label: "国企", value: "3" }, + { label: "教育机构", value: "4" }, + { label: "个人", value: "5" }, +]; + +export const SUPPLIER_TYPE_OPTIONS = [ + { label: "原材料供应商", value: "1" }, + { label: "设备供应商", value: "2" }, + { label: "服务供应商", value: "3" }, + { label: "其他", value: "4" }, +]; + +export const STATUS_OPTIONS = [ + { label: "正常", value: "1" }, + { label: "禁用", value: "0" }, + { label: "冻结", value: "2" }, + { label: "已注销", value: "3" }, +]; + +export const GENDER_OPTIONS = [ + { label: "未知", value: 0 }, + { label: "男", value: 1 }, + { label: "女", value: 2 }, +]; + +/** 联系人关联类型:1=客户,2=供应商 */ +export const RELATED_TYPE_OPTIONS = [ + { label: "客户", value: 1 }, + { label: "供应商", value: 2 }, +]; + +/** 客户移入公海原因 */ +export const POOL_REASON_OPTIONS = [ + "长期未跟进", + "合作终止", + "重复建档", + "负责人离职", + "手动释放", + "其他", +]; + +const TYPE_MAP = { + customer: { + 1: "企业", + 2: "政府机构", + 3: "国企", + 4: "教育机构", + 5: "个人", + }, + supplier: { + 1: "原材料供应商", + 2: "设备供应商", + 3: "服务供应商", + 4: "其他", + }, +}; + +const LEVEL_MAP = { + customer: { + 1: "核心客户", + 2: "重要客户", + 3: "普通客户", + 4: "潜在客户", + }, + supplier: { + 1: "核心供应商", + 2: "重要供应商", + 3: "普通供应商", + 4: "潜在供应商", + }, +}; + +const STATUS_MAP = { 0: "禁用", 1: "正常", 2: "冻结", 3: "已注销" }; +const STATUS_TAG_MAP = { 0: "info", 1: "success", 2: "warning", 3: "danger" }; +const LEVEL_TAG_MAP = { 1: "danger", 2: "warning", 3: "info", 4: "" }; +const GENDER_MAP = { 0: "未知", 1: "男", 2: "女" }; + +const normalize = (val) => (val === undefined || val === null ? "" : String(val)); + +export const typeOptions = (kind = "customer") => + (kind === "supplier" ? SUPPLIER_TYPE_OPTIONS : CUSTOMER_TYPE_OPTIONS); + +export const typeText = (kind, val) => + TYPE_MAP[kind]?.[normalize(val)] || normalize(val) || "-"; + +export const typeTag = (val) => { + const map = { 1: "primary", 2: "warning", 3: "success", 4: "info", 5: "info" }; + return map[normalize(val)] || "info"; +}; + +export const levelOptions = (kind = "customer") => + Object.entries(LEVEL_MAP[kind] || LEVEL_MAP.customer).map(([value, label]) => ({ + value, + label, + })); + +export const levelText = (kind, val) => + LEVEL_MAP[kind]?.[normalize(val)] || normalize(val) || "-"; + +export const levelTag = (val) => LEVEL_TAG_MAP[normalize(val)] ?? ""; + +export const statusText = (val) => STATUS_MAP[normalize(val)] || normalize(val) || "-"; + +export const statusTag = (val) => STATUS_TAG_MAP[normalize(val)] || "info"; + +export const genderText = (val) => GENDER_MAP[normalize(val)] || "未知"; + +export const relatedTypeText = (val) => + normalize(val) === "1" ? "客户" : normalize(val) === "2" ? "供应商" : "-"; + +export function formatDate(val) { + if (!val) return "-"; + const d = new Date(val); + if (isNaN(d.getTime())) return String(val); + const m = String(d.getMonth() + 1).padStart(2, "0"); + const day = String(d.getDate()).padStart(2, "0"); + return `${d.getFullYear()}-${m}-${day}`; +} + +export function formatDateTime(val) { + if (!val) return "-"; + const d = new Date(val); + if (isNaN(d.getTime())) return String(val); + const m = String(d.getMonth() + 1).padStart(2, "0"); + const day = String(d.getDate()).padStart(2, "0"); + const h = String(d.getHours()).padStart(2, "0"); + const min = String(d.getMinutes()).padStart(2, "0"); + return `${d.getFullYear()}-${m}-${day} ${h}:${min}`; +} diff --git a/backend/src/views/apps/crm/index.vue b/backend/src/views/apps/crm/index.vue new file mode 100644 index 0000000..3fa5df9 --- /dev/null +++ b/backend/src/views/apps/crm/index.vue @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/backend/src/views/apps/crm/pool/index.vue b/backend/src/views/apps/crm/pool/index.vue new file mode 100644 index 0000000..c7f0585 --- /dev/null +++ b/backend/src/views/apps/crm/pool/index.vue @@ -0,0 +1,511 @@ + + + + + diff --git a/backend/src/views/apps/crm/supplier/components/edit.vue b/backend/src/views/apps/crm/supplier/components/edit.vue new file mode 100644 index 0000000..061e070 --- /dev/null +++ b/backend/src/views/apps/crm/supplier/components/edit.vue @@ -0,0 +1,284 @@ + + + diff --git a/backend/src/views/apps/crm/supplier/index.vue b/backend/src/views/apps/crm/supplier/index.vue new file mode 100644 index 0000000..083bd3e --- /dev/null +++ b/backend/src/views/apps/crm/supplier/index.vue @@ -0,0 +1,533 @@ + + + + +