上一条和下一条功能实现
This commit is contained in:
parent
903c0d3930
commit
9beb069550
|
|
@ -3,7 +3,7 @@ import {
|
|||
} from "./chunk-FKOWI4VU.js";
|
||||
import {
|
||||
ElMessage
|
||||
} from "./chunk-QM5QNHIO.js";
|
||||
} from "./chunk-IRFI6G53.js";
|
||||
import "./chunk-TQUTZUXW.js";
|
||||
import "./chunk-GVKQVKU2.js";
|
||||
import "./chunk-PHKUHJQP.js";
|
||||
|
|
@ -11,6 +11,7 @@ import "./chunk-LROEKXT5.js";
|
|||
import "./chunk-O7KFMITO.js";
|
||||
import "./chunk-YNRHTVZR.js";
|
||||
import "./chunk-67TUTJCN.js";
|
||||
import "./chunk-7BPWZNUD.js";
|
||||
import "./chunk-ULX5FOVL.js";
|
||||
import {
|
||||
Fragment,
|
||||
|
|
@ -51,7 +52,6 @@ import {
|
|||
withKeys,
|
||||
withModifiers
|
||||
} from "./chunk-GTWINWNV.js";
|
||||
import "./chunk-7BPWZNUD.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
|
||||
// node_modules/.pnpm/@form-create+designer@3.2.8_vue@3.5.12_typescript@5.3.3_/node_modules/@form-create/designer/dist/index.es.js
|
||||
|
|
|
|||
|
|
@ -1,36 +1,35 @@
|
|||
import "./chunk-BCFMTI3R.js";
|
||||
import "./chunk-FBJWDERR.js";
|
||||
import "./chunk-4TBAE7E3.js";
|
||||
import "./chunk-OX6JP2AA.js";
|
||||
import "./chunk-5MJQOEES.js";
|
||||
import "./chunk-WTG273Z3.js";
|
||||
import "./chunk-46FTKYZS.js";
|
||||
import "./chunk-4TBAE7E3.js";
|
||||
import "./chunk-C373DD4S.js";
|
||||
import "./chunk-HZRC7S76.js";
|
||||
import "./chunk-DWEZRTLP.js";
|
||||
import "./chunk-5CTUXGGF.js";
|
||||
import "./chunk-B4L4KVT2.js";
|
||||
import "./chunk-MGTDGDA4.js";
|
||||
import "./chunk-KARBCTKP.js";
|
||||
import "./chunk-OX6JP2AA.js";
|
||||
import "./chunk-JWIHIIT3.js";
|
||||
import "./chunk-MC27QSJL.js";
|
||||
import "./chunk-UUC56VKA.js";
|
||||
import "./chunk-HGRMPKRI.js";
|
||||
import "./chunk-NZR6SVVT.js";
|
||||
import "./chunk-B4L4KVT2.js";
|
||||
import "./chunk-WTG273Z3.js";
|
||||
import "./chunk-MGTDGDA4.js";
|
||||
import "./chunk-T6DOWK6H.js";
|
||||
import "./chunk-ZEGP4DVR.js";
|
||||
import "./chunk-FRMPVTTW.js";
|
||||
import "./chunk-DWEZRTLP.js";
|
||||
import "./chunk-5CTUXGGF.js";
|
||||
import "./chunk-MB7KUOZ6.js";
|
||||
import "./chunk-5KQYIVGF.js";
|
||||
import "./chunk-7EBAVFZW.js";
|
||||
import "./chunk-IIHLKWL5.js";
|
||||
import "./chunk-JQ2IYF3Y.js";
|
||||
import "./chunk-THZSTYZP.js";
|
||||
import "./chunk-DB6OWVVK.js";
|
||||
import "./chunk-FRMPVTTW.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-JQ2IYF3Y.js";
|
||||
import "./chunk-T6DOWK6H.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-KARBCTKP.js";
|
||||
import "./chunk-ZEGP4DVR.js";
|
||||
import "./chunk-IIHLKWL5.js";
|
||||
import "./chunk-7EBAVFZW.js";
|
||||
import "./chunk-MB7KUOZ6.js";
|
||||
import "./chunk-VLWH5T2T.js";
|
||||
import "./chunk-5KQYIVGF.js";
|
||||
import {
|
||||
ElAutocomplete,
|
||||
ElButton,
|
||||
|
|
@ -60,18 +59,19 @@ import {
|
|||
ElTooltip,
|
||||
ElTree,
|
||||
ElUpload
|
||||
} from "./chunk-QM5QNHIO.js";
|
||||
} from "./chunk-IRFI6G53.js";
|
||||
import "./chunk-TQUTZUXW.js";
|
||||
import "./chunk-GVKQVKU2.js";
|
||||
import "./chunk-PHKUHJQP.js";
|
||||
import "./chunk-LROEKXT5.js";
|
||||
import "./chunk-O7KFMITO.js";
|
||||
import "./chunk-VLWH5T2T.js";
|
||||
import "./chunk-YNRHTVZR.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
import "./chunk-67TUTJCN.js";
|
||||
import "./chunk-7BPWZNUD.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
import "./chunk-ULX5FOVL.js";
|
||||
import "./chunk-GTWINWNV.js";
|
||||
import "./chunk-7BPWZNUD.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
|
||||
// node_modules/.pnpm/@form-create+element-ui@3.2.14_vue@3.5.12_typescript@5.3.3_/node_modules/@form-create/element-ui/auto-import.js
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,19 @@
|
|||
import {
|
||||
PaletteProvider
|
||||
} from "./chunk-2VL3FEXP.js";
|
||||
LANE_INDENTATION,
|
||||
collectLanes,
|
||||
computeChildrenBBox,
|
||||
computeLanesResize,
|
||||
ensureConstraints,
|
||||
getChildLanes,
|
||||
getLanesRoot,
|
||||
getMinResizeBounds,
|
||||
resizeBounds,
|
||||
substractTRBL
|
||||
} from "./chunk-YD4V2JYA.js";
|
||||
import {
|
||||
getParent,
|
||||
isDirectionHorizontal
|
||||
} from "./chunk-KKQ6WPIB.js";
|
||||
import {
|
||||
BaseViewer,
|
||||
CommandInterceptor,
|
||||
|
|
@ -42,7 +55,31 @@ import {
|
|||
transform,
|
||||
translate,
|
||||
translate_default
|
||||
} from "./chunk-NTWXIMGM.js";
|
||||
} from "./chunk-HOG6PRDN.js";
|
||||
import {
|
||||
asBounds,
|
||||
asTRBL,
|
||||
eachElement,
|
||||
filterRedundantWaypoints,
|
||||
findPathIntersections,
|
||||
getBBox,
|
||||
getClosure,
|
||||
getElementLineIntersection,
|
||||
getEnclosedElements,
|
||||
getMid,
|
||||
getMidPoint,
|
||||
getOrientation,
|
||||
getParents,
|
||||
isConnection,
|
||||
isLabel,
|
||||
pointDistance,
|
||||
pointInRect,
|
||||
pointsAligned,
|
||||
pointsOnLine,
|
||||
roundBounds,
|
||||
roundPoint,
|
||||
selfAndAllChildren
|
||||
} from "./chunk-HXHANZ4Q.js";
|
||||
import {
|
||||
append,
|
||||
attr as attr2,
|
||||
|
|
@ -68,46 +105,6 @@ import {
|
|||
query,
|
||||
remove
|
||||
} from "./chunk-PBO22ZEO.js";
|
||||
import {
|
||||
LANE_INDENTATION,
|
||||
collectLanes,
|
||||
computeChildrenBBox,
|
||||
computeLanesResize,
|
||||
ensureConstraints,
|
||||
getChildLanes,
|
||||
getLanesRoot,
|
||||
getMinResizeBounds,
|
||||
resizeBounds,
|
||||
substractTRBL
|
||||
} from "./chunk-UEIE3ZOK.js";
|
||||
import {
|
||||
asBounds,
|
||||
asTRBL,
|
||||
eachElement,
|
||||
filterRedundantWaypoints,
|
||||
findPathIntersections,
|
||||
getBBox,
|
||||
getClosure,
|
||||
getElementLineIntersection,
|
||||
getEnclosedElements,
|
||||
getMid,
|
||||
getMidPoint,
|
||||
getOrientation,
|
||||
getParents,
|
||||
isConnection,
|
||||
isLabel,
|
||||
pointDistance,
|
||||
pointInRect,
|
||||
pointsAligned,
|
||||
pointsOnLine,
|
||||
roundBounds,
|
||||
roundPoint,
|
||||
selfAndAllChildren
|
||||
} from "./chunk-T4R4535C.js";
|
||||
import {
|
||||
getParent,
|
||||
isDirectionHorizontal
|
||||
} from "./chunk-KKQ6WPIB.js";
|
||||
import {
|
||||
hasCompensateEventDefinition,
|
||||
hasErrorEventDefinition,
|
||||
|
|
@ -124,6 +121,9 @@ import {
|
|||
is,
|
||||
isAny
|
||||
} from "./chunk-FNF472WR.js";
|
||||
import {
|
||||
PaletteProvider
|
||||
} from "./chunk-2VL3FEXP.js";
|
||||
import {
|
||||
assign,
|
||||
bind,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
Viewer
|
||||
} from "./chunk-NTWXIMGM.js";
|
||||
} from "./chunk-HOG6PRDN.js";
|
||||
import "./chunk-HXHANZ4Q.js";
|
||||
import "./chunk-J6RTEKLL.js";
|
||||
import "./chunk-PBO22ZEO.js";
|
||||
import "./chunk-T4R4535C.js";
|
||||
import "./chunk-7C6J56BH.js";
|
||||
import "./chunk-FNF472WR.js";
|
||||
import "./chunk-YTJ5ESGD.js";
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import {
|
|||
computeLanesResize,
|
||||
getChildLanes,
|
||||
getLanesRoot
|
||||
} from "./chunk-UEIE3ZOK.js";
|
||||
import "./chunk-T4R4535C.js";
|
||||
} from "./chunk-YD4V2JYA.js";
|
||||
import "./chunk-KKQ6WPIB.js";
|
||||
import "./chunk-HXHANZ4Q.js";
|
||||
import "./chunk-7C6J56BH.js";
|
||||
import "./chunk-FNF472WR.js";
|
||||
import "./chunk-YTJ5ESGD.js";
|
||||
|
|
|
|||
|
|
@ -1,985 +0,0 @@
|
|||
import {
|
||||
assign,
|
||||
every,
|
||||
filter,
|
||||
find,
|
||||
forEach,
|
||||
groupBy,
|
||||
has,
|
||||
isArray,
|
||||
isNumber,
|
||||
isObject,
|
||||
isUndefined,
|
||||
sortBy
|
||||
} from "./chunk-YTJ5ESGD.js";
|
||||
|
||||
// node_modules/.pnpm/diagram-js@14.11.3/node_modules/diagram-js/lib/util/Geometry.js
|
||||
function pointDistance(a, b) {
|
||||
if (!a || !b) {
|
||||
return -1;
|
||||
}
|
||||
return Math.sqrt(
|
||||
Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)
|
||||
);
|
||||
}
|
||||
function pointsOnLine(p, q, r, accuracy) {
|
||||
if (typeof accuracy === "undefined") {
|
||||
accuracy = 5;
|
||||
}
|
||||
if (!p || !q || !r) {
|
||||
return false;
|
||||
}
|
||||
var val = (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x), dist = pointDistance(p, q);
|
||||
return Math.abs(val / dist) <= accuracy;
|
||||
}
|
||||
var ALIGNED_THRESHOLD = 2;
|
||||
function pointsAligned(a, b) {
|
||||
var points = Array.from(arguments).flat();
|
||||
const axisMap = {
|
||||
"x": "v",
|
||||
"y": "h"
|
||||
};
|
||||
for (const [axis, orientation] of Object.entries(axisMap)) {
|
||||
if (pointsAlignedOnAxis(axis, points)) {
|
||||
return orientation;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function pointsAlignedOnAxis(axis, points) {
|
||||
const referencePoint = points[0];
|
||||
return every(points, function(point) {
|
||||
return Math.abs(referencePoint[axis] - point[axis]) <= ALIGNED_THRESHOLD;
|
||||
});
|
||||
}
|
||||
function pointInRect(p, rect, tolerance) {
|
||||
tolerance = tolerance || 0;
|
||||
return p.x > rect.x - tolerance && p.y > rect.y - tolerance && p.x < rect.x + rect.width + tolerance && p.y < rect.y + rect.height + tolerance;
|
||||
}
|
||||
function getMidPoint(p, q) {
|
||||
return {
|
||||
x: Math.round(p.x + (q.x - p.x) / 2),
|
||||
y: Math.round(p.y + (q.y - p.y) / 2)
|
||||
};
|
||||
}
|
||||
|
||||
// node_modules/.pnpm/path-intersection@3.1.0/node_modules/path-intersection/intersect.js
|
||||
var p2s = /,?([a-z]),?/gi;
|
||||
var toFloat = parseFloat;
|
||||
var math = Math;
|
||||
var PI = math.PI;
|
||||
var mmin = math.min;
|
||||
var mmax = math.max;
|
||||
var pow = math.pow;
|
||||
var abs = math.abs;
|
||||
var pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?[\s]*,?[\s]*)+)/ig;
|
||||
var pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)[\s]*,?[\s]*/ig;
|
||||
var isArray2 = Array.isArray || function(o) {
|
||||
return o instanceof Array;
|
||||
};
|
||||
function hasProperty(obj, property) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, property);
|
||||
}
|
||||
function clone(obj) {
|
||||
if (typeof obj == "function" || Object(obj) !== obj) {
|
||||
return obj;
|
||||
}
|
||||
var res = new obj.constructor();
|
||||
for (var key in obj) {
|
||||
if (hasProperty(obj, key)) {
|
||||
res[key] = clone(obj[key]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function repush(array, item) {
|
||||
for (var i = 0, ii = array.length; i < ii; i++)
|
||||
if (array[i] === item) {
|
||||
return array.push(array.splice(i, 1)[0]);
|
||||
}
|
||||
}
|
||||
function cacher(f) {
|
||||
function newf() {
|
||||
var arg = Array.prototype.slice.call(arguments, 0), args = arg.join("␀"), cache = newf.cache = newf.cache || {}, count = newf.count = newf.count || [];
|
||||
if (hasProperty(cache, args)) {
|
||||
repush(count, args);
|
||||
return cache[args];
|
||||
}
|
||||
count.length >= 1e3 && delete cache[count.shift()];
|
||||
count.push(args);
|
||||
cache[args] = f(...arguments);
|
||||
return cache[args];
|
||||
}
|
||||
return newf;
|
||||
}
|
||||
function parsePathString(pathString) {
|
||||
if (!pathString) {
|
||||
return null;
|
||||
}
|
||||
var pth = paths(pathString);
|
||||
if (pth.arr) {
|
||||
return clone(pth.arr);
|
||||
}
|
||||
var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0 }, data = [];
|
||||
if (isArray2(pathString) && isArray2(pathString[0])) {
|
||||
data = clone(pathString);
|
||||
}
|
||||
if (!data.length) {
|
||||
String(pathString).replace(pathCommand, function(a, b, c) {
|
||||
var params = [], name = b.toLowerCase();
|
||||
c.replace(pathValues, function(a2, b2) {
|
||||
b2 && params.push(+b2);
|
||||
});
|
||||
if (name == "m" && params.length > 2) {
|
||||
data.push([b, ...params.splice(0, 2)]);
|
||||
name = "l";
|
||||
b = b == "m" ? "l" : "L";
|
||||
}
|
||||
while (params.length >= paramCounts[name]) {
|
||||
data.push([b, ...params.splice(0, paramCounts[name])]);
|
||||
if (!paramCounts[name]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
data.toString = paths.toString;
|
||||
pth.arr = clone(data);
|
||||
return data;
|
||||
}
|
||||
function paths(ps) {
|
||||
var p = paths.ps = paths.ps || {};
|
||||
if (p[ps]) {
|
||||
p[ps].sleep = 100;
|
||||
} else {
|
||||
p[ps] = {
|
||||
sleep: 100
|
||||
};
|
||||
}
|
||||
setTimeout(function() {
|
||||
for (var key in p) {
|
||||
if (hasProperty(p, key) && key != ps) {
|
||||
p[key].sleep--;
|
||||
!p[key].sleep && delete p[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
return p[ps];
|
||||
}
|
||||
function rectBBox(x, y, width, height) {
|
||||
if (arguments.length === 1) {
|
||||
y = x.y;
|
||||
width = x.width;
|
||||
height = x.height;
|
||||
x = x.x;
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
x2: x + width,
|
||||
y2: y + height
|
||||
};
|
||||
}
|
||||
function pathToString() {
|
||||
return this.join(",").replace(p2s, "$1");
|
||||
}
|
||||
function pathClone(pathArray) {
|
||||
var res = clone(pathArray);
|
||||
res.toString = pathToString;
|
||||
return res;
|
||||
}
|
||||
function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
|
||||
var t1 = 1 - t, t13 = pow(t1, 3), t12 = pow(t1, 2), t2 = t * t, t3 = t2 * t, x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x, y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y;
|
||||
return {
|
||||
x: fixError(x),
|
||||
y: fixError(y)
|
||||
};
|
||||
}
|
||||
function bezierBBox(points) {
|
||||
var bbox = curveBBox(...points);
|
||||
return rectBBox(
|
||||
bbox.x0,
|
||||
bbox.y0,
|
||||
bbox.x1 - bbox.x0,
|
||||
bbox.y1 - bbox.y0
|
||||
);
|
||||
}
|
||||
function isPointInsideBBox(bbox, x, y) {
|
||||
return x >= bbox.x && x <= bbox.x + bbox.width && y >= bbox.y && y <= bbox.y + bbox.height;
|
||||
}
|
||||
function isBBoxIntersect(bbox1, bbox2) {
|
||||
bbox1 = rectBBox(bbox1);
|
||||
bbox2 = rectBBox(bbox2);
|
||||
return isPointInsideBBox(bbox2, bbox1.x, bbox1.y) || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y) || isPointInsideBBox(bbox2, bbox1.x, bbox1.y2) || isPointInsideBBox(bbox2, bbox1.x2, bbox1.y2) || isPointInsideBBox(bbox1, bbox2.x, bbox2.y) || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y) || isPointInsideBBox(bbox1, bbox2.x, bbox2.y2) || isPointInsideBBox(bbox1, bbox2.x2, bbox2.y2) || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x) && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y);
|
||||
}
|
||||
function base3(t, p1, p2, p3, p4) {
|
||||
var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4, t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
|
||||
return t * t2 - 3 * p1 + 3 * p2;
|
||||
}
|
||||
function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
|
||||
if (z == null) {
|
||||
z = 1;
|
||||
}
|
||||
z = z > 1 ? 1 : z < 0 ? 0 : z;
|
||||
var z2 = z / 2, n = 12, Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678, -0.5873, 0.5873, -0.7699, 0.7699, -0.9041, 0.9041, -0.9816, 0.9816], Cvalues = [0.2491, 0.2491, 0.2335, 0.2335, 0.2032, 0.2032, 0.1601, 0.1601, 0.1069, 0.1069, 0.0472, 0.0472], sum = 0;
|
||||
for (var i = 0; i < n; i++) {
|
||||
var ct = z2 * Tvalues[i] + z2, xbase = base3(ct, x1, x2, x3, x4), ybase = base3(ct, y1, y2, y3, y4), comb = xbase * xbase + ybase * ybase;
|
||||
sum += Cvalues[i] * math.sqrt(comb);
|
||||
}
|
||||
return z2 * sum;
|
||||
}
|
||||
function intersectLines(x1, y1, x2, y2, x3, y3, x4, y4) {
|
||||
if (mmax(x1, x2) < mmin(x3, x4) || mmin(x1, x2) > mmax(x3, x4) || mmax(y1, y2) < mmin(y3, y4) || mmin(y1, y2) > mmax(y3, y4)) {
|
||||
return;
|
||||
}
|
||||
var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4), ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4), denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
if (!denominator) {
|
||||
return;
|
||||
}
|
||||
var px = fixError(nx / denominator), py = fixError(ny / denominator), px2 = +px.toFixed(2), py2 = +py.toFixed(2);
|
||||
if (px2 < +mmin(x1, x2).toFixed(2) || px2 > +mmax(x1, x2).toFixed(2) || px2 < +mmin(x3, x4).toFixed(2) || px2 > +mmax(x3, x4).toFixed(2) || py2 < +mmin(y1, y2).toFixed(2) || py2 > +mmax(y1, y2).toFixed(2) || py2 < +mmin(y3, y4).toFixed(2) || py2 > +mmax(y3, y4).toFixed(2)) {
|
||||
return;
|
||||
}
|
||||
return { x: px, y: py };
|
||||
}
|
||||
function fixError(number) {
|
||||
return Math.round(number * 1e11) / 1e11;
|
||||
}
|
||||
function findBezierIntersections(bez1, bez2, justCount) {
|
||||
var bbox1 = bezierBBox(bez1), bbox2 = bezierBBox(bez2);
|
||||
if (!isBBoxIntersect(bbox1, bbox2)) {
|
||||
return justCount ? 0 : [];
|
||||
}
|
||||
var l1 = bezlen(...bez1), l2 = bezlen(...bez2), n1 = isLine(bez1) ? 1 : ~~(l1 / 5) || 1, n2 = isLine(bez2) ? 1 : ~~(l2 / 5) || 1, dots1 = [], dots2 = [], xy = {}, res = justCount ? 0 : [];
|
||||
for (var i = 0; i < n1 + 1; i++) {
|
||||
var p = findDotsAtSegment(...bez1, i / n1);
|
||||
dots1.push({ x: p.x, y: p.y, t: i / n1 });
|
||||
}
|
||||
for (i = 0; i < n2 + 1; i++) {
|
||||
p = findDotsAtSegment(...bez2, i / n2);
|
||||
dots2.push({ x: p.x, y: p.y, t: i / n2 });
|
||||
}
|
||||
for (i = 0; i < n1; i++) {
|
||||
for (var j = 0; j < n2; j++) {
|
||||
var di = dots1[i], di1 = dots1[i + 1], dj = dots2[j], dj1 = dots2[j + 1], ci = abs(di1.x - di.x) < 0.01 ? "y" : "x", cj = abs(dj1.x - dj.x) < 0.01 ? "y" : "x", is = intersectLines(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y), key;
|
||||
if (is) {
|
||||
key = is.x.toFixed(9) + "#" + is.y.toFixed(9);
|
||||
if (xy[key]) {
|
||||
continue;
|
||||
}
|
||||
xy[key] = true;
|
||||
var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t), t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t);
|
||||
if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) {
|
||||
if (justCount) {
|
||||
res++;
|
||||
} else {
|
||||
res.push({
|
||||
x: is.x,
|
||||
y: is.y,
|
||||
t1,
|
||||
t2
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function findPathIntersections(path1, path2, justCount) {
|
||||
path1 = pathToCurve(path1);
|
||||
path2 = pathToCurve(path2);
|
||||
var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2, res = justCount ? 0 : [];
|
||||
for (var i = 0, ii = path1.length; i < ii; i++) {
|
||||
var pi = path1[i];
|
||||
if (pi[0] == "M") {
|
||||
x1 = x1m = pi[1];
|
||||
y1 = y1m = pi[2];
|
||||
} else {
|
||||
if (pi[0] == "C") {
|
||||
bez1 = [x1, y1, ...pi.slice(1)];
|
||||
x1 = bez1[6];
|
||||
y1 = bez1[7];
|
||||
} else {
|
||||
bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m];
|
||||
x1 = x1m;
|
||||
y1 = y1m;
|
||||
}
|
||||
for (var j = 0, jj = path2.length; j < jj; j++) {
|
||||
var pj = path2[j];
|
||||
if (pj[0] == "M") {
|
||||
x2 = x2m = pj[1];
|
||||
y2 = y2m = pj[2];
|
||||
} else {
|
||||
if (pj[0] == "C") {
|
||||
bez2 = [x2, y2, ...pj.slice(1)];
|
||||
x2 = bez2[6];
|
||||
y2 = bez2[7];
|
||||
} else {
|
||||
bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m];
|
||||
x2 = x2m;
|
||||
y2 = y2m;
|
||||
}
|
||||
var intr = findBezierIntersections(bez1, bez2, justCount);
|
||||
if (justCount) {
|
||||
res += intr;
|
||||
} else {
|
||||
for (var k = 0, kk = intr.length; k < kk; k++) {
|
||||
intr[k].segment1 = i;
|
||||
intr[k].segment2 = j;
|
||||
intr[k].bez1 = bez1;
|
||||
intr[k].bez2 = bez2;
|
||||
}
|
||||
res = res.concat(intr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function pathToAbsolute(pathArray) {
|
||||
var pth = paths(pathArray);
|
||||
if (pth.abs) {
|
||||
return pathClone(pth.abs);
|
||||
}
|
||||
if (!isArray2(pathArray) || !isArray2(pathArray && pathArray[0])) {
|
||||
pathArray = parsePathString(pathArray);
|
||||
}
|
||||
if (!pathArray || !pathArray.length) {
|
||||
return [["M", 0, 0]];
|
||||
}
|
||||
var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0, pa0;
|
||||
if (pathArray[0][0] == "M") {
|
||||
x = +pathArray[0][1];
|
||||
y = +pathArray[0][2];
|
||||
mx = x;
|
||||
my = y;
|
||||
start++;
|
||||
res[0] = ["M", x, y];
|
||||
}
|
||||
for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
|
||||
res.push(r = []);
|
||||
pa = pathArray[i];
|
||||
pa0 = pa[0];
|
||||
if (pa0 != pa0.toUpperCase()) {
|
||||
r[0] = pa0.toUpperCase();
|
||||
switch (r[0]) {
|
||||
case "A":
|
||||
r[1] = pa[1];
|
||||
r[2] = pa[2];
|
||||
r[3] = pa[3];
|
||||
r[4] = pa[4];
|
||||
r[5] = pa[5];
|
||||
r[6] = +pa[6] + x;
|
||||
r[7] = +pa[7] + y;
|
||||
break;
|
||||
case "V":
|
||||
r[1] = +pa[1] + y;
|
||||
break;
|
||||
case "H":
|
||||
r[1] = +pa[1] + x;
|
||||
break;
|
||||
case "M":
|
||||
mx = +pa[1] + x;
|
||||
my = +pa[2] + y;
|
||||
default:
|
||||
for (var j = 1, jj = pa.length; j < jj; j++) {
|
||||
r[j] = +pa[j] + (j % 2 ? x : y);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var k = 0, kk = pa.length; k < kk; k++) {
|
||||
r[k] = pa[k];
|
||||
}
|
||||
}
|
||||
pa0 = pa0.toUpperCase();
|
||||
switch (r[0]) {
|
||||
case "Z":
|
||||
x = +mx;
|
||||
y = +my;
|
||||
break;
|
||||
case "H":
|
||||
x = r[1];
|
||||
break;
|
||||
case "V":
|
||||
y = r[1];
|
||||
break;
|
||||
case "M":
|
||||
mx = r[r.length - 2];
|
||||
my = r[r.length - 1];
|
||||
default:
|
||||
x = r[r.length - 2];
|
||||
y = r[r.length - 1];
|
||||
}
|
||||
}
|
||||
res.toString = pathToString;
|
||||
pth.abs = pathClone(res);
|
||||
return res;
|
||||
}
|
||||
function isLine(bez) {
|
||||
return bez[0] === bez[2] && bez[1] === bez[3] && bez[4] === bez[6] && bez[5] === bez[7];
|
||||
}
|
||||
function lineToCurve(x1, y1, x2, y2) {
|
||||
return [
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
x2,
|
||||
y2
|
||||
];
|
||||
}
|
||||
function qubicToCurve(x1, y1, ax, ay, x2, y2) {
|
||||
var _13 = 1 / 3, _23 = 2 / 3;
|
||||
return [
|
||||
_13 * x1 + _23 * ax,
|
||||
_13 * y1 + _23 * ay,
|
||||
_13 * x2 + _23 * ax,
|
||||
_13 * y2 + _23 * ay,
|
||||
x2,
|
||||
y2
|
||||
];
|
||||
}
|
||||
function arcToCurve(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
|
||||
var _120 = PI * 120 / 180, rad = PI / 180 * (+angle || 0), res = [], xy, rotate = cacher(function(x3, y3, rad2) {
|
||||
var X = x3 * math.cos(rad2) - y3 * math.sin(rad2), Y = x3 * math.sin(rad2) + y3 * math.cos(rad2);
|
||||
return { x: X, y: Y };
|
||||
});
|
||||
if (!recursive) {
|
||||
xy = rotate(x1, y1, -rad);
|
||||
x1 = xy.x;
|
||||
y1 = xy.y;
|
||||
xy = rotate(x2, y2, -rad);
|
||||
x2 = xy.x;
|
||||
y2 = xy.y;
|
||||
var x = (x1 - x2) / 2, y = (y1 - y2) / 2;
|
||||
var h = x * x / (rx * rx) + y * y / (ry * ry);
|
||||
if (h > 1) {
|
||||
h = math.sqrt(h);
|
||||
rx = h * rx;
|
||||
ry = h * ry;
|
||||
}
|
||||
var rx2 = rx * rx, ry2 = ry * ry, k = (large_arc_flag == sweep_flag ? -1 : 1) * math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), cx = k * rx * y / ry + (x1 + x2) / 2, cy = k * -ry * x / rx + (y1 + y2) / 2, f1 = math.asin(((y1 - cy) / ry).toFixed(9)), f2 = math.asin(((y2 - cy) / ry).toFixed(9));
|
||||
f1 = x1 < cx ? PI - f1 : f1;
|
||||
f2 = x2 < cx ? PI - f2 : f2;
|
||||
f1 < 0 && (f1 = PI * 2 + f1);
|
||||
f2 < 0 && (f2 = PI * 2 + f2);
|
||||
if (sweep_flag && f1 > f2) {
|
||||
f1 = f1 - PI * 2;
|
||||
}
|
||||
if (!sweep_flag && f2 > f1) {
|
||||
f2 = f2 - PI * 2;
|
||||
}
|
||||
} else {
|
||||
f1 = recursive[0];
|
||||
f2 = recursive[1];
|
||||
cx = recursive[2];
|
||||
cy = recursive[3];
|
||||
}
|
||||
var df = f2 - f1;
|
||||
if (abs(df) > _120) {
|
||||
var f2old = f2, x2old = x2, y2old = y2;
|
||||
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
|
||||
x2 = cx + rx * math.cos(f2);
|
||||
y2 = cy + ry * math.sin(f2);
|
||||
res = arcToCurve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
|
||||
}
|
||||
df = f2 - f1;
|
||||
var c1 = math.cos(f1), s1 = math.sin(f1), c2 = math.cos(f2), s2 = math.sin(f2), t = math.tan(df / 4), hx = 4 / 3 * rx * t, hy = 4 / 3 * ry * t, m1 = [x1, y1], m2 = [x1 + hx * s1, y1 - hy * c1], m3 = [x2 + hx * s2, y2 - hy * c2], m4 = [x2, y2];
|
||||
m2[0] = 2 * m1[0] - m2[0];
|
||||
m2[1] = 2 * m1[1] - m2[1];
|
||||
if (recursive) {
|
||||
return [m2, m3, m4].concat(res);
|
||||
} else {
|
||||
res = [m2, m3, m4].concat(res).join().split(",");
|
||||
var newres = [];
|
||||
for (var i = 0, ii = res.length; i < ii; i++) {
|
||||
newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
|
||||
}
|
||||
return newres;
|
||||
}
|
||||
}
|
||||
function curveBBox(x0, y0, x1, y1, x2, y2, x3, y3) {
|
||||
var tvalues = [], bounds = [[], []], a, b, c, t, t1, t2, b2ac, sqrtb2ac;
|
||||
for (var i = 0; i < 2; ++i) {
|
||||
if (i == 0) {
|
||||
b = 6 * x0 - 12 * x1 + 6 * x2;
|
||||
a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
|
||||
c = 3 * x1 - 3 * x0;
|
||||
} else {
|
||||
b = 6 * y0 - 12 * y1 + 6 * y2;
|
||||
a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
|
||||
c = 3 * y1 - 3 * y0;
|
||||
}
|
||||
if (abs(a) < 1e-12) {
|
||||
if (abs(b) < 1e-12) {
|
||||
continue;
|
||||
}
|
||||
t = -c / b;
|
||||
if (0 < t && t < 1) {
|
||||
tvalues.push(t);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
b2ac = b * b - 4 * c * a;
|
||||
sqrtb2ac = math.sqrt(b2ac);
|
||||
if (b2ac < 0) {
|
||||
continue;
|
||||
}
|
||||
t1 = (-b + sqrtb2ac) / (2 * a);
|
||||
if (0 < t1 && t1 < 1) {
|
||||
tvalues.push(t1);
|
||||
}
|
||||
t2 = (-b - sqrtb2ac) / (2 * a);
|
||||
if (0 < t2 && t2 < 1) {
|
||||
tvalues.push(t2);
|
||||
}
|
||||
}
|
||||
var j = tvalues.length, jlen = j, mt;
|
||||
while (j--) {
|
||||
t = tvalues[j];
|
||||
mt = 1 - t;
|
||||
bounds[0][j] = mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3;
|
||||
bounds[1][j] = mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3;
|
||||
}
|
||||
bounds[0][jlen] = x0;
|
||||
bounds[1][jlen] = y0;
|
||||
bounds[0][jlen + 1] = x3;
|
||||
bounds[1][jlen + 1] = y3;
|
||||
bounds[0].length = bounds[1].length = jlen + 2;
|
||||
return {
|
||||
x0: mmin(...bounds[0]),
|
||||
y0: mmin(...bounds[1]),
|
||||
x1: mmax(...bounds[0]),
|
||||
y1: mmax(...bounds[1])
|
||||
};
|
||||
}
|
||||
function pathToCurve(path) {
|
||||
var pth = paths(path);
|
||||
if (pth.curve) {
|
||||
return pathClone(pth.curve);
|
||||
}
|
||||
var curvedPath = pathToAbsolute(path), attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null }, processPath = function(path2, d, pathCommand3) {
|
||||
var nx, ny;
|
||||
if (!path2) {
|
||||
return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
|
||||
}
|
||||
!(path2[0] in { T: 1, Q: 1 }) && (d.qx = d.qy = null);
|
||||
switch (path2[0]) {
|
||||
case "M":
|
||||
d.X = path2[1];
|
||||
d.Y = path2[2];
|
||||
break;
|
||||
case "A":
|
||||
path2 = ["C", ...arcToCurve(d.x, d.y, ...path2.slice(1))];
|
||||
break;
|
||||
case "S":
|
||||
if (pathCommand3 == "C" || pathCommand3 == "S") {
|
||||
nx = d.x * 2 - d.bx;
|
||||
ny = d.y * 2 - d.by;
|
||||
} else {
|
||||
nx = d.x;
|
||||
ny = d.y;
|
||||
}
|
||||
path2 = ["C", nx, ny, ...path2.slice(1)];
|
||||
break;
|
||||
case "T":
|
||||
if (pathCommand3 == "Q" || pathCommand3 == "T") {
|
||||
d.qx = d.x * 2 - d.qx;
|
||||
d.qy = d.y * 2 - d.qy;
|
||||
} else {
|
||||
d.qx = d.x;
|
||||
d.qy = d.y;
|
||||
}
|
||||
path2 = ["C", ...qubicToCurve(d.x, d.y, d.qx, d.qy, path2[1], path2[2])];
|
||||
break;
|
||||
case "Q":
|
||||
d.qx = path2[1];
|
||||
d.qy = path2[2];
|
||||
path2 = ["C", ...qubicToCurve(d.x, d.y, path2[1], path2[2], path2[3], path2[4])];
|
||||
break;
|
||||
case "L":
|
||||
path2 = ["C", ...lineToCurve(d.x, d.y, path2[1], path2[2])];
|
||||
break;
|
||||
case "H":
|
||||
path2 = ["C", ...lineToCurve(d.x, d.y, path2[1], d.y)];
|
||||
break;
|
||||
case "V":
|
||||
path2 = ["C", ...lineToCurve(d.x, d.y, d.x, path2[1])];
|
||||
break;
|
||||
case "Z":
|
||||
path2 = ["C", ...lineToCurve(d.x, d.y, d.X, d.Y)];
|
||||
break;
|
||||
}
|
||||
return path2;
|
||||
}, fixArc = function(pp, i2) {
|
||||
if (pp[i2].length > 7) {
|
||||
pp[i2].shift();
|
||||
var pi = pp[i2];
|
||||
while (pi.length) {
|
||||
pathCommands[i2] = "A";
|
||||
pp.splice(i2++, 0, ["C", ...pi.splice(0, 6)]);
|
||||
}
|
||||
pp.splice(i2, 1);
|
||||
ii = curvedPath.length;
|
||||
}
|
||||
}, pathCommands = [], pfirst = "", pathCommand2 = "";
|
||||
for (var i = 0, ii = curvedPath.length; i < ii; i++) {
|
||||
curvedPath[i] && (pfirst = curvedPath[i][0]);
|
||||
if (pfirst != "C") {
|
||||
pathCommands[i] = pfirst;
|
||||
i && (pathCommand2 = pathCommands[i - 1]);
|
||||
}
|
||||
curvedPath[i] = processPath(curvedPath[i], attrs, pathCommand2);
|
||||
if (pathCommands[i] != "A" && pfirst == "C")
|
||||
pathCommands[i] = "C";
|
||||
fixArc(curvedPath, i);
|
||||
var seg = curvedPath[i], seglen = seg.length;
|
||||
attrs.x = seg[seglen - 2];
|
||||
attrs.y = seg[seglen - 1];
|
||||
attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
|
||||
attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
|
||||
}
|
||||
pth.curve = pathClone(curvedPath);
|
||||
return curvedPath;
|
||||
}
|
||||
|
||||
// node_modules/.pnpm/diagram-js@14.11.3/node_modules/diagram-js/lib/util/ModelUtil.js
|
||||
function isConnection(value) {
|
||||
return isObject(value) && has(value, "waypoints");
|
||||
}
|
||||
function isLabel(value) {
|
||||
return isObject(value) && has(value, "labelTarget");
|
||||
}
|
||||
|
||||
// node_modules/.pnpm/diagram-js@14.11.3/node_modules/diagram-js/lib/layout/LayoutUtil.js
|
||||
function roundBounds(bounds) {
|
||||
return {
|
||||
x: Math.round(bounds.x),
|
||||
y: Math.round(bounds.y),
|
||||
width: Math.round(bounds.width),
|
||||
height: Math.round(bounds.height)
|
||||
};
|
||||
}
|
||||
function roundPoint(point) {
|
||||
return {
|
||||
x: Math.round(point.x),
|
||||
y: Math.round(point.y)
|
||||
};
|
||||
}
|
||||
function asTRBL(bounds) {
|
||||
return {
|
||||
top: bounds.y,
|
||||
right: bounds.x + (bounds.width || 0),
|
||||
bottom: bounds.y + (bounds.height || 0),
|
||||
left: bounds.x
|
||||
};
|
||||
}
|
||||
function asBounds(trbl) {
|
||||
return {
|
||||
x: trbl.left,
|
||||
y: trbl.top,
|
||||
width: trbl.right - trbl.left,
|
||||
height: trbl.bottom - trbl.top
|
||||
};
|
||||
}
|
||||
function getBoundsMid(bounds) {
|
||||
return roundPoint({
|
||||
x: bounds.x + (bounds.width || 0) / 2,
|
||||
y: bounds.y + (bounds.height || 0) / 2
|
||||
});
|
||||
}
|
||||
function getConnectionMid(connection) {
|
||||
var waypoints = connection.waypoints;
|
||||
var parts = waypoints.reduce(function(parts2, point, index) {
|
||||
var lastPoint = waypoints[index - 1];
|
||||
if (lastPoint) {
|
||||
var lastPart = parts2[parts2.length - 1];
|
||||
var startLength = lastPart && lastPart.endLength || 0;
|
||||
var length = distance(lastPoint, point);
|
||||
parts2.push({
|
||||
start: lastPoint,
|
||||
end: point,
|
||||
startLength,
|
||||
endLength: startLength + length,
|
||||
length
|
||||
});
|
||||
}
|
||||
return parts2;
|
||||
}, []);
|
||||
var totalLength = parts.reduce(function(length, part) {
|
||||
return length + part.length;
|
||||
}, 0);
|
||||
var midLength = totalLength / 2;
|
||||
var i = 0;
|
||||
var midSegment = parts[i];
|
||||
while (midSegment.endLength < midLength) {
|
||||
midSegment = parts[++i];
|
||||
}
|
||||
var segmentProgress = (midLength - midSegment.startLength) / midSegment.length;
|
||||
var midPoint = {
|
||||
x: midSegment.start.x + (midSegment.end.x - midSegment.start.x) * segmentProgress,
|
||||
y: midSegment.start.y + (midSegment.end.y - midSegment.start.y) * segmentProgress
|
||||
};
|
||||
return midPoint;
|
||||
}
|
||||
function getMid(element) {
|
||||
if (isConnection(element)) {
|
||||
return getConnectionMid(element);
|
||||
}
|
||||
return getBoundsMid(element);
|
||||
}
|
||||
function getOrientation(rect, reference, padding) {
|
||||
padding = padding || 0;
|
||||
if (!isObject(padding)) {
|
||||
padding = { x: padding, y: padding };
|
||||
}
|
||||
var rectOrientation = asTRBL(rect), referenceOrientation = asTRBL(reference);
|
||||
var top = rectOrientation.bottom + padding.y <= referenceOrientation.top, right = rectOrientation.left - padding.x >= referenceOrientation.right, bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom, left = rectOrientation.right + padding.x <= referenceOrientation.left;
|
||||
var vertical = top ? "top" : bottom ? "bottom" : null, horizontal = left ? "left" : right ? "right" : null;
|
||||
if (horizontal && vertical) {
|
||||
return vertical + "-" + horizontal;
|
||||
} else {
|
||||
return horizontal || vertical || "intersect";
|
||||
}
|
||||
}
|
||||
function getElementLineIntersection(elementPath, linePath, cropStart) {
|
||||
var intersections = getIntersections(elementPath, linePath);
|
||||
if (intersections.length === 1) {
|
||||
return roundPoint(intersections[0]);
|
||||
} else if (intersections.length === 2 && pointDistance(intersections[0], intersections[1]) < 1) {
|
||||
return roundPoint(intersections[0]);
|
||||
} else if (intersections.length > 1) {
|
||||
intersections = sortBy(intersections, function(i) {
|
||||
var distance2 = Math.floor(i.t2 * 100) || 1;
|
||||
distance2 = 100 - distance2;
|
||||
distance2 = (distance2 < 10 ? "0" : "") + distance2;
|
||||
return i.segment2 + "#" + distance2;
|
||||
});
|
||||
return roundPoint(intersections[cropStart ? 0 : intersections.length - 1]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function getIntersections(a, b) {
|
||||
return findPathIntersections(a, b);
|
||||
}
|
||||
function filterRedundantWaypoints(waypoints) {
|
||||
waypoints = waypoints.slice();
|
||||
var idx = 0, point, previousPoint, nextPoint;
|
||||
while (waypoints[idx]) {
|
||||
point = waypoints[idx];
|
||||
previousPoint = waypoints[idx - 1];
|
||||
nextPoint = waypoints[idx + 1];
|
||||
if (pointDistance(point, nextPoint) === 0 || pointsOnLine(previousPoint, nextPoint, point)) {
|
||||
waypoints.splice(idx, 1);
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
return waypoints;
|
||||
}
|
||||
function distance(a, b) {
|
||||
return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
|
||||
}
|
||||
|
||||
// node_modules/.pnpm/diagram-js@14.11.3/node_modules/diagram-js/lib/util/Elements.js
|
||||
function getParents(elements) {
|
||||
return filter(elements, function(element) {
|
||||
return !find(elements, function(e) {
|
||||
return e !== element && getParent(element, e);
|
||||
});
|
||||
});
|
||||
}
|
||||
function getParent(element, parent) {
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
if (element === parent) {
|
||||
return parent;
|
||||
}
|
||||
if (!element.parent) {
|
||||
return;
|
||||
}
|
||||
return getParent(element.parent, parent);
|
||||
}
|
||||
function add(elements, element, unique) {
|
||||
var canAdd = !unique || elements.indexOf(element) === -1;
|
||||
if (canAdd) {
|
||||
elements.push(element);
|
||||
}
|
||||
return canAdd;
|
||||
}
|
||||
function eachElement(elements, fn, depth) {
|
||||
depth = depth || 0;
|
||||
if (!isArray(elements)) {
|
||||
elements = [elements];
|
||||
}
|
||||
forEach(elements, function(s, i) {
|
||||
var filter2 = fn(s, i, depth);
|
||||
if (isArray(filter2) && filter2.length) {
|
||||
eachElement(filter2, fn, depth + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
function selfAndChildren(elements, unique, maxDepth) {
|
||||
var result = [], processedChildren = [];
|
||||
eachElement(elements, function(element, i, depth) {
|
||||
add(result, element, unique);
|
||||
var children = element.children;
|
||||
if (maxDepth === -1 || depth < maxDepth) {
|
||||
if (children && add(processedChildren, children, unique)) {
|
||||
return children;
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
function selfAndAllChildren(elements, allowDuplicates) {
|
||||
return selfAndChildren(elements, !allowDuplicates, -1);
|
||||
}
|
||||
function getClosure(elements, isTopLevel, closure) {
|
||||
if (isUndefined(isTopLevel)) {
|
||||
isTopLevel = true;
|
||||
}
|
||||
if (isObject(isTopLevel)) {
|
||||
closure = isTopLevel;
|
||||
isTopLevel = true;
|
||||
}
|
||||
closure = closure || {};
|
||||
var allShapes = copyObject(closure.allShapes), allConnections = copyObject(closure.allConnections), enclosedElements = copyObject(closure.enclosedElements), enclosedConnections = copyObject(closure.enclosedConnections);
|
||||
var topLevel = copyObject(
|
||||
closure.topLevel,
|
||||
isTopLevel && groupBy(elements, function(e) {
|
||||
return e.id;
|
||||
})
|
||||
);
|
||||
function handleConnection(c) {
|
||||
if (topLevel[c.source.id] && topLevel[c.target.id]) {
|
||||
topLevel[c.id] = [c];
|
||||
}
|
||||
if (allShapes[c.source.id] && allShapes[c.target.id]) {
|
||||
enclosedConnections[c.id] = enclosedElements[c.id] = c;
|
||||
}
|
||||
allConnections[c.id] = c;
|
||||
}
|
||||
function handleElement(element) {
|
||||
enclosedElements[element.id] = element;
|
||||
if (element.waypoints) {
|
||||
enclosedConnections[element.id] = allConnections[element.id] = element;
|
||||
} else {
|
||||
allShapes[element.id] = element;
|
||||
forEach(element.incoming, handleConnection);
|
||||
forEach(element.outgoing, handleConnection);
|
||||
return element.children;
|
||||
}
|
||||
}
|
||||
eachElement(elements, handleElement);
|
||||
return {
|
||||
allShapes,
|
||||
allConnections,
|
||||
topLevel,
|
||||
enclosedConnections,
|
||||
enclosedElements
|
||||
};
|
||||
}
|
||||
function getBBox(elements, stopRecursion) {
|
||||
stopRecursion = !!stopRecursion;
|
||||
if (!isArray(elements)) {
|
||||
elements = [elements];
|
||||
}
|
||||
var minX, minY, maxX, maxY;
|
||||
forEach(elements, function(element) {
|
||||
var bbox = element;
|
||||
if (element.waypoints && !stopRecursion) {
|
||||
bbox = getBBox(element.waypoints, true);
|
||||
}
|
||||
var x = bbox.x, y = bbox.y, height = bbox.height || 0, width = bbox.width || 0;
|
||||
if (x < minX || minX === void 0) {
|
||||
minX = x;
|
||||
}
|
||||
if (y < minY || minY === void 0) {
|
||||
minY = y;
|
||||
}
|
||||
if (x + width > maxX || maxX === void 0) {
|
||||
maxX = x + width;
|
||||
}
|
||||
if (y + height > maxY || maxY === void 0) {
|
||||
maxY = y + height;
|
||||
}
|
||||
});
|
||||
return {
|
||||
x: minX,
|
||||
y: minY,
|
||||
height: maxY - minY,
|
||||
width: maxX - minX
|
||||
};
|
||||
}
|
||||
function getEnclosedElements(elements, bbox) {
|
||||
var filteredElements = {};
|
||||
forEach(elements, function(element) {
|
||||
var e = element;
|
||||
if (e.waypoints) {
|
||||
e = getBBox(e);
|
||||
}
|
||||
if (!isNumber(bbox.y) && e.x > bbox.x) {
|
||||
filteredElements[element.id] = element;
|
||||
}
|
||||
if (!isNumber(bbox.x) && e.y > bbox.y) {
|
||||
filteredElements[element.id] = element;
|
||||
}
|
||||
if (e.x > bbox.x && e.y > bbox.y) {
|
||||
if (isNumber(bbox.width) && isNumber(bbox.height) && e.width + e.x < bbox.width + bbox.x && e.height + e.y < bbox.height + bbox.y) {
|
||||
filteredElements[element.id] = element;
|
||||
} else if (!isNumber(bbox.width) || !isNumber(bbox.height)) {
|
||||
filteredElements[element.id] = element;
|
||||
}
|
||||
}
|
||||
});
|
||||
return filteredElements;
|
||||
}
|
||||
function getType(element) {
|
||||
if ("waypoints" in element) {
|
||||
return "connection";
|
||||
}
|
||||
if ("x" in element) {
|
||||
return "shape";
|
||||
}
|
||||
return "root";
|
||||
}
|
||||
function isFrameElement(element) {
|
||||
return !!(element && element.isFrame);
|
||||
}
|
||||
function copyObject(src1, src2) {
|
||||
return assign({}, src1 || {}, src2 || {});
|
||||
}
|
||||
|
||||
export {
|
||||
pointDistance,
|
||||
pointsOnLine,
|
||||
pointsAligned,
|
||||
pointInRect,
|
||||
getMidPoint,
|
||||
findPathIntersections,
|
||||
isConnection,
|
||||
isLabel,
|
||||
roundBounds,
|
||||
roundPoint,
|
||||
asTRBL,
|
||||
asBounds,
|
||||
getMid,
|
||||
getOrientation,
|
||||
getElementLineIntersection,
|
||||
filterRedundantWaypoints,
|
||||
getParents,
|
||||
eachElement,
|
||||
selfAndAllChildren,
|
||||
getClosure,
|
||||
getBBox,
|
||||
getEnclosedElements,
|
||||
getType,
|
||||
isFrameElement
|
||||
};
|
||||
//# sourceMappingURL=chunk-T4R4535C.js.map
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
install as install2
|
||||
} from "./chunk-KJFX5ANN.js";
|
||||
import {
|
||||
install
|
||||
} from "./chunk-XNS7XN3L.js";
|
||||
} from "./chunk-KNIVXUEF.js";
|
||||
import {
|
||||
install as install2
|
||||
} from "./chunk-OBFEZ4QG.js";
|
||||
import {
|
||||
extendChartView,
|
||||
extendSeriesModel,
|
||||
|
|
@ -13,20 +13,20 @@ import {
|
|||
installLabelLayout,
|
||||
number_exports,
|
||||
util_exports
|
||||
} from "./chunk-2TFNBWBF.js";
|
||||
import "./chunk-X5CQ556A.js";
|
||||
import "./chunk-NF6IA4AL.js";
|
||||
} from "./chunk-M56X7I32.js";
|
||||
import "./chunk-LMKTAN4E.js";
|
||||
import "./chunk-V45EMKQW.js";
|
||||
import {
|
||||
SeriesData_default,
|
||||
registerLayout,
|
||||
registerPreprocessor,
|
||||
use
|
||||
} from "./chunk-QJLIGECE.js";
|
||||
import "./chunk-GVYX3QQL.js";
|
||||
} from "./chunk-ZCOAJCG3.js";
|
||||
import "./chunk-IWFMEAQB.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
|
||||
// node_modules/.pnpm/echarts@5.5.1/node_modules/echarts/lib/echarts.js
|
||||
use([install2, install]);
|
||||
use([install, install2]);
|
||||
use(installLabelLayout);
|
||||
|
||||
// node_modules/.pnpm/echarts-wordcloud@2.1.0_echarts@5.5.1/node_modules/echarts-wordcloud/src/WordCloudSeries.js
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,19 +1,20 @@
|
|||
import {
|
||||
install as install50
|
||||
} from "./chunk-6JA6BRXS.js";
|
||||
import {
|
||||
install as install27,
|
||||
install10 as install36,
|
||||
install11 as install37,
|
||||
install12 as install38,
|
||||
install13 as install39,
|
||||
install14 as install40,
|
||||
install15 as install41,
|
||||
install16 as install42,
|
||||
install17 as install43,
|
||||
install18 as install44,
|
||||
install19 as install45,
|
||||
install14 as install41,
|
||||
install15 as install42,
|
||||
install16 as install43,
|
||||
install17 as install44,
|
||||
install18 as install45,
|
||||
install19 as install46,
|
||||
install2 as install28,
|
||||
install20 as install46,
|
||||
install21 as install47,
|
||||
install22 as install48,
|
||||
install20 as install47,
|
||||
install21 as install48,
|
||||
install22 as install49,
|
||||
install3 as install29,
|
||||
install4 as install30,
|
||||
install5 as install31,
|
||||
|
|
@ -21,55 +22,54 @@ import {
|
|||
install7 as install33,
|
||||
install8 as install34,
|
||||
install9 as install35
|
||||
} from "./chunk-ND6EB7CF.js";
|
||||
} from "./chunk-AUPWZZDD.js";
|
||||
import {
|
||||
install as install50
|
||||
} from "./chunk-2I4JXH7Q.js";
|
||||
install as install40
|
||||
} from "./chunk-PRCWTGNB.js";
|
||||
import {
|
||||
install as install49
|
||||
} from "./chunk-KJFX5ANN.js";
|
||||
install as install39
|
||||
} from "./chunk-X72D7ZPW.js";
|
||||
import {
|
||||
install as install38
|
||||
} from "./chunk-XNXAGDXI.js";
|
||||
import "./chunk-EM6SBCMK.js";
|
||||
import {
|
||||
install,
|
||||
install10 as install12,
|
||||
install11 as install13,
|
||||
install14 as install17,
|
||||
install15 as install18,
|
||||
install16 as install19,
|
||||
install17 as install20,
|
||||
install18 as install21,
|
||||
install19 as install22,
|
||||
install10 as install11,
|
||||
install11 as install12,
|
||||
install12 as install14,
|
||||
install13 as install15,
|
||||
install14 as install16,
|
||||
install15 as install17,
|
||||
install16 as install18,
|
||||
install17 as install19,
|
||||
install18 as install20,
|
||||
install19 as install21,
|
||||
install2,
|
||||
install20 as install23,
|
||||
install21 as install24,
|
||||
install22 as install25,
|
||||
install20 as install22,
|
||||
install21 as install23,
|
||||
install22 as install24,
|
||||
install3,
|
||||
install4 as install5,
|
||||
install5 as install7,
|
||||
install6 as install8,
|
||||
install7 as install9,
|
||||
install8 as install10,
|
||||
install9 as install11
|
||||
} from "./chunk-NMP246CO.js";
|
||||
install4,
|
||||
install5,
|
||||
install6 as install7,
|
||||
install7 as install8,
|
||||
install8 as install9,
|
||||
install9 as install10
|
||||
} from "./chunk-TWPUBDXC.js";
|
||||
import {
|
||||
install as install15
|
||||
} from "./chunk-P7FEEIEF.js";
|
||||
import {
|
||||
install as install16
|
||||
} from "./chunk-QQGFJP25.js";
|
||||
import {
|
||||
install as install14
|
||||
} from "./chunk-D6WD7HPM.js";
|
||||
import "./chunk-FGVL4PGJ.js";
|
||||
import {
|
||||
install3 as install4,
|
||||
install4 as install6
|
||||
} from "./chunk-F5M4HOVH.js";
|
||||
import "./chunk-VW272IHF.js";
|
||||
install3 as install6,
|
||||
install4 as install13
|
||||
} from "./chunk-WRWLNY4R.js";
|
||||
import "./chunk-GORRBXQH.js";
|
||||
import "./chunk-QAR3K42R.js";
|
||||
import "./chunk-LEHUY6WA.js";
|
||||
import "./chunk-NWTUODUO.js";
|
||||
import {
|
||||
install as install25
|
||||
} from "./chunk-KNIVXUEF.js";
|
||||
import {
|
||||
install as install26
|
||||
} from "./chunk-XNS7XN3L.js";
|
||||
} from "./chunk-OBFEZ4QG.js";
|
||||
import {
|
||||
extendChartView,
|
||||
extendComponentModel,
|
||||
|
|
@ -82,12 +82,12 @@ import {
|
|||
number_exports,
|
||||
time_exports,
|
||||
util_exports as util_exports2
|
||||
} from "./chunk-2TFNBWBF.js";
|
||||
import "./chunk-X5CQ556A.js";
|
||||
} from "./chunk-M56X7I32.js";
|
||||
import "./chunk-LMKTAN4E.js";
|
||||
import {
|
||||
Axis_default,
|
||||
parseGeoJSON
|
||||
} from "./chunk-NF6IA4AL.js";
|
||||
} from "./chunk-V45EMKQW.js";
|
||||
import {
|
||||
Chart_default,
|
||||
Component_default,
|
||||
|
|
@ -136,7 +136,7 @@ import {
|
|||
use,
|
||||
version,
|
||||
warn
|
||||
} from "./chunk-QJLIGECE.js";
|
||||
} from "./chunk-ZCOAJCG3.js";
|
||||
import {
|
||||
BoundingRect_default,
|
||||
Displayable_default,
|
||||
|
|
@ -163,7 +163,7 @@ import {
|
|||
util_exports,
|
||||
vector_exports,
|
||||
zrender_exports
|
||||
} from "./chunk-GVYX3QQL.js";
|
||||
} from "./chunk-IWFMEAQB.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
|
||||
// node_modules/.pnpm/zrender@5.6.0/node_modules/zrender/lib/tool/convertPath.js
|
||||
|
|
@ -1894,42 +1894,42 @@ function installUniversalTransition(registers) {
|
|||
}
|
||||
|
||||
// node_modules/.pnpm/echarts@5.5.1/node_modules/echarts/index.js
|
||||
use([install49]);
|
||||
use([install25]);
|
||||
use([install50]);
|
||||
use([install27, install28, install29, install30, install31, install32, install33, install34, install35, install36, install37, install38, install39, install40, install41, install42, install43, install44, install45, install46, install47, install48]);
|
||||
use(install2);
|
||||
use(install3);
|
||||
use(install4);
|
||||
use(install5);
|
||||
use([install, install2, install3, install4, install5, install7, install8, install9, install10, install11, install12, install14, install15, install16, install17, install18, install19, install20, install21, install22, install23, install24]);
|
||||
use(install28);
|
||||
use(install29);
|
||||
use(install6);
|
||||
use(install7);
|
||||
use(install8);
|
||||
use(install9);
|
||||
use(install10);
|
||||
use(install);
|
||||
use(install11);
|
||||
use(install12);
|
||||
use(install30);
|
||||
use(install13);
|
||||
use(install14);
|
||||
use(install15);
|
||||
use(install16);
|
||||
use(install17);
|
||||
use(install20);
|
||||
use(install18);
|
||||
use(install19);
|
||||
use(install23);
|
||||
use(install21);
|
||||
use(install22);
|
||||
use(install24);
|
||||
use(install25);
|
||||
use(install31);
|
||||
use(install32);
|
||||
use(install33);
|
||||
use(install34);
|
||||
use(install27);
|
||||
use(install35);
|
||||
use(install36);
|
||||
use(install37);
|
||||
use(install38);
|
||||
use(install39);
|
||||
use(install40);
|
||||
use(install41);
|
||||
use(install44);
|
||||
use(install42);
|
||||
use(install43);
|
||||
use(install47);
|
||||
use(install45);
|
||||
use(install46);
|
||||
use(install48);
|
||||
use(install49);
|
||||
use(install26);
|
||||
use(installUniversalTransition);
|
||||
use(installLabelLayout);
|
||||
export {
|
||||
Axis_default as Axis,
|
||||
Chart_default as ChartView,
|
||||
Component_default2 as ComponentModel,
|
||||
Component_default as ComponentView,
|
||||
Component_default as ComponentModel,
|
||||
Component_default2 as ComponentView,
|
||||
SeriesData_default as List,
|
||||
Model_default as Model,
|
||||
PRIORITY,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -21,15 +21,15 @@ import {
|
|||
install7,
|
||||
install8,
|
||||
install9
|
||||
} from "./chunk-ND6EB7CF.js";
|
||||
import "./chunk-F5M4HOVH.js";
|
||||
import "./chunk-VW272IHF.js";
|
||||
} from "./chunk-TWPUBDXC.js";
|
||||
import "./chunk-WRWLNY4R.js";
|
||||
import "./chunk-GORRBXQH.js";
|
||||
import "./chunk-QAR3K42R.js";
|
||||
import "./chunk-LEHUY6WA.js";
|
||||
import "./chunk-X5CQ556A.js";
|
||||
import "./chunk-NF6IA4AL.js";
|
||||
import "./chunk-QJLIGECE.js";
|
||||
import "./chunk-GVYX3QQL.js";
|
||||
import "./chunk-NWTUODUO.js";
|
||||
import "./chunk-LMKTAN4E.js";
|
||||
import "./chunk-V45EMKQW.js";
|
||||
import "./chunk-ZCOAJCG3.js";
|
||||
import "./chunk-IWFMEAQB.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
export {
|
||||
install2 as BarChart,
|
||||
|
|
|
|||
|
|
@ -1,83 +1,83 @@
|
|||
import {
|
||||
install as install2,
|
||||
install10 as install14,
|
||||
install11 as install15,
|
||||
install12 as install19,
|
||||
install13 as install20,
|
||||
install14 as install21,
|
||||
install15 as install22,
|
||||
install16 as install23,
|
||||
install17 as install24,
|
||||
install18 as install25,
|
||||
install19 as install26,
|
||||
install2 as install3,
|
||||
install20 as install27,
|
||||
install21 as install28,
|
||||
install22 as install29,
|
||||
install3 as install4,
|
||||
install4 as install7,
|
||||
install5 as install9,
|
||||
install6 as install10,
|
||||
install7 as install11,
|
||||
install8 as install12,
|
||||
install9 as install13
|
||||
} from "./chunk-NMP246CO.js";
|
||||
install as install6,
|
||||
install10 as install15,
|
||||
install11 as install16,
|
||||
install12 as install20,
|
||||
install13 as install21,
|
||||
install14 as install22,
|
||||
install15 as install23,
|
||||
install16 as install24,
|
||||
install17 as install25,
|
||||
install18 as install26,
|
||||
install19 as install27,
|
||||
install2 as install7,
|
||||
install20 as install28,
|
||||
install21 as install29,
|
||||
install22 as install30,
|
||||
install3 as install8,
|
||||
install4 as install9,
|
||||
install5 as install10,
|
||||
install6 as install11,
|
||||
install7 as install12,
|
||||
install8 as install13,
|
||||
install9 as install14
|
||||
} from "./chunk-AUPWZZDD.js";
|
||||
import {
|
||||
install as install17
|
||||
} from "./chunk-P7FEEIEF.js";
|
||||
install as install19
|
||||
} from "./chunk-PRCWTGNB.js";
|
||||
import {
|
||||
install as install18
|
||||
} from "./chunk-QQGFJP25.js";
|
||||
} from "./chunk-X72D7ZPW.js";
|
||||
import {
|
||||
install as install16
|
||||
} from "./chunk-D6WD7HPM.js";
|
||||
import "./chunk-FGVL4PGJ.js";
|
||||
install as install17
|
||||
} from "./chunk-XNXAGDXI.js";
|
||||
import "./chunk-EM6SBCMK.js";
|
||||
import {
|
||||
install,
|
||||
install2 as install5,
|
||||
install3 as install6,
|
||||
install4 as install8
|
||||
} from "./chunk-F5M4HOVH.js";
|
||||
import "./chunk-VW272IHF.js";
|
||||
install2,
|
||||
install3,
|
||||
install4
|
||||
} from "./chunk-WRWLNY4R.js";
|
||||
import "./chunk-GORRBXQH.js";
|
||||
import "./chunk-QAR3K42R.js";
|
||||
import "./chunk-LEHUY6WA.js";
|
||||
import "./chunk-NWTUODUO.js";
|
||||
import {
|
||||
install as install30
|
||||
} from "./chunk-XNS7XN3L.js";
|
||||
import "./chunk-NF6IA4AL.js";
|
||||
import "./chunk-QJLIGECE.js";
|
||||
import "./chunk-GVYX3QQL.js";
|
||||
install as install5
|
||||
} from "./chunk-OBFEZ4QG.js";
|
||||
import "./chunk-V45EMKQW.js";
|
||||
import "./chunk-ZCOAJCG3.js";
|
||||
import "./chunk-IWFMEAQB.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
export {
|
||||
install28 as AriaComponent,
|
||||
install2 as AxisPointerComponent,
|
||||
install13 as BrushComponent,
|
||||
install9 as CalendarComponent,
|
||||
install24 as DataZoomComponent,
|
||||
install22 as DataZoomInsideComponent,
|
||||
install23 as DataZoomSliderComponent,
|
||||
install30 as DatasetComponent,
|
||||
install6 as GeoComponent,
|
||||
install10 as GraphicComponent,
|
||||
install3 as GridComponent,
|
||||
install29 as AriaComponent,
|
||||
install6 as AxisPointerComponent,
|
||||
install14 as BrushComponent,
|
||||
install10 as CalendarComponent,
|
||||
install25 as DataZoomComponent,
|
||||
install23 as DataZoomInsideComponent,
|
||||
install24 as DataZoomSliderComponent,
|
||||
install5 as DatasetComponent,
|
||||
install3 as GeoComponent,
|
||||
install11 as GraphicComponent,
|
||||
install7 as GridComponent,
|
||||
install as GridSimpleComponent,
|
||||
install21 as LegendComponent,
|
||||
install19 as LegendPlainComponent,
|
||||
install20 as LegendScrollComponent,
|
||||
install18 as MarkAreaComponent,
|
||||
install17 as MarkLineComponent,
|
||||
install16 as MarkPointComponent,
|
||||
install8 as ParallelComponent,
|
||||
install4 as PolarComponent,
|
||||
install5 as RadarComponent,
|
||||
install7 as SingleAxisComponent,
|
||||
install15 as TimelineComponent,
|
||||
install14 as TitleComponent,
|
||||
install11 as ToolboxComponent,
|
||||
install12 as TooltipComponent,
|
||||
install29 as TransformComponent,
|
||||
install27 as VisualMapComponent,
|
||||
install25 as VisualMapContinuousComponent,
|
||||
install26 as VisualMapPiecewiseComponent
|
||||
install22 as LegendComponent,
|
||||
install20 as LegendPlainComponent,
|
||||
install21 as LegendScrollComponent,
|
||||
install19 as MarkAreaComponent,
|
||||
install18 as MarkLineComponent,
|
||||
install17 as MarkPointComponent,
|
||||
install4 as ParallelComponent,
|
||||
install8 as PolarComponent,
|
||||
install2 as RadarComponent,
|
||||
install9 as SingleAxisComponent,
|
||||
install16 as TimelineComponent,
|
||||
install15 as TitleComponent,
|
||||
install12 as ToolboxComponent,
|
||||
install13 as TooltipComponent,
|
||||
install30 as TransformComponent,
|
||||
install28 as VisualMapComponent,
|
||||
install26 as VisualMapContinuousComponent,
|
||||
install27 as VisualMapPiecewiseComponent
|
||||
};
|
||||
//# sourceMappingURL=echarts_components.js.map
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ import {
|
|||
number_exports,
|
||||
time_exports,
|
||||
util_exports as util_exports2
|
||||
} from "./chunk-2TFNBWBF.js";
|
||||
import "./chunk-X5CQ556A.js";
|
||||
} from "./chunk-M56X7I32.js";
|
||||
import "./chunk-LMKTAN4E.js";
|
||||
import {
|
||||
Axis_default,
|
||||
parseGeoJSON
|
||||
} from "./chunk-NF6IA4AL.js";
|
||||
} from "./chunk-V45EMKQW.js";
|
||||
import {
|
||||
Chart_default,
|
||||
Component_default,
|
||||
|
|
@ -52,7 +52,7 @@ import {
|
|||
throttle,
|
||||
use,
|
||||
version
|
||||
} from "./chunk-QJLIGECE.js";
|
||||
} from "./chunk-ZCOAJCG3.js";
|
||||
import {
|
||||
brushSingle,
|
||||
color_exports,
|
||||
|
|
@ -62,13 +62,13 @@ import {
|
|||
util_exports,
|
||||
vector_exports,
|
||||
zrender_exports
|
||||
} from "./chunk-GVYX3QQL.js";
|
||||
} from "./chunk-IWFMEAQB.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
export {
|
||||
Axis_default as Axis,
|
||||
Chart_default as ChartView,
|
||||
Component_default2 as ComponentModel,
|
||||
Component_default as ComponentView,
|
||||
Component_default as ComponentModel,
|
||||
Component_default2 as ComponentView,
|
||||
SeriesData_default as List,
|
||||
Model_default as Model,
|
||||
PRIORITY,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import {
|
||||
install
|
||||
} from "./chunk-QQGFJP25.js";
|
||||
import "./chunk-FGVL4PGJ.js";
|
||||
} from "./chunk-PRCWTGNB.js";
|
||||
import "./chunk-EM6SBCMK.js";
|
||||
import "./chunk-QAR3K42R.js";
|
||||
import {
|
||||
use
|
||||
} from "./chunk-QJLIGECE.js";
|
||||
import "./chunk-GVYX3QQL.js";
|
||||
} from "./chunk-ZCOAJCG3.js";
|
||||
import "./chunk-IWFMEAQB.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
|
||||
// node_modules/.pnpm/echarts@5.5.1/node_modules/echarts/lib/component/markArea.js
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import {
|
||||
install
|
||||
} from "./chunk-P7FEEIEF.js";
|
||||
import "./chunk-FGVL4PGJ.js";
|
||||
import "./chunk-VW272IHF.js";
|
||||
} from "./chunk-X72D7ZPW.js";
|
||||
import "./chunk-EM6SBCMK.js";
|
||||
import "./chunk-GORRBXQH.js";
|
||||
import "./chunk-QAR3K42R.js";
|
||||
import {
|
||||
use
|
||||
} from "./chunk-QJLIGECE.js";
|
||||
import "./chunk-GVYX3QQL.js";
|
||||
} from "./chunk-ZCOAJCG3.js";
|
||||
import "./chunk-IWFMEAQB.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
|
||||
// node_modules/.pnpm/echarts@5.5.1/node_modules/echarts/lib/component/markLine.js
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import {
|
||||
install
|
||||
} from "./chunk-D6WD7HPM.js";
|
||||
import "./chunk-FGVL4PGJ.js";
|
||||
import "./chunk-LEHUY6WA.js";
|
||||
} from "./chunk-XNXAGDXI.js";
|
||||
import "./chunk-EM6SBCMK.js";
|
||||
import "./chunk-NWTUODUO.js";
|
||||
import {
|
||||
use
|
||||
} from "./chunk-QJLIGECE.js";
|
||||
import "./chunk-GVYX3QQL.js";
|
||||
} from "./chunk-ZCOAJCG3.js";
|
||||
import "./chunk-IWFMEAQB.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
|
||||
// node_modules/.pnpm/echarts@5.5.1/node_modules/echarts/lib/component/markPoint.js
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import {
|
||||
install as install2
|
||||
} from "./chunk-2I4JXH7Q.js";
|
||||
} from "./chunk-6JA6BRXS.js";
|
||||
import {
|
||||
install
|
||||
} from "./chunk-KJFX5ANN.js";
|
||||
import "./chunk-GVYX3QQL.js";
|
||||
} from "./chunk-KNIVXUEF.js";
|
||||
import "./chunk-IWFMEAQB.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
export {
|
||||
install as CanvasRenderer,
|
||||
|
|
|
|||
|
|
@ -453,7 +453,7 @@ import {
|
|||
virtualizedScrollbarProps,
|
||||
watermarkProps,
|
||||
zIndexContextKey
|
||||
} from "./chunk-QM5QNHIO.js";
|
||||
} from "./chunk-IRFI6G53.js";
|
||||
import {
|
||||
genFileId,
|
||||
uploadBaseProps,
|
||||
|
|
@ -481,9 +481,9 @@ import "./chunk-LROEKXT5.js";
|
|||
import "./chunk-O7KFMITO.js";
|
||||
import "./chunk-YNRHTVZR.js";
|
||||
import "./chunk-67TUTJCN.js";
|
||||
import "./chunk-7BPWZNUD.js";
|
||||
import "./chunk-ULX5FOVL.js";
|
||||
import "./chunk-GTWINWNV.js";
|
||||
import "./chunk-7BPWZNUD.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
var export_dayjs = import_dayjs.default;
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -453,7 +453,7 @@ import {
|
|||
virtualizedScrollbarProps,
|
||||
watermarkProps,
|
||||
zIndexContextKey
|
||||
} from "./chunk-QM5QNHIO.js";
|
||||
} from "./chunk-IRFI6G53.js";
|
||||
import {
|
||||
genFileId,
|
||||
uploadBaseProps,
|
||||
|
|
@ -481,9 +481,9 @@ import "./chunk-LROEKXT5.js";
|
|||
import "./chunk-O7KFMITO.js";
|
||||
import "./chunk-YNRHTVZR.js";
|
||||
import "./chunk-67TUTJCN.js";
|
||||
import "./chunk-7BPWZNUD.js";
|
||||
import "./chunk-ULX5FOVL.js";
|
||||
import "./chunk-GTWINWNV.js";
|
||||
import "./chunk-7BPWZNUD.js";
|
||||
import "./chunk-GFT2G5UO.js";
|
||||
var export_dayjs = import_dayjs.default;
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import "./chunk-5MJQOEES.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
//# sourceMappingURL=element-plus_es_components_autocomplete_style_css.js.map
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import "./chunk-HZRC7S76.js";
|
||||
import "./chunk-NZR6SVVT.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-7EBAVFZW.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-VLWH5T2T.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
//# sourceMappingURL=element-plus_es_components_cascader_style_css.js.map
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import "./chunk-THZSTYZP.js";
|
||||
import "./chunk-DB6OWVVK.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
//# sourceMappingURL=element-plus_es_components_date-picker_style_css.js.map
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import "./chunk-MC27QSJL.js";
|
|||
import "./chunk-UUC56VKA.js";
|
||||
import "./chunk-HGRMPKRI.js";
|
||||
import "./chunk-NZR6SVVT.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import "./chunk-OKMHUHKP.js";
|
||||
import "./chunk-NZR6SVVT.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import "./chunk-WTG273Z3.js";
|
||||
import "./chunk-MGTDGDA4.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-T6DOWK6H.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
//# sourceMappingURL=element-plus_es_components_slider_style_css.js.map
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-T6DOWK6H.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-7EBAVFZW.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
|
||||
// node_modules/.pnpm/element-plus@2.9.1_vue@3.5.12_typescript@5.3.3_/node_modules/element-plus/es/components/table/style/css.mjs
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import "./chunk-FRMPVTTW.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
//# sourceMappingURL=element-plus_es_components_time-picker_style_css.js.map
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import "./chunk-MC27QSJL.js";
|
|||
import "./chunk-UUC56VKA.js";
|
||||
import "./chunk-HGRMPKRI.js";
|
||||
import "./chunk-NZR6SVVT.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import "./chunk-7EBAVFZW.js";
|
||||
import "./chunk-JQ2IYF3Y.js";
|
||||
import "./chunk-DB6OWVVK.js";
|
||||
import "./chunk-C47PGQGR.js";
|
||||
import "./chunk-JQ2IYF3Y.js";
|
||||
import "./chunk-7EBAVFZW.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
|
||||
// node_modules/.pnpm/element-plus@2.9.1_vue@3.5.12_typescript@5.3.3_/node_modules/element-plus/es/components/transfer/style/css.mjs
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import "./chunk-MC27QSJL.js";
|
|||
import "./chunk-UUC56VKA.js";
|
||||
import "./chunk-HGRMPKRI.js";
|
||||
import "./chunk-NZR6SVVT.js";
|
||||
import "./chunk-7EBAVFZW.js";
|
||||
import "./chunk-HODVN2HK.js";
|
||||
import "./chunk-3N2VGZJ2.js";
|
||||
import "./chunk-7EBAVFZW.js";
|
||||
import "./chunk-5TRUIT6X.js";
|
||||
|
||||
// node_modules/.pnpm/element-plus@2.9.1_vue@3.5.12_typescript@5.3.3_/node_modules/element-plus/es/components/tree-select/style/css.mjs
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
import {
|
||||
Hook,
|
||||
UrlBuilder,
|
||||
buildCSSItem,
|
||||
buildJSItem,
|
||||
loadJS,
|
||||
noop,
|
||||
walkTree,
|
||||
wrapFunction
|
||||
} from "./chunk-UGLGEV52.js";
|
||||
import {
|
||||
BinTrieFlags,
|
||||
DecodingMode,
|
||||
|
|
@ -11,16 +21,6 @@ import {
|
|||
fromCodePoint,
|
||||
replaceCodePoint
|
||||
} from "./chunk-QK75OLN6.js";
|
||||
import {
|
||||
Hook,
|
||||
UrlBuilder,
|
||||
buildCSSItem,
|
||||
buildJSItem,
|
||||
loadJS,
|
||||
noop,
|
||||
walkTree,
|
||||
wrapFunction
|
||||
} from "./chunk-UGLGEV52.js";
|
||||
import {
|
||||
__commonJS,
|
||||
__export,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ export interface CirculateRecordVO {
|
|||
customerId: number // 客户ID
|
||||
type: number // 1:客户池,2:公海池 3:线索池
|
||||
typeId: number // 根据type来判断,例:type=1,则typeid是客户ID
|
||||
creator: string // 创建人
|
||||
createTime: string // 创建时间
|
||||
}
|
||||
|
||||
// 流转记录 API
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ export const FollowLabelApi = {
|
|||
return await request.get({ url: `/crm/follow-label/page`, params })
|
||||
},
|
||||
|
||||
getFollowLabelList: async (params: any) => {
|
||||
return await request.get({ url: `/crm/follow-label/list`, params })
|
||||
},
|
||||
|
||||
// 查询跟进标签详情
|
||||
getFollowLabel: async (id: number) => {
|
||||
return await request.get({ url: `/crm/follow-label/get?id=` + id })
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ export const CustomerLabelApi = {
|
|||
return await request.get({ url: `/crm/customer-label/page`, params })
|
||||
},
|
||||
|
||||
getCustomerLabelList: async (params: any) => {
|
||||
return await request.get({ url: `/crm/customer-label/list`, params })
|
||||
},
|
||||
|
||||
// 查询客户标签详情
|
||||
getCustomerLabel: async (id: number) => {
|
||||
return await request.get({ url: `/crm/customer-label/get?id=` + id })
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -7,6 +7,7 @@ export interface FollwRecordVO {
|
|||
content: string // 跟进内容
|
||||
createTime: string // 创建时间
|
||||
creator: string // 创建人
|
||||
userId: string // 客户经理
|
||||
}
|
||||
|
||||
// 跟进记录 API
|
||||
|
|
@ -16,32 +17,8 @@ export const FollwRecordApi = {
|
|||
return await request.get({ url: `/crm/follw-record/page`, params })
|
||||
},
|
||||
|
||||
getFollwRecordList: async (params: any) => {
|
||||
return await request.get({ url: `/crm/follw-record/list`, params })
|
||||
},
|
||||
|
||||
// 查询跟进记录详情
|
||||
getFollwRecord: async (id: number) => {
|
||||
return await request.get({ url: `/crm/follw-record/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增跟进记录
|
||||
createFollwRecord: async (data: FollwRecordVO) => {
|
||||
return await request.post({ url: `/crm/follw-record/create`, data })
|
||||
},
|
||||
|
||||
// 修改跟进记录
|
||||
updateFollwRecord: async (data: FollwRecordVO) => {
|
||||
return await request.put({ url: `/crm/follw-record/update`, data })
|
||||
},
|
||||
|
||||
// 删除跟进记录
|
||||
deleteFollwRecord: async (id: number) => {
|
||||
return await request.delete({ url: `/crm/follw-record/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出跟进记录 Excel
|
||||
exportFollwRecord: async (params) => {
|
||||
return await request.download({ url: `/crm/follw-record/export-excel`, params })
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { ImportLevelApi, type ImportLevelVO } from '@/api/crm/config/Importlevel'
|
||||
import { ChannelApi, type ChannelVO } from '@/api/crm/config/channel'
|
||||
import { FollowStatusApi, type FollowStatusVO } from '@/api/crm/config/followstatus'
|
||||
import { CustomerSourceApi } from '@/api/crm/config/customersource'
|
||||
import { CustomerTypeApi } from '@/api/crm/config/customertype'
|
||||
import { OpenSeaApi, type OpenSeaVO } from '@/api/crm/opensea/index'
|
||||
import { CustomerLabelApi } from '@/api/crm/config/customerlabel'
|
||||
import { FollowLabelApi } from '@/api/crm/config/Followlabel'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as DepApi from '@/api/system/dept'
|
||||
import { UserVO } from '@/api/system/user'
|
||||
import { DeptVO } from '@/api/system/dept'
|
||||
|
||||
|
||||
export const useCrmStore = defineStore('crm', {
|
||||
state: () => ({
|
||||
importLevelList: [] as ImportLevelVO[],
|
||||
channelList: [] as ChannelVO[],
|
||||
followStatusList: [] as FollowStatusVO[],
|
||||
customerSourceList: [] as any[],
|
||||
customerTypeList: [] as any[],
|
||||
userList: [] as UserVO[],
|
||||
depList: [] as DeptVO[],
|
||||
openSeaList: [] as OpenSeaVO[],
|
||||
customerLabelList: [] as any[],
|
||||
followLabelList: [] as any[]
|
||||
}),
|
||||
actions: {
|
||||
async getImportLevelList() {
|
||||
if (this.importLevelList.length > 0) {
|
||||
return this.importLevelList
|
||||
}
|
||||
const data = await ImportLevelApi.getImportLevelList(null)
|
||||
this.importLevelList = data
|
||||
return this.importLevelList
|
||||
},
|
||||
async getChannelList() {
|
||||
if (this.channelList.length > 0) {
|
||||
return this.channelList
|
||||
}
|
||||
const data = await ChannelApi.getChannelList(null)
|
||||
this.channelList = data
|
||||
return this.channelList
|
||||
},
|
||||
async getFollowStatusList() {
|
||||
if (this.followStatusList.length > 0) {
|
||||
return this.followStatusList
|
||||
}
|
||||
const data = await FollowStatusApi.getFollowStatusList(null)
|
||||
this.followStatusList = data
|
||||
return this.followStatusList
|
||||
},
|
||||
async getCustomerSourceList() {
|
||||
if (this.customerSourceList.length > 0) {
|
||||
return this.customerSourceList
|
||||
}
|
||||
const data = await CustomerSourceApi.getCustomerSourceList(null)
|
||||
this.customerSourceList = data
|
||||
return this.customerSourceList
|
||||
},
|
||||
async getCustomerTypeList() {
|
||||
if (this.customerTypeList.length > 0) {
|
||||
return this.customerTypeList
|
||||
}
|
||||
const data = await CustomerTypeApi.getCustomerTypeList(null)
|
||||
this.customerTypeList = data
|
||||
return this.customerTypeList
|
||||
},
|
||||
async getUserList() {
|
||||
const data = await UserApi.getSimpleUsertList()
|
||||
this.userList = data
|
||||
return this.userList
|
||||
},
|
||||
async getDepList() {
|
||||
const data = await DepApi.getSimpleDeptList()
|
||||
this.depList = data
|
||||
return this.depList
|
||||
},
|
||||
async getDepListByCurUser() {
|
||||
const data = await DepApi.getDeptListByCurUser()
|
||||
this.depList = data
|
||||
return this.depList
|
||||
},
|
||||
async getOpenSeaList() {
|
||||
const data = await OpenSeaApi.getOpenSeaList(null)
|
||||
this.openSeaList = data
|
||||
return this.openSeaList
|
||||
},
|
||||
async getOpenSeaListByCurUser() {
|
||||
const data = await OpenSeaApi.getOpenSeaListByCurUser()
|
||||
this.openSeaList = data
|
||||
return this.openSeaList
|
||||
},
|
||||
async getCustomerLabelList() {
|
||||
if (this.customerLabelList.length > 0) {
|
||||
return this.customerLabelList
|
||||
}
|
||||
const data = await CustomerLabelApi.getCustomerLabelList(null)
|
||||
this.customerLabelList = data
|
||||
return this.customerLabelList
|
||||
},
|
||||
async getFollowLabelList() {
|
||||
if (this.followLabelList.length > 0) {
|
||||
return this.followLabelList
|
||||
}
|
||||
const data = await FollowLabelApi.getFollowLabelList(null)
|
||||
this.followLabelList = data
|
||||
return this.followLabelList
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -7,6 +7,11 @@ export {}
|
|||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
2: typeof import('./../views/crm/customer/dep/tables/all copy 2.vue')['default']
|
||||
3: typeof import('./../views/crm/customer/dep/tables/all copy 3.vue')['default']
|
||||
4: typeof import('./../views/crm/customer/dep/tables/all copy 4.vue')['default']
|
||||
5: typeof import('./../views/crm/customer/my/tables/all copy 5.vue')['default']
|
||||
All: typeof import('./../views/crm/customer/my/tables/all.vue')['default']
|
||||
Allcustomer: typeof import('./../views/crm/customer/customerlist/allcustomer/index.vue')['default']
|
||||
Allocate: typeof import('./../api/crm/allocate/index.ts')['default']
|
||||
AllocateForm: typeof import('./../views/crm/components/Allocate/AllocateForm.vue')['default']
|
||||
|
|
@ -29,20 +34,23 @@ declare module 'vue' {
|
|||
ContentDetailWrap: typeof import('./../components/ContentDetailWrap/src/ContentDetailWrap.vue')['default']
|
||||
ContentWrap: typeof import('./../components/ContentWrap/src/ContentWrap.vue')['default']
|
||||
CopperModal: typeof import('./../components/Cropper/src/CopperModal.vue')['default']
|
||||
copy: typeof import('./../views/crm/opensea/customer/myopensea copy.vue')['default']
|
||||
copy: typeof import('./../views/crm/components/Customer/Infor copy.vue')['default']
|
||||
CopyTaskNode: typeof import('./../components/SimpleProcessDesignerV2/src/nodes/CopyTaskNode.vue')['default']
|
||||
CopyTaskNodeConfig: typeof import('./../components/SimpleProcessDesignerV2/src/nodes-config/CopyTaskNodeConfig.vue')['default']
|
||||
CountTo: typeof import('./../components/CountTo/src/CountTo.vue')['default']
|
||||
Crm: typeof import('./../router/modules/crm.ts')['default']
|
||||
Crm: typeof import('./../store/modules/crm.ts')['default']
|
||||
Crontab: typeof import('./../components/Crontab/src/Crontab.vue')['default']
|
||||
Cropper: typeof import('./../components/Cropper/src/Cropper.vue')['default']
|
||||
CropperAvatar: typeof import('./../components/Cropper/src/CropperAvatar.vue')['default']
|
||||
Cule: typeof import('./../views/crm/customer/cule/index.vue')['default']
|
||||
Culecustomer: typeof import('./../api/crm/customer/culecustomer/index.ts')['default']
|
||||
Customer: typeof import('./../api/crm/customer/customer/index.ts')['default']
|
||||
CustomerBaseInfo: typeof import('./../views/crm/components/CustomerDetail/customerBaseInfor/CustomerBaseInfo.vue')['default']
|
||||
CustomerDetail: typeof import('./../views/crm/components/CustomerDetail/CustomerDetail.vue')['default']
|
||||
Customer: typeof import('./../api/crm/customer/customer.ts')['default']
|
||||
CustomerBaseInfo: typeof import('./../views/crm/components/CustomerDetail/CustomerInfor/CustomerBaseInfo.vue')['default']
|
||||
CustomerBaseInfo_1: typeof import('./../views/crm/components/Customer/CustomerInfor/CustomerBaseInfo_1.vue')['default']
|
||||
CustomerDetail: typeof import('./../views/crm/components/Customer/CustomerDetail.vue')['default']
|
||||
CustomerInfor: typeof import('./../views/crm/components/Customer/CustomerInfor/CustomerInfor.vue')['default']
|
||||
CustomerInforForm: typeof import('./../views/crm/customer/opensea/CustomerInforForm.vue')['default']
|
||||
Customerlabels: typeof import('./../api/crm/config/customerlabels.ts')['default']
|
||||
Data: typeof import('./../views/crm/data/upload/data/index.vue')['default']
|
||||
DelayTimerNode: typeof import('./../components/SimpleProcessDesignerV2/src/nodes/DelayTimerNode.vue')['default']
|
||||
DelayTimerNodeConfig: typeof import('./../components/SimpleProcessDesignerV2/src/nodes-config/DelayTimerNodeConfig.vue')['default']
|
||||
|
|
@ -50,7 +58,7 @@ declare module 'vue' {
|
|||
Depcustomer: typeof import('./../views/crm/customer/customerlist/depcustomer/index.vue')['default']
|
||||
Descriptions: typeof import('./../components/Descriptions/src/Descriptions.vue')['default']
|
||||
DescriptionsItemLabel: typeof import('./../components/Descriptions/src/DescriptionsItemLabel.vue')['default']
|
||||
Detail: typeof import('./../views/crm/data/upload/detail/index.vue')['default']
|
||||
Detail: typeof import('./../views/crm/components/Customer/Detail.vue')['default']
|
||||
Dialog: typeof import('./../components/Dialog/src/Dialog.vue')['default']
|
||||
DictSelect: typeof import('./../components/FormCreate/src/components/DictSelect.vue')['default']
|
||||
DictTag: typeof import('./../components/DictTag/src/DictTag.vue')['default']
|
||||
|
|
@ -64,10 +72,12 @@ declare module 'vue' {
|
|||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElBadge: typeof import('element-plus/es')['ElBadge']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
|
||||
ElCard: typeof import('element-plus/es')['ElCard']
|
||||
ElCascader: typeof import('element-plus/es')['ElCascader']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCheckTag: typeof import('element-plus/es')['ElCheckTag']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
|
|
@ -84,6 +94,7 @@ declare module 'vue' {
|
|||
ElementOtherConfig: typeof import('./../components/bpmnProcessDesigner/package/penal/other/ElementOtherConfig.vue')['default']
|
||||
ElementProperties: typeof import('./../components/bpmnProcessDesigner/package/penal/properties/ElementProperties.vue')['default']
|
||||
ElementTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/ElementTask.vue')['default']
|
||||
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
|
|
@ -112,6 +123,7 @@ declare module 'vue' {
|
|||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElTimeline: typeof import('element-plus/es')['ElTimeline']
|
||||
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
|
||||
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
ElTree: typeof import('element-plus/es')['ElTree']
|
||||
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
|
||||
|
|
@ -121,6 +133,7 @@ declare module 'vue' {
|
|||
ExclusiveNode: typeof import('./../components/SimpleProcessDesignerV2/src/nodes/ExclusiveNode.vue')['default']
|
||||
FlowCondition: typeof import('./../components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue')['default']
|
||||
Follow: typeof import('./../views/crm/follow/index.vue')['default']
|
||||
FollowRecord: typeof import('./../views/crm/components/Customer/FollowRecord.vue')['default']
|
||||
Follw: typeof import('./../api/crm/follw/index.ts')['default']
|
||||
Follwrecord: typeof import('./../api/crm/follw/follwrecord/index.ts')['default']
|
||||
Form: typeof import('./../components/Form/src/Form.vue')['default']
|
||||
|
|
@ -129,7 +142,9 @@ declare module 'vue' {
|
|||
IconSelect: typeof import('./../components/Icon/src/IconSelect.vue')['default']
|
||||
IFrame: typeof import('./../components/IFrame/src/IFrame.vue')['default']
|
||||
ImageViewer: typeof import('./../components/ImageViewer/src/ImageViewer.vue')['default']
|
||||
Import: typeof import('./../views/crm/customer/my/tables/import.vue')['default']
|
||||
InclusiveNode: typeof import('./../components/SimpleProcessDesignerV2/src/nodes/InclusiveNode.vue')['default']
|
||||
Infor: typeof import('./../views/crm/components/Customer/Infor.vue')['default']
|
||||
Infotip: typeof import('./../components/Infotip/src/Infotip.vue')['default']
|
||||
InputPassword: typeof import('./../components/InputPassword/src/InputPassword.vue')['default']
|
||||
InputWithColor: typeof import('./../components/InputWithColor/index.vue')['default']
|
||||
|
|
@ -142,7 +157,7 @@ declare module 'vue' {
|
|||
NodeHandler: typeof import('./../components/SimpleProcessDesignerV2/src/NodeHandler.vue')['default']
|
||||
Opeansea: typeof import('../api/crm/opensea')['default']
|
||||
OpenDepForm: typeof import('./../views/crm/opensea/opensea/OpenDepForm.vue')['default']
|
||||
Opensea: typeof import('./../api/crm/opensea/index.ts')['default']
|
||||
Opensea: typeof import('./../views/crm/customer/opensea/index.vue')['default']
|
||||
OpenseaCustomerForm: typeof import('./../views/crm/customer/customerlist/openseacustomer/OpenseaCustomerForm.vue')['default']
|
||||
OpenSeaForm: typeof import('./../views/crm/opensea/opensea/OpenSeaForm.vue')['default']
|
||||
OperateLogV2: typeof import('./../components/OperateLogV2/src/OperateLogV2.vue')['default']
|
||||
|
|
@ -156,6 +171,7 @@ declare module 'vue' {
|
|||
ProcessViewer: typeof import('./../components/bpmnProcessDesigner/package/designer/ProcessViewer.vue')['default']
|
||||
PropertiesPanel: typeof import('./../components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue')['default']
|
||||
Qrcode: typeof import('./../components/Qrcode/src/Qrcode.vue')['default']
|
||||
QuickFollow: typeof import('./../views/crm/components/Customer/QuickFollow.vue')['default']
|
||||
ReceiveTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue')['default']
|
||||
Repeat: typeof import('./../api/crm/repeat/index.ts')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
|
@ -164,6 +180,7 @@ declare module 'vue' {
|
|||
ScriptTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ScriptTask.vue')['default']
|
||||
Search: typeof import('./../components/Search/src/Search.vue')['default']
|
||||
ServiceTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue')['default']
|
||||
Seven: typeof import('./../views/crm/customer/my/tables/seven.vue')['default']
|
||||
ShortcutDateRangePicker: typeof import('./../components/ShortcutDateRangePicker/index.vue')['default']
|
||||
SignalAndMessage: typeof import('./../components/bpmnProcessDesigner/package/penal/signal-message/SignalAndMessage.vue')['default']
|
||||
SimpleProcessDesigner: typeof import('./../components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue')['default']
|
||||
|
|
@ -174,11 +191,18 @@ declare module 'vue' {
|
|||
Sticky: typeof import('./../components/Sticky/src/Sticky.vue')['default']
|
||||
SummaryCard: typeof import('./../components/SummaryCard/index.vue')['default']
|
||||
Table: typeof import('./../components/Table/src/Table.vue')['default']
|
||||
Tables: typeof import('./../views/crm/customer/dep/tables/index.vue')['default']
|
||||
TableSelectForm: typeof import('./../components/Table/src/TableSelectForm.vue')['default']
|
||||
Tabs: typeof import('./../views/crm/customer/my/tabs.vue')['default']
|
||||
Team: typeof import('./../api/crm/team/index.ts')['default']
|
||||
Teamcustomer: typeof import('../api/crm/customer/team')['default']
|
||||
Today: typeof import('./../views/crm/customer/my/tables/today.vue')['default']
|
||||
Tody: typeof import('./../views/crm/customer/team/tables/tody.vue')['default']
|
||||
Tooltip: typeof import('./../components/Tooltip/src/Tooltip.vue')['default']
|
||||
TransferForm: typeof import('./../views/crm/components/Transfer/TransferForm.vue')['default']
|
||||
TransferRecord: typeof import('./../views/crm/components/Customer/TransferRecord.vue')['default']
|
||||
Types: typeof import('./../api/system/dept/types.ts')['default']
|
||||
Unfollow: typeof import('./../views/crm/customer/my/tables/unfollow.vue')['default']
|
||||
UploadFile: typeof import('./../components/UploadFile/src/UploadFile.vue')['default']
|
||||
UploadImg: typeof import('./../components/UploadFile/src/UploadImg.vue')['default']
|
||||
UploadImgs: typeof import('./../components/UploadFile/src/UploadImgs.vue')['default']
|
||||
|
|
@ -196,6 +220,7 @@ declare module 'vue' {
|
|||
XTextButton: typeof import('./../components/XButton/src/XTextButton.vue')['default']
|
||||
}
|
||||
export interface ComponentCustomProperties {
|
||||
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
|
||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ const submitForm = async () => {
|
|||
customerIds: formData.value.customerIds
|
||||
}
|
||||
await CustomerInforApi.allocate(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
message.success('分配成功')
|
||||
dialogVisible.value = false
|
||||
emit('success')// 发送操作成功的事件
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,196 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 顶部标题和操作按钮区域 -->
|
||||
<div class="flex justify-between mb-2">
|
||||
<div class="flex items-center">
|
||||
<h2 class="text-xl font-bold mr-10px min-w-[100px]" >{{ customerForm.customerName }}({{ customerForm.mobile }})</h2>
|
||||
<span class="ml-2px text-sm">撞库次数: {{ customerForm.repeatCount }}次</span>
|
||||
<span class="text-sm ml-2">跟进次数: {{ customerForm.followCount }}次</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-button size="small" class="mr-1" :disabled="!props.prevId" @click="emit('prev')">上一条</el-button>
|
||||
<el-button size="small" :disabled="!props.nextId" @click="emit('next')">下一条</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作按钮栏 -->
|
||||
<div class="mb-4">
|
||||
<el-button size="small" class="mr-1" type="primary" plain @click="openAllocateForm"><Icon icon="ep:plus" class="mr-5px" />分配</el-button>
|
||||
<el-button size="small" class="mr-1" type="primary" plain @click="handleReceive"><Icon icon="ep:plus" class="mr-5px" />领取</el-button>
|
||||
<el-button size="small" class="mr-1" type="warning" plain @click="openTransferForm"><Icon icon="ep:share" class="mr-5px" />转公海</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 进度导航 -->
|
||||
<div class="mb-4">
|
||||
<el-steps :space="200" :active="3" simple>
|
||||
<el-step title="线索" />
|
||||
<el-step title="跟进中" />
|
||||
<el-step title="已邀约" />
|
||||
<el-step title="已上门" />
|
||||
<el-step title="已签约" />
|
||||
<el-step title="办理中" />
|
||||
<el-step title="已完成" />
|
||||
</el-steps>
|
||||
</div>
|
||||
|
||||
<!-- 标签页和时间轴布局 -->
|
||||
<div class="flex gap-4">
|
||||
<!-- 左侧标签页 -->
|
||||
<div class="w-[300px]">
|
||||
<el-tabs>
|
||||
<el-tab-pane label="跟进记录">
|
||||
<FollowRecord :customer-id="props.customerId" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="流转记录">
|
||||
<TransferRecord :customer-id="props.customerId" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 右侧跟进记录 -->
|
||||
<div class="rightContent w-[calc(100%-300px)]">
|
||||
<el-tabs>
|
||||
<el-tab-pane label="客户资料">
|
||||
<div class="mt-4">
|
||||
<QuickFollow />
|
||||
<Infor />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ContentWrap>
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import Infor from './Infor.vue'
|
||||
import QuickFollow from './QuickFollow.vue'
|
||||
import FollowRecord from './FollowRecord.vue'
|
||||
import TransferRecord from './TransferRecord.vue'
|
||||
import { CustomerInforApi } from '@/api/crm/customer/customer'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps<{
|
||||
customerId?: number
|
||||
prevId?: number
|
||||
nextId?: number
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['prev', 'next'])
|
||||
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
// 客户表单数据
|
||||
const customerForm = ref({
|
||||
id: undefined as number | undefined,
|
||||
sex: 1,
|
||||
age: undefined as string | undefined,
|
||||
customerName: '',
|
||||
expectAmount: undefined as string | undefined,
|
||||
city: '',
|
||||
customerLevel: 0,
|
||||
customerType: '潜在',
|
||||
followContent: '',
|
||||
mobile: '',
|
||||
remark: '',
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
ownerUserId: undefined,
|
||||
depId: undefined,
|
||||
contactLastTime: undefined,
|
||||
contactLastContent: undefined,
|
||||
createTime: undefined,
|
||||
repeatCount: 0,
|
||||
followCount: 0
|
||||
})
|
||||
|
||||
// 加载客户详情数据
|
||||
const loadCustomerData = async (id?: number) => {
|
||||
if (id) {
|
||||
try {
|
||||
// 获取客户详情
|
||||
const data = await CustomerInforApi.getCustomer(id)
|
||||
// 更新表单数据
|
||||
Object.assign(customerForm.value, data)
|
||||
} catch (error) {
|
||||
message.error('获取失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听customerId变化,重新加载数据
|
||||
watch(() => props.customerId, (newId) => {
|
||||
loadCustomerData(newId)
|
||||
}, { immediate: true })
|
||||
|
||||
// 提供给子组件的方法
|
||||
const provide = {
|
||||
customerForm
|
||||
}
|
||||
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
|
||||
const customerIds = [props.customerId]
|
||||
const depId =0 // 获取第一个选中客户的depId
|
||||
formRef.value.open(customerIds, depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
const customerIds = [props.customerId]
|
||||
transferFormRef.value.open(customerIds)
|
||||
}
|
||||
/** 领取客户 */
|
||||
const handleReceive = async () => {
|
||||
|
||||
try {
|
||||
const customerIds = [props.customerId]
|
||||
await CustomerInforApi.receive({ customerIds: customerIds.map(String) })
|
||||
message.success('领取成功')
|
||||
} catch (error) {
|
||||
message.error('领取失败')
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-timeline {
|
||||
padding-right: 10px;
|
||||
max-height: calc(100vh - 240px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
:deep(.el-step__icon) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:deep(.el-step.is-simple .el-step__title) {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
:deep(.el-steps--simple) {
|
||||
padding: 5px 25px;
|
||||
}
|
||||
:deep(.el-step.is-simple .el-step__arrow) {
|
||||
transform: scale(0.6);
|
||||
}
|
||||
.rightContent {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<template>
|
||||
<div class="mt-4">
|
||||
<el-timeline v-if="followRecordList.length > 0" v-infinite-scroll="loadMore" :infinite-scroll-disabled="disabled">
|
||||
<el-timeline-item v-for="item in followRecordList" :key="item.id">
|
||||
<div>{{ item.content }}</div>
|
||||
<div class="flex flex-col gap-1 text-gray-400 text-sm mt-1">
|
||||
<span>客户经理:{{ userList.find((user) => user.id === Number(item.userId))?.nickname || '-' }}</span>
|
||||
<span>跟进时间:{{ formatDate(new Date(Number(item.createTime))) }}</span>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
<div v-if="loading" class="text-center py-4">加载中...</div>
|
||||
<div v-if="finished" class="text-center text-gray-400 py-4">没有更多了</div>
|
||||
</el-timeline>
|
||||
<div v-else class="text-center text-gray-400 py-4">暂无跟进记录</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { FollwRecordApi, FollwRecordVO } from '@/api/crm/follw'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
defineOptions({ name: 'FollowRecord' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
// 使用 CRM store
|
||||
const crmStore = useCrmStore()
|
||||
const { userList } = storeToRefs(crmStore)
|
||||
|
||||
const props = defineProps<{
|
||||
customerId?: number
|
||||
}>()
|
||||
|
||||
const followRecordList = ref<FollwRecordVO[]>([])
|
||||
const pageNo = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
const finished = ref(false)
|
||||
|
||||
const disabled = computed(() => loading.value || finished.value)
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
if (!props.customerId || loading.value) return
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await FollwRecordApi.getFollwRecordPage({
|
||||
customerId: props.customerId,
|
||||
pageNo: pageNo.value,
|
||||
pageSize: pageSize.value
|
||||
})
|
||||
|
||||
if (pageNo.value === 1) {
|
||||
followRecordList.value = res.list || []
|
||||
} else {
|
||||
followRecordList.value.push(...(res.list || []))
|
||||
}
|
||||
total.value = res.total || 0
|
||||
|
||||
// 判断是否加载完所有数据
|
||||
if (followRecordList.value.length >= total.value) {
|
||||
finished.value = true
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error('获取跟进记录失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
const loadMore = async () => {
|
||||
if (finished.value) return
|
||||
pageNo.value++
|
||||
await loadData()
|
||||
}
|
||||
|
||||
// 监听 customerId 变化,重置并加载数据
|
||||
watch(() => props.customerId, async (id) => {
|
||||
if (id) {
|
||||
pageNo.value = 1
|
||||
finished.value = false
|
||||
// 确保用户列表已加载
|
||||
await crmStore.getUserList()
|
||||
await loadData()
|
||||
}
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-timeline {
|
||||
padding-right: 10px;
|
||||
max-height: calc(100vh - 240px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
<template>
|
||||
<div class="flex justify-between items-center mb-2 bg-gray-50 px-4 py-2 rounded-md border border-gray-100">
|
||||
<div class="text-xs font-medium text-gray-700 flex items-center">
|
||||
<div class="w-1 h-3.5 bg-blue-500 rounded-full mr-2"></div>
|
||||
基本信息
|
||||
</div>
|
||||
<el-button type="primary" size="small" class="text-xs !px-3 !py-1.5">保存</el-button>
|
||||
</div>
|
||||
|
||||
<el-form :model="form" label-width="80px" class="bg-white p-4 rounded-lg text-xs">
|
||||
|
||||
<!-- 选择器组 -->
|
||||
<div class="flex items-center gap-6 mb-3 pb-2 border-b border-gray-100">
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="客户姓名" prop="customerName" class="!mb-0 !text-xs" >
|
||||
<el-input
|
||||
v-model="form.customerName"
|
||||
placeholder="请输入"
|
||||
class="!w-[120px]"
|
||||
size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="年龄" prop="age" class="!mb-0 !text-xs" >
|
||||
<el-input
|
||||
v-model="form.age"
|
||||
placeholder="请输入"
|
||||
class="!w-[120px]"
|
||||
size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="性别" prop="sex" class="!mb-0 !text-xs" >
|
||||
<el-select
|
||||
v-model="form.sex"
|
||||
placeholder="请选择"
|
||||
class="!w-[120px]"
|
||||
size="small"
|
||||
>
|
||||
<el-option label="未知" value="0" class="!text-xs" />
|
||||
<el-option label="男" value="1" class="!text-xs" />
|
||||
<el-option label="女" value="2" class="!text-xs" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
</div>
|
||||
<div class="flex items-center gap-6 mb-3 pb-2 border-b border-gray-100">
|
||||
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="期望金额" prop="expectAmount" class="!mb-0 !text-xs" >
|
||||
<el-input
|
||||
v-model="form.expectAmount"
|
||||
placeholder="请输入"
|
||||
class="!w-[120px]"
|
||||
size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="居住地址" prop="adress" class="!mb-0 !text-xs" >
|
||||
<el-input
|
||||
v-model="form.adress"
|
||||
placeholder="请输入"
|
||||
class="!w-[350px]"
|
||||
size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- 备注 -->
|
||||
<div class="flex items-start">
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="客户信息" class="flex-1 !text-xs">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder=""
|
||||
resize="none"
|
||||
size="small"
|
||||
class="[&_.el-textarea__inner]:!text-xs"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
|
||||
const props = defineProps<{
|
||||
customerId?: number
|
||||
}>()
|
||||
|
||||
// 从 CRM store 中获取数据
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, customerTypeList, customerLabelList, followLabelList } = storeToRefs(crmStore)
|
||||
const customerTypeOptions = ref<any>()
|
||||
|
||||
// cascader 配置
|
||||
const cascaderProps = {
|
||||
emitPath: false
|
||||
}
|
||||
|
||||
const form = ref({
|
||||
sex: 1,
|
||||
age: undefined as string | undefined,
|
||||
customerName: '',
|
||||
expectAmount: undefined as string | undefined,
|
||||
adress: '',
|
||||
remark: '',
|
||||
})
|
||||
|
||||
// 监听 customerId 变化
|
||||
watch(() => props.customerId, async (id) => {
|
||||
if (id) {
|
||||
try {
|
||||
// 这里添加获取客户跟进信息的API调用
|
||||
// const data = await CustomerApi.getCustomerFollowInfo(id)
|
||||
// Object.assign(form.value, data)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch customer follow info:', error)
|
||||
}
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 初始化数据
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getCustomerLabelList(),
|
||||
crmStore.getFollowLabelList()
|
||||
])
|
||||
// 构建客户类型树形结构
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize data:', error)
|
||||
}
|
||||
})
|
||||
|
||||
// 保存
|
||||
const handleSave = () => {
|
||||
// TODO: 实现保存逻辑
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
<template>
|
||||
<div class="flex justify-between items-center mb-2 bg-gray-50 px-4 py-2 rounded-md border border-gray-100">
|
||||
<div class="text-xs font-medium text-gray-700 flex items-center">
|
||||
<div class="w-1 h-3.5 bg-blue-500 rounded-full mr-2"></div>
|
||||
快捷输入
|
||||
</div>
|
||||
<el-button type="primary" size="small" class="text-xs !px-3 !py-1.5">保存</el-button>
|
||||
</div>
|
||||
|
||||
<el-form :model="form" label-width="80px" class="bg-white p-4 rounded-lg text-xs">
|
||||
<!-- 开关选项组 -->
|
||||
<div class="flex items-center gap-4 mb-3 pb-2 border-b border-gray-100">
|
||||
<el-form-item label="车" class="!mb-0 !text-xs">
|
||||
<el-switch v-model="form.haveCar" size="small" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="房" class="!mb-0 !text-xs">
|
||||
<el-switch v-model="form.haveHouse" size="small" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="社保" class="!mb-0 !text-xs">
|
||||
<el-switch v-model="form.haveSocialSecurity" size="small" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="公积" class="!mb-0 !text-xs">
|
||||
<el-switch v-model="form.haveProvidentFund" size="small" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="保单" class="!mb-0 !text-xs">
|
||||
<el-switch v-model="form.haveGuaranteeSlip" size="small" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 选择器组 -->
|
||||
<div class="flex items-center gap-6 mb-3 pb-2 border-b border-gray-100">
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="客户类型" prop="customerTypeId" class="!mb-0 !text-xs">
|
||||
<el-cascader
|
||||
v-model="form.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
class="!w-[150px]"
|
||||
size="small"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId" class="!mb-0 !text-xs">
|
||||
<el-select
|
||||
v-model="form.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-[150px]"
|
||||
size="small"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
class="!text-xs"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" class="!mb-0 !text-xs">
|
||||
<el-switch v-model="form.haveGuaranteeSlip" size="small" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-6 mb-3 pb-2 border-b border-gray-100">
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="下次跟进" prop="nextFollowTime" class="!mb-0 !text-xs">
|
||||
<div class="flex items-center gap-2">
|
||||
<el-date-picker
|
||||
v-model="form.nextFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetime"
|
||||
placeholder="请选择时间"
|
||||
class="!w-[150px]"
|
||||
size="small"
|
||||
/>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
class="!text-xs"
|
||||
plain
|
||||
@click="setNextFollowTime(15)"
|
||||
>15分钟</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
class="!text-xs"
|
||||
plain
|
||||
@click="setNextFollowTime(30)"
|
||||
>30分钟</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
class="!text-xs"
|
||||
plain
|
||||
@click="setNextFollowTime(60)"
|
||||
>1小时</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
class="!text-xs"
|
||||
plain
|
||||
@click="setNextFollowTime(1440)"
|
||||
>1天后</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
class="!text-xs"
|
||||
plain
|
||||
@click="setNextFollowTime(4320)"
|
||||
>3天后</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 标签组 -->
|
||||
<div class="mb-3 pb-2 border-b border-gray-100">
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="客户标签" class="!mb-0 !text-xs">
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<el-check-tag
|
||||
v-for="label in customerLabelList"
|
||||
:key="label.id"
|
||||
:modelValue="form.labelIds?.includes(label.id)"
|
||||
@change="(checked) => handleLabelChange(label.id, checked)"
|
||||
class="!cursor-pointer hover:!border-primary !text-xs !py-0 !px-1.5"
|
||||
>
|
||||
{{ label.name }}
|
||||
</el-check-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 备注 -->
|
||||
<div class="flex items-start">
|
||||
<el-form-item label-class="text-gray-600 text-xs" label="跟进备注" class="flex-1 !text-xs">
|
||||
<el-input
|
||||
v-model="form.content"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入跟进备注"
|
||||
resize="none"
|
||||
size="small"
|
||||
class="[&_.el-textarea__inner]:!text-xs"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<label class="text-gray-600 w-[80px] text-xs"></label>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<el-check-tag
|
||||
v-for="label in followLabelList"
|
||||
:key="label.id"
|
||||
:modelValue="form.labelIds?.includes(label.id)"
|
||||
@change="(checked) => handleLabelChange(label.id, checked)"
|
||||
class="!cursor-pointer hover:!border-primary !text-xs !py-0 !px-1.5"
|
||||
>
|
||||
{{ label.name }}
|
||||
</el-check-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const props = defineProps<{
|
||||
customerId?: number
|
||||
}>()
|
||||
|
||||
// 从 CRM store 中获取数据
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, customerTypeList, customerLabelList, followLabelList } = storeToRefs(crmStore)
|
||||
const customerTypeOptions = ref<any>()
|
||||
|
||||
// cascader 配置
|
||||
const cascaderProps = {
|
||||
emitPath: false
|
||||
}
|
||||
|
||||
const form = ref({
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
labelIds: [] as number[],
|
||||
content: undefined,
|
||||
nextFollowTime: undefined as string | undefined
|
||||
})
|
||||
|
||||
// 监听 customerId 变化
|
||||
watch(() => props.customerId, async (id) => {
|
||||
if (id) {
|
||||
try {
|
||||
// 这里添加获取客户跟进信息的API调用
|
||||
// const data = await CustomerApi.getCustomerFollowInfo(id)
|
||||
// Object.assign(form.value, data)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch customer follow info:', error)
|
||||
}
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 初始化数据
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getCustomerLabelList(),
|
||||
crmStore.getFollowLabelList()
|
||||
])
|
||||
// 构建客户类型树形结构
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize data:', error)
|
||||
}
|
||||
})
|
||||
|
||||
// 处理标签选择
|
||||
const handleLabelChange = (labelId: number, checked: boolean) => {
|
||||
if (checked) {
|
||||
form.value.labelIds.push(labelId)
|
||||
} else {
|
||||
const index = form.value.labelIds.indexOf(labelId)
|
||||
if (index !== -1) {
|
||||
form.value.labelIds.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置下次跟进时间
|
||||
const setNextFollowTime = (minutes: number) => {
|
||||
form.value.nextFollowTime = dayjs().add(minutes, 'minute').format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
// 保存
|
||||
const handleSave = () => {
|
||||
// TODO: 实现保存逻辑
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<template>
|
||||
<div class="transfer-record-container">
|
||||
<el-timeline
|
||||
v-if="transferRecordList.length > 0"
|
||||
v-infinite-scroll="loadMore"
|
||||
:infinite-scroll-disabled="disabled"
|
||||
:infinite-scroll-immediate="false"
|
||||
:infinite-scroll-distance="50"
|
||||
class="timeline-wrapper"
|
||||
>
|
||||
<el-timeline-item v-for="item in transferRecordList" :key="item.id">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center">
|
||||
<span class="text-gray-500 w-[80px]">数据位置:</span>
|
||||
<span>{{ getDictLabel(DICT_TYPE.CRM_CUSTOMER_DATA_BELONG, item.type) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-gray-500 w-[80px]">数据归属:</span>
|
||||
<template v-if="item.type === 1">
|
||||
<span>{{ userList.find(user => user.id === Number(item.typeId))?.nickname || '-' }}</span>
|
||||
</template>
|
||||
<template v-if="item.type === 2">
|
||||
<span>{{ openSeaList.find(openSea => openSea.id === Number(item.typeId))?.name || '-' }}</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="text-gray-500 w-[80px]">操作时间:</span>
|
||||
<span>{{ formatDate(new Date(Number(item.createTime))) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
<div v-if="loading" class="text-center py-4">加载中...</div>
|
||||
<div v-if="finished" class="text-center text-gray-400 py-4">没有更多了</div>
|
||||
</el-timeline>
|
||||
<div v-else class="text-center text-gray-400 py-4">暂无流转记录</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { CirculateRecordApi, CirculateRecordVO } from '@/api/crm/circulate'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
|
||||
|
||||
defineOptions({ name: 'TransferRecord' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
// 使用 CRM store
|
||||
const crmStore = useCrmStore()
|
||||
const { userList, openSeaList } = storeToRefs(crmStore)
|
||||
|
||||
const props = defineProps<{
|
||||
customerId?: number
|
||||
}>()
|
||||
|
||||
const transferRecordList = ref<CirculateRecordVO[]>([])
|
||||
const pageNo = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
const finished = ref(false)
|
||||
|
||||
const disabled = computed(() => {
|
||||
return loading.value || finished.value || !props.customerId
|
||||
})
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
if (!props.customerId || loading.value) return
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
// 加载相关信息
|
||||
if (pageNo.value === 1) {
|
||||
await Promise.all([
|
||||
crmStore.getUserList(),
|
||||
crmStore.getOpenSeaList()
|
||||
])
|
||||
}
|
||||
|
||||
const res = await CirculateRecordApi.getCirculateRecordPage({
|
||||
customerId: props.customerId,
|
||||
pageNo: pageNo.value,
|
||||
pageSize: pageSize.value
|
||||
})
|
||||
|
||||
const list = res.list || []
|
||||
total.value = res.total || 0
|
||||
|
||||
if (pageNo.value === 1) {
|
||||
transferRecordList.value = list
|
||||
} else {
|
||||
transferRecordList.value.push(...list)
|
||||
}
|
||||
|
||||
// 判断是否加载完所有数据
|
||||
finished.value = transferRecordList.value.length >= total.value || list.length < pageSize.value
|
||||
} catch (error: any) {
|
||||
message.error('获取流转记录失败')
|
||||
finished.value = true
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
const loadMore = async () => {
|
||||
if (loading.value || finished.value) return
|
||||
pageNo.value++
|
||||
await loadData()
|
||||
}
|
||||
|
||||
// 监听 customerId 变化,重置并加载数据
|
||||
watch(() => props.customerId, async (id) => {
|
||||
if (id) {
|
||||
pageNo.value = 1
|
||||
total.value = 0
|
||||
finished.value = false
|
||||
transferRecordList.value = []
|
||||
await loadData()
|
||||
} else {
|
||||
transferRecordList.value = []
|
||||
finished.value = true
|
||||
}
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.transfer-record-container {
|
||||
height: calc(100vh - 240px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.timeline-wrapper {
|
||||
height: 100%;
|
||||
padding-right: 10px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.timeline-wrapper::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.timeline-wrapper::-webkit-scrollbar-thumb {
|
||||
background-color: #dcdfe6;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.timeline-wrapper::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 顶部标题和操作按钮区域 -->
|
||||
<div class="flex justify-between mb-2">
|
||||
<div class="flex items-center">
|
||||
<h2 class="text-xl font-bold mr-2">{{ customerForm.customerName }}</h2>
|
||||
<el-tag size="small">撞库次数: 0次</el-tag>
|
||||
<el-tag size="small" class="ml-2">跟进次数: 0次</el-tag>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-button size="small" class="mr-1">上一条</el-button>
|
||||
<el-button size="small">下一条</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作按钮栏 -->
|
||||
<div class="mb-4">
|
||||
<el-button size="small" class="mr-1">分配客户</el-button>
|
||||
<el-button size="small" class="mr-1">领取客户</el-button>
|
||||
<el-button size="small" class="mr-1">退回公海</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 进度导航 -->
|
||||
<div class="progress-nav mb-4">
|
||||
<el-steps :space="200" :active="1" simple>
|
||||
<el-step title="线索" />
|
||||
<el-step title="跟进中" />
|
||||
<el-step title="已邀约" />
|
||||
<el-step title="已上门" />
|
||||
<el-step title="已签约" />
|
||||
<el-step title="办理中" />
|
||||
<el-step title="已完成" />
|
||||
</el-steps>
|
||||
</div>
|
||||
|
||||
<!-- 标签页和时间轴布局 -->
|
||||
<div class="flex gap-4">
|
||||
<!-- 左侧标签页 -->
|
||||
<div class="flex-1">
|
||||
<el-tabs>
|
||||
<el-tab-pane label="客户资料">
|
||||
<div class="mt-4">
|
||||
<CustomerBaseInfo />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="客户资质">
|
||||
<div class="mt-4">
|
||||
<h3 class="text-lg font-bold mb-4">客户资质</h3>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="邀约记录(0)">
|
||||
<div class="mt-4">
|
||||
<h3 class="text-lg font-bold mb-4">邀约记录</h3>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="上门记录(0)">
|
||||
<div class="mt-4">
|
||||
<h3 class="text-lg font-bold mb-4">上门记录</h3>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="签约记录(0)">
|
||||
<div class="mt-4">
|
||||
<h3 class="text-lg font-bold mb-4">签约记录</h3>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="进件记录(0)">
|
||||
<div class="mt-4">
|
||||
<h3 class="text-lg font-bold mb-4">进件记录</h3>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="撞库记录(0)">
|
||||
<div class="mt-4">
|
||||
<h3 class="text-lg font-bold mb-4">撞库记录</h3>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 右侧跟进记录 -->
|
||||
<div class="w-[300px]">
|
||||
<el-tabs>
|
||||
<el-tab-pane label="跟进历史">
|
||||
<div class="mt-4">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="item in followRecordList" :key="item.id">
|
||||
<div>{{ item.content }}</div>
|
||||
<div class="flex justify-between text-gray-400 text-sm mt-1">
|
||||
<span>跟进人:{{ item.creator }}</span>
|
||||
<span>{{ formatDate(new Date(Number(item.createTime))) }}</span>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import CustomerBaseInfo from './customerBaseInfor/CustomerBaseInfo.vue'
|
||||
import { CustomerInforApi } from '@/api/crm/customer/customer'
|
||||
import { FollwRecordApi,FollwRecordVO } from '@/api/crm/follw'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
defineOptions({ name: 'CustomerDetail' })
|
||||
|
||||
const props = defineProps<{
|
||||
customerId?: number
|
||||
}>()
|
||||
const followRecordList = ref<FollwRecordVO[]>([])
|
||||
// 客户表单数据
|
||||
const customerForm = ref({
|
||||
id: undefined as number | undefined,
|
||||
sex: 1,
|
||||
age: undefined as string | undefined,
|
||||
customerName: '',
|
||||
expectAmount: undefined as string | undefined,
|
||||
city: '',
|
||||
customerLevel: 0,
|
||||
customerType: '潜在',
|
||||
followContent: '',
|
||||
mobile: '',
|
||||
remark: '',
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
ownerUserId: undefined,
|
||||
depId: undefined,
|
||||
contactLastTime: undefined,
|
||||
contactLastContent: undefined,
|
||||
createTime: undefined
|
||||
})
|
||||
|
||||
// 监听 customerId 变化,加载客户详情
|
||||
watch(() => props.customerId, async (id) => {
|
||||
if (id) {
|
||||
try {
|
||||
// 获取客户详情
|
||||
const data = await CustomerInforApi.getCustomerInfor(id)
|
||||
// 更新表单数据
|
||||
Object.assign(customerForm.value, data)
|
||||
//获取跟进记录
|
||||
followRecordList.value = await FollwRecordApi.getFollwRecordList(id)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch customer details:', error)
|
||||
}
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 提供给子组件的方法
|
||||
const provide = {
|
||||
customerForm
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-timeline {
|
||||
padding-right: 10px;
|
||||
max-height: calc(100vh - 240px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
<template>
|
||||
<div class="customer-base-info">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-bold">基本信息</h3>
|
||||
<el-button type="primary" size="small">保存</el-button>
|
||||
</div>
|
||||
|
||||
<el-form label-width="100px">
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<!-- 性别 -->
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="form.sex">
|
||||
<el-radio :label="1">先生</el-radio>
|
||||
<el-radio :label="2">女士</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 年龄 -->
|
||||
<el-form-item label="年龄">
|
||||
<el-select v-model="form.age" class="w-full" placeholder="18-24岁">
|
||||
<el-option label="18-24岁" value="18-24" />
|
||||
<el-option label="25-35岁" value="25-35" />
|
||||
<el-option label="36-45岁" value="36-45" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="期望金额">
|
||||
<el-select v-model="form.expectAmount" class="w-full" placeholder="暂不确定">
|
||||
<el-option label="暂不确定" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<!-- 企业名称 -->
|
||||
|
||||
<el-form-item label="客户星级">
|
||||
<el-select v-model="form.expectAmount" class="w-full" placeholder="暂不确定">
|
||||
<el-option label="暂不确定" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="家庭住址">
|
||||
<el-input v-model="form.companyName" placeholder="请输入企业名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="客户备注">
|
||||
<el-input v-model="form.city" placeholder="" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex gap-4 items-center">
|
||||
<el-form-item label="房">
|
||||
<el-switch />
|
||||
</el-form-item>
|
||||
<el-form-item label="车">
|
||||
<el-switch />
|
||||
</el-form-item>
|
||||
<el-form-item label="社保">
|
||||
<el-switch />
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金">
|
||||
<el-switch />
|
||||
</el-form-item>
|
||||
<el-form-item label="保单">
|
||||
<el-switch />
|
||||
</el-form-item>
|
||||
<el-form-item label="营业执照">
|
||||
<el-switch />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<!-- 客户类型 -->
|
||||
<el-form-item label="客户类型" required>
|
||||
<div class="mb-2">
|
||||
<el-radio-group v-model="form.customerType" class="flex gap-2">
|
||||
<el-radio-button label="潜在">潜在</el-radio-button>
|
||||
<el-radio-button label="意向">意向</el-radio-button>
|
||||
<el-radio-button label="无效">无效</el-radio-button>
|
||||
<el-radio-button label="空号">空号</el-radio-button>
|
||||
<el-radio-button label="需求不明">需求不明</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<!-- 客户标签 -->
|
||||
<el-form-item label="客户标签">
|
||||
<div class="grid grid-cols-10 gap-2">
|
||||
<el-tag v-for="tag in customerTags" :key="tag" class="cursor-pointer" @click="toggleTag(tag)">
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 客户标签 -->
|
||||
<el-form-item label="跟进标签">
|
||||
<div class="grid grid-cols-10 gap-2">
|
||||
<el-tag v-for="tag in customerTags" :key="tag" class="cursor-pointer" @click="toggleTag(tag)">
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 跟进内容 -->
|
||||
<el-form-item label="跟进内容">
|
||||
<el-input type="textarea" v-model="form.followContent" :rows="4" placeholder="请输入跟进内容" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const form = ref({
|
||||
sex: 1,
|
||||
age: '18-24',
|
||||
companyName: '',
|
||||
expectAmount: '',
|
||||
city: '',
|
||||
customerLevel: 0,
|
||||
customerType: '潜在',
|
||||
followContent: ''
|
||||
})
|
||||
|
||||
const customerTags = [
|
||||
'生意', '上班', '自业', '信用卡', '微粒贷',
|
||||
'抵押房', '全款房', '有保单', '有社保', '公积金'
|
||||
]
|
||||
|
||||
const toggleTag = (tag: string) => {
|
||||
// 处理标签选择逻辑
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-radio-group {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -93,7 +93,7 @@ const submitForm = async () => {
|
|||
customerIds: formData.value.customerIds
|
||||
}
|
||||
await CustomerInforApi.transfer(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
message.success('转移成功')
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -52,6 +52,56 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="录入时间" prop="createTime">
|
||||
<el-date-picker
|
||||
|
|
@ -108,13 +158,38 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="录入时间" align="center" prop="createTime" :formatter="dateFormatter" min-width="180px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.customerId)"
|
||||
@click="openInforForm(scope.row.id)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
|
|
@ -133,7 +208,13 @@
|
|||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
<CustomerDetail
|
||||
:customer-id="customerId"
|
||||
:prev-id="prevId"
|
||||
:next-id="nextId"
|
||||
@prev="handlePrevNext('prev')"
|
||||
@next="handlePrevNext('next')"
|
||||
/>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
|
|
@ -148,9 +229,10 @@ import { buildTree } from '@/utils/tree'
|
|||
import {CustomerSourceApi} from '@/api/crm/config/customersource'
|
||||
import {ChannelApi,ChannelVO} from '@/api/crm/config/channel'
|
||||
import {CustomerInforApi, CustomerInforVO} from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/CustomerDetail/CustomerDetail.vue'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
|
||||
/** 客户信息 列表 */
|
||||
|
|
@ -170,6 +252,11 @@ const queryParams = reactive({
|
|||
mobile: undefined,
|
||||
customerSourceId: undefined,
|
||||
channelId: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
createTime: []
|
||||
})
|
||||
|
||||
|
|
@ -186,6 +273,8 @@ const multipleSelection = ref<CustomerInforVO[]>([])
|
|||
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const prevId = ref<number>(0) // 上一条记录ID
|
||||
const nextId = ref<number>(0) // 下一条记录ID
|
||||
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
|
@ -217,6 +306,13 @@ const resetQuery = () => {
|
|||
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
// 找到当前记录在列表中的索引
|
||||
const currentIndex = list.value.findIndex(item => item.id === id)
|
||||
|
||||
// 获取上一条和下一条记录的ID
|
||||
prevId.value = currentIndex > 0 ? list.value[currentIndex - 1].id : 0
|
||||
nextId.value = currentIndex < list.value.length - 1 ? list.value[currentIndex + 1].id : 0
|
||||
|
||||
drawerVisible.value = true
|
||||
}
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
|
|
@ -253,4 +349,23 @@ onMounted(async () => {
|
|||
customerSourceList.value=await CustomerSourceApi.getCustomerSourceList(null);
|
||||
customerSourceOptions.value=buildTree(customerSourceList.value);
|
||||
})
|
||||
|
||||
/** 处理上一条/下一条切换 */
|
||||
const handlePrevNext = async (type: 'prev' | 'next') => {
|
||||
// 找到当前记录在列表中的索引
|
||||
const currentIndex = list.value.findIndex(item => item.id === customerId.value)
|
||||
|
||||
// 根据类型确定要切换到的索引
|
||||
const targetIndex = type === 'prev' ? currentIndex - 1 : currentIndex + 1
|
||||
|
||||
// 检查目标索引是否有效
|
||||
if (targetIndex >= 0 && targetIndex < list.value.length) {
|
||||
const targetId = list.value[targetIndex].id
|
||||
// 更新当前查看的客户ID
|
||||
customerId.value = targetId
|
||||
// 更新上一条/下一条的ID
|
||||
prevId.value = targetIndex > 0 ? list.value[targetIndex - 1].id : 0
|
||||
nextId.value = targetIndex < list.value.length - 1 ? list.value[targetIndex + 1].id : 0
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据渠道" prop="channelId">
|
||||
<el-select
|
||||
v-model="queryParams.channelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in channelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="录入时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openAllocateForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="120px" fixed/>
|
||||
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" width="100px">
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="数据渠道" align="center" prop="channelId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (channelList.find((cs: any) => cs.id === scope.row.channelId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="录入时间" align="center" prop="createTime" :formatter="dateFormatter" min-width="180px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.customerId)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import {CustomerSourceApi} from '@/api/crm/config/customersource'
|
||||
import {CuleApi, CuleVO} from '@/api/crm/cule/index'
|
||||
import {ChannelApi,ChannelVO} from '@/api/crm/config/channel'
|
||||
import CustomerDetail from '@/views/crm/customer/components/detail/CustomerDetail.vue'
|
||||
import AllocateForm from '../components/Allocate/AllocateForm.vue'
|
||||
import TransferForm from '../components/Opensea/TransferForm.vue'
|
||||
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CuleVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
mobile: undefined,
|
||||
customerSourceId: undefined,
|
||||
channelId: undefined,
|
||||
createTime: []
|
||||
})
|
||||
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
const channelList = ref<ChannelVO[]>([])
|
||||
const customerSourceList = ref<[]>([])
|
||||
let customerSourceOptions = ref<any>();
|
||||
const multipleSelection = ref<CuleVO[]>([])
|
||||
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CuleApi.getCuleCustomerPage(queryParams);
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
const handleSelectionChange = (val: CuleVO[]) => {
|
||||
multipleSelection.value = val
|
||||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要分配的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.customerId)
|
||||
formRef.value.open(customerIds)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.customerId)
|
||||
transferFormRef.value.open(customerIds, 1)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载渠道列表
|
||||
channelList.value = await ChannelApi.getChannelList(null);
|
||||
customerSourceList.value=await CustomerSourceApi.getCustomerSourceList(null);
|
||||
customerSourceOptions.value=buildTree(customerSourceList.value);
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,493 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="负责部门" prop="depId">
|
||||
<el-select
|
||||
v-model="queryParams.depId"
|
||||
placeholder="请选择"
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in depList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分配时间" prop="latestAllocateTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.latestAllocateTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openAllocateForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ userList.find((cs) => cs.id === scope.row.userId)?.nickname||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'DepAllCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(false) // 列表的加载中状态
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
depId: 0,
|
||||
lastFollowTime: [],
|
||||
lastFollowContent: undefined,
|
||||
createTime: [],
|
||||
isImport: undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: []
|
||||
})
|
||||
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
|
||||
const cascaderProps = {
|
||||
emitPath: false
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getDepCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
// 保持部门ID不变
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
}
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要分配的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
formRef.value.open(customerIds, queryParams.depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds, 1)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepListByCurUser()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
|
||||
// 设置默认部门
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
} else {
|
||||
message.error('无部门管理权限')
|
||||
return
|
||||
}
|
||||
await getList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,489 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="负责部门" prop="depId">
|
||||
<el-select
|
||||
v-model="queryParams.depId"
|
||||
placeholder="请选择"
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in depList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followstatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分配时间" prop="latestAllocateTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.latestAllocateTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openAllocateForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followstatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ userList.find((cs) => cs.id === scope.row.userId)?.nickname||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
interface FollowStatus {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'DepImportCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(false)
|
||||
const list = ref<CustomerInforVO[]>([])
|
||||
const total = ref(0)
|
||||
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const multipleSelection = ref<CustomerInforVO[]>([]) // 选中的数据
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList , customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
depId: depList.value[0]?.id,
|
||||
lastFollowTime: [],
|
||||
lastFollowContent: undefined,
|
||||
createTime: [],
|
||||
isImport: 1,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: []
|
||||
})
|
||||
|
||||
const followstatusList = ref<FollowStatus[]>([])
|
||||
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getDepCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
}
|
||||
queryParams.isImport = 1
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要分配的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
formRef.value.open(customerIds, queryParams.depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds, 1)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepListByCurUser()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
|
||||
// 设置默认部门
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
} else {
|
||||
message.error('无部门管理权限')
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化查询,会触发数据总数回写
|
||||
await getList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,492 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="负责部门" prop="depId">
|
||||
<el-select
|
||||
v-model="queryParams.depId"
|
||||
placeholder="请选择"
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in depList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openAllocateForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ userList.find((cs) => cs.id === scope.row.userId)?.nickname||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'DepSevenCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(false) // 列表的加载中状态
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
|
||||
const cascaderProps = {
|
||||
emitPath: false
|
||||
}
|
||||
|
||||
// 设置最近7天的时间范围
|
||||
const sevenDaysAgo = dayjs().subtract(7, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||
const today = dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
depId: depList.value[0]?.id,
|
||||
lastFollowTime: [],
|
||||
lastFollowContent: undefined,
|
||||
createTime: [],
|
||||
isImport: undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: [sevenDaysAgo, today]// 默认查询最近7天
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getDepCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
// 保持部门ID和7天时间范围不变
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
}
|
||||
queryParams.createTime = [sevenDaysAgo, today]
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要分配的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
formRef.value.open(customerIds, queryParams.depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds, 1)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepListByCurUser()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
|
||||
// 设置默认部门
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
} else {
|
||||
message.error('无部门管理权限')
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化查询,会触发数据总数回写
|
||||
await getList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,493 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="负责部门" prop="depId">
|
||||
<el-select
|
||||
v-model="queryParams.depId"
|
||||
placeholder="请选择"
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in depList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followstatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openAllocateForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ userList.find((cs) => cs.id === scope.row.userId)?.nickname||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'DepTodayCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(false) // 列表的加载中状态
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, followStatusList, customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
depId: depList.value[0]?.id,
|
||||
lastFollowTime: [],
|
||||
lastFollowContent: undefined,
|
||||
createTime: [],
|
||||
isImport: undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: [
|
||||
dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||
]
|
||||
})
|
||||
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
|
||||
const cascaderProps = {
|
||||
emitPath: false
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getDepCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
// 保持部门ID和今日分配时间不变
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
}
|
||||
queryParams.latestAllocateTime = [
|
||||
dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||
]
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要分配的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
formRef.value.open(customerIds, queryParams.depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds, 1)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepListByCurUser()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
|
||||
// 设置默认部门
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
} else {
|
||||
message.error('无部门管理权限')
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化查询,会触发数据总数回写
|
||||
await getList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,482 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="负责部门" prop="depId">
|
||||
<el-select
|
||||
v-model="queryParams.depId"
|
||||
placeholder="请选择"
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in depList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分配时间" prop="latestAllocateTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.latestAllocateTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openAllocateForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name || "未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ userList.find((cs) => cs.id === scope.row.userId)?.nickname||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'DepUnfollowCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(false) // 列表的加载中状态
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
depId: depList.value[0]?.id,
|
||||
lastFollowTime: [],
|
||||
lastFollowContent: undefined,
|
||||
createTime: [],
|
||||
isImport: undefined,
|
||||
isFollow: 0, // 默认查询未跟进客户
|
||||
latestAllocateTime: []
|
||||
})
|
||||
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
|
||||
const cascaderProps = {
|
||||
emitPath: false
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getDepCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
// 保持部门ID和未跟进状态不变
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
}
|
||||
queryParams.isFollow = 0
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要分配的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
formRef.value.open(customerIds, queryParams.depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds, 1)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepListByCurUser()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
|
||||
// 设置默认部门
|
||||
if (depList.value.length > 0) {
|
||||
queryParams.depId = depList.value[0].id
|
||||
} else {
|
||||
message.error('无部门管理权限')
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化查询,会触发数据总数回写
|
||||
await getList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
||||
<el-tab-pane :label="'所有客户 (' + counts.all + ')'" name="all">
|
||||
<AllCustomer v-if="activeTab === 'all'" @update-count="updateCount('all', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'今日分配 (' + counts.today + ')'" name="today">
|
||||
<TodayCustomer v-if="activeTab === 'today'" @update-count="updateCount('today', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'未跟进客户 (' + counts.unfollow + ')'" name="unfollow">
|
||||
<UnfollowCustomer v-if="activeTab === 'unfollow'" @update-count="updateCount('unfollow', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'重点客户 (' + counts.import + ')'" name="import">
|
||||
<ImportCustomer v-if="activeTab === 'import'" @update-count="updateCount('import', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'近七天新数据 (' + counts.seven + ')'" name="seven">
|
||||
<SevenCustomer v-if="activeTab === 'seven'" @update-count="updateCount('seven', $event)" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import AllCustomer from '@/views/crm/customer/dep/tables/all.vue'
|
||||
import TodayCustomer from '@/views/crm/customer/dep/tables/today.vue'
|
||||
import UnfollowCustomer from '@/views/crm/customer/dep/tables/unfollow.vue'
|
||||
import ImportCustomer from '@/views/crm/customer/dep/tables/import.vue'
|
||||
import SevenCustomer from '@/views/crm/customer/dep/tables/seven.vue'
|
||||
|
||||
defineOptions({ name: 'CustomerTabs' })
|
||||
|
||||
const activeTab = ref('all')
|
||||
const counts = reactive({
|
||||
all: 0,
|
||||
today: 0,
|
||||
unfollow: 0,
|
||||
import: 0,
|
||||
seven: 0
|
||||
})
|
||||
|
||||
// 更新各标签页的数量
|
||||
const updateCount = (type: string, count: number) => {
|
||||
counts[type] = count
|
||||
}
|
||||
|
||||
const handleTabClick = () => {
|
||||
// 可以在这里处理标签页切换的逻辑
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs__content) {
|
||||
padding: 20px 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followstatusList"
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
|
|
@ -133,21 +133,8 @@
|
|||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据渠道" prop="channelId">
|
||||
<el-select
|
||||
v-model="queryParams.channelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in channelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
|
|
@ -180,9 +167,9 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastContactTime">
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastContactTime"
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
|
|
@ -227,7 +214,7 @@
|
|||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followstatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
|
|
@ -248,8 +235,8 @@
|
|||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastContactTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastContactContent" width="180px"/>
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
|
|
@ -288,17 +275,8 @@
|
|||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="数据渠道" align="center" prop="channelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (channelList.find((cs: any) => cs.id === scope.row.channelId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="录入时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
|
||||
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
|
|
@ -337,14 +315,10 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import {ImportLevelApi,ImportLevelVO} from '@/api/crm/config/Importlevel'
|
||||
import {FollowStatusApi,FollowStatusVO} from '@/api/crm/config/followstatus'
|
||||
import {CustomerSourceApi} from '@/api/crm/config/customersource'
|
||||
import {CustomerTypeApi} from '@/api/crm/config/customertype'
|
||||
import {ChannelApi,ChannelVO} from '@/api/crm/config/channel'
|
||||
import CustomerDetail from '@/views/crm/components/CustomerDetail/CustomerDetail.vue'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
|
|
@ -358,6 +332,7 @@ const total = ref(0) // 列表的总页数
|
|||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
|
|
@ -375,18 +350,15 @@ const queryParams = reactive({
|
|||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
lastContactTime: [],
|
||||
lastFollowTime: [],
|
||||
createTime: [],
|
||||
isImport: undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const importLevelList = ref<ImportLevelVO[]>([])
|
||||
const channelList = ref<ChannelVO[]>([])
|
||||
const followstatusList = ref<FollowStatusVO[]>([])
|
||||
const customerSourceList = ref<[]>([])
|
||||
const customerTypeList = ref<[]>([])
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>();
|
||||
let customerTypeOptions = ref<any>();
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
|
|
@ -394,14 +366,15 @@ const customerId = ref<number>() // 当前查看的客户ID
|
|||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getMyCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
|
@ -444,14 +417,16 @@ const handleSelectionChange = (val: CustomerInforVO[]) => {
|
|||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载渠道列表
|
||||
importLevelList.value = await ImportLevelApi.getImportLevelList(null);
|
||||
channelList.value = await ChannelApi.getChannelList(null);
|
||||
followstatusList.value = await FollowStatusApi.getFollowStatusList(null);
|
||||
customerSourceList.value=await CustomerSourceApi.getCustomerSourceList(null);
|
||||
customerTypeList.value=await CustomerTypeApi.getCustomerTypeList(null);
|
||||
customerSourceOptions.value=buildTree(customerSourceList.value);
|
||||
customerTypeOptions.value=buildTree(customerTypeList.value);
|
||||
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList()
|
||||
])
|
||||
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分配时间" prop="latestAllocateTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.latestAllocateTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="录入时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
|
||||
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
lastFollowTime: [] as string[],
|
||||
createTime: [] as string[],
|
||||
isImport: 1 as number | undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: [] as string[]
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getMyCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds,1)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList()
|
||||
])
|
||||
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="录入时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
|
||||
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
lastFollowTime: [] as string[],
|
||||
createTime: [] as string[],
|
||||
isImport: undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: [] as string[]
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 设置近7天的时间范围
|
||||
queryParams.latestAllocateTime = [
|
||||
dayjs().subtract(7, 'day').format('YYYY-MM-DD 00:00:00'),
|
||||
dayjs().format('YYYY-MM-DD 23:59:59')
|
||||
]
|
||||
const data = await CustomerInforApi.getMyCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds,1)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList()
|
||||
])
|
||||
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
lastFollowTime: [] as string[],
|
||||
createTime: [] as string[],
|
||||
isImport: undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: [] as string[]
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 添加今日日期条件
|
||||
queryParams.latestAllocateTime = [
|
||||
dayjs().format('YYYY-MM-DD 00:00:00'),
|
||||
dayjs().format('YYYY-MM-DD 23:59:59')
|
||||
]
|
||||
const data = await CustomerInforApi.getMyCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds,1)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList()
|
||||
])
|
||||
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,408 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分配时间" prop="latestAllocateTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.latestAllocateTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
lastFollowTime: [] as string[],
|
||||
createTime: [] as string[],
|
||||
isImport: undefined,
|
||||
isFollow: 0 as number | undefined,
|
||||
latestAllocateTime: [] as string[]
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
queryParams.isFollow = 0 // 设置未跟进状态
|
||||
const data = await CustomerInforApi.getMyCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds,1)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList()
|
||||
])
|
||||
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
||||
<el-tab-pane :label="'所有客户 (' + counts.all + ')'" name="all">
|
||||
<AllCustomer v-if="activeTab === 'all'" @update-count="updateCount('all', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'今日分配 (' + counts.today + ')'" name="today">
|
||||
<TodayCustomer v-if="activeTab === 'today'" @update-count="updateCount('today', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'未跟进客户 (' + counts.unfollow + ')'" name="unfollow">
|
||||
<UnfollowCustomer v-if="activeTab === 'unfollow'" @update-count="updateCount('unfollow', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'重点客户 (' + counts.import + ')'" name="import">
|
||||
<ImportCustomer v-if="activeTab === 'import'" @update-count="updateCount('import', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'近七天新数据 (' + counts.seven + ')'" name="seven">
|
||||
<SevenCustomer v-if="activeTab === 'seven'" @update-count="updateCount('seven', $event)" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import AllCustomer from '@/views/crm/customer/my/tables/all.vue'
|
||||
import TodayCustomer from '@/views/crm/customer/my/tables/today.vue'
|
||||
import UnfollowCustomer from '@/views/crm/customer/my/tables/unfollow.vue'
|
||||
import ImportCustomer from '@/views/crm/customer/my/tables/import.vue'
|
||||
import SevenCustomer from '@/views/crm/customer/my/tables/seven.vue'
|
||||
|
||||
defineOptions({ name: 'CustomerTabs' })
|
||||
|
||||
const activeTab = ref('all')
|
||||
const counts = reactive({
|
||||
all: 0,
|
||||
today: 0,
|
||||
unfollow: 0,
|
||||
import: 0,
|
||||
seven: 0
|
||||
})
|
||||
|
||||
// 更新各标签页的数量
|
||||
const updateCount = (type: string, count: number) => {
|
||||
counts[type] = count
|
||||
}
|
||||
|
||||
const handleTabClick = () => {
|
||||
// 可以在这里处理标签页切换的逻辑
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs__content) {
|
||||
padding: 20px 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followstatusList"
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
|
|
@ -204,9 +204,9 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastContactTime">
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastContactTime"
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
|
|
@ -260,7 +260,7 @@
|
|||
<el-table-column label="跟进状态" align="center" prop="followStatusId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followstatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
|
|
@ -295,8 +295,8 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastContactTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastContactContent" width="180px"/>
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" width="140px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
|
|
@ -384,28 +384,30 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import {ImportLevelApi,ImportLevelVO} from '@/api/crm/config/Importlevel'
|
||||
import {FollowStatusApi,FollowStatusVO} from '@/api/crm/config/followstatus'
|
||||
import {CustomerSourceApi} from '@/api/crm/config/customersource'
|
||||
import {CustomerTypeApi} from '@/api/crm/config/customertype'
|
||||
import {ChannelApi,ChannelVO} from '@/api/crm/config/channel'
|
||||
import CustomerDetail from '@/views/crm/components/CustomerDetail/CustomerDetail.vue'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as DepApi from '@/api/system/dept'
|
||||
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
defineOptions({ name: 'TeamCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
let depOptions = ref<any>()
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
|
|
@ -423,24 +425,14 @@ const queryParams = reactive({
|
|||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
depId: undefined,
|
||||
lastContactTime: [],
|
||||
lastContactContent: undefined,
|
||||
lastFollowTime: [],
|
||||
lastFollowContent: undefined,
|
||||
createTime: [],
|
||||
isImport: undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const importLevelList = ref<ImportLevelVO[]>([])
|
||||
const channelList = ref<ChannelVO[]>([])
|
||||
const followstatusList = ref<FollowStatusVO[]>([])
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
const depList = ref<DepApi.DeptVO[]>([])
|
||||
const customerSourceList = ref<[]>([])
|
||||
const customerTypeList = ref<[]>([])
|
||||
let depOptions = ref<any>();
|
||||
let customerSourceOptions = ref<any>();
|
||||
let customerTypeOptions = ref<any>();
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
|
||||
const cascaderProps= {
|
||||
|
|
@ -453,12 +445,14 @@ const formRef = ref() // 分配表单的 Ref
|
|||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getTeamCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
|
@ -506,27 +500,22 @@ const handleSelectionChange = (val: CustomerInforVO[]) => {
|
|||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载渠道列表
|
||||
importLevelList.value = await ImportLevelApi.getImportLevelList(null);
|
||||
channelList.value = await ChannelApi.getChannelList(null);
|
||||
followstatusList.value = await FollowStatusApi.getFollowStatusList(null);
|
||||
userList.value=await UserApi.getSimpleUsertList();
|
||||
depList.value=await DepApi.getSimpleDeptList();
|
||||
customerSourceList.value=await CustomerSourceApi.getCustomerSourceList(null);
|
||||
customerTypeList.value=await CustomerTypeApi.getCustomerTypeList(null);
|
||||
depOptions.value=buildTree(depList.value);
|
||||
customerSourceOptions.value=buildTree(customerSourceList.value);
|
||||
customerTypeOptions.value=buildTree(customerTypeList.value);
|
||||
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepList()
|
||||
])
|
||||
|
||||
depOptions.value = buildTree(depList.value)
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -8,38 +8,7 @@
|
|||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="负责部门" prop="depId">
|
||||
<el-select
|
||||
v-model="queryParams.depId"
|
||||
placeholder="请选择"
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in depList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
>
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
|
|
@ -50,12 +19,12 @@
|
|||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
|
|
@ -91,7 +60,7 @@
|
|||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followstatusList"
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
|
|
@ -108,7 +77,7 @@
|
|||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
|
|
@ -149,7 +118,34 @@
|
|||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="所属部门" prop="depId">
|
||||
<el-cascader
|
||||
v-model="queryParams.depId"
|
||||
:show-all-levels="false"
|
||||
:options="depOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
|
|
@ -175,17 +171,7 @@
|
|||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
|
|
@ -208,9 +194,9 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastContactTime">
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastContactTime"
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
|
|
@ -246,33 +232,35 @@
|
|||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
</el-button>
|
||||
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followstatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
|
|
@ -283,22 +271,29 @@
|
|||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" >
|
||||
<el-table-column label="所属部门" align="center" prop="depId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ depList.find((cs) => cs.id === scope.row.depId)?.name||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ userList.find((cs) => cs.id === scope.row.userId)?.nickname||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastContactTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastContactContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" width="140px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
|
|
@ -331,17 +326,15 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="数据渠道" align="center" prop="channelId" >
|
||||
<el-table-column label="数据渠道" align="center" prop="channelId" width="140px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (channelList.find((cs: any) => cs.id === scope.row.channelId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="录入时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
|
||||
|
||||
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
|
|
@ -352,18 +345,19 @@
|
|||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
|
|
@ -373,7 +367,6 @@
|
|||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -381,48 +374,29 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
|||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import {ImportLevelApi,ImportLevelVO} from '@/api/crm/config/Importlevel'
|
||||
import {FollowStatusApi,FollowStatusVO} from '@/api/crm/config/followstatus'
|
||||
import {CustomerSourceApi} from '@/api/crm/config/customersource'
|
||||
import {CustomerTypeApi} from '@/api/crm/config/customertype'
|
||||
import {ChannelApi,ChannelVO} from '@/api/crm/config/channel'
|
||||
import CustomerDetail from '@/views/crm/components/CustomerDetail/CustomerDetail.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as DepApi from '@/api/system/dept'
|
||||
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
defineOptions({ name: 'ImportCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const depList = ref<DepApi.DeptVO[]>([])
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const importLevelList = ref<ImportLevelVO[]>([])
|
||||
const channelList = ref<ChannelVO[]>([])
|
||||
const followstatusList = ref<FollowStatusVO[]>([])
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
const customerSourceList = ref<[]>([])
|
||||
const customerTypeList = ref<[]>([])
|
||||
let depOptions = ref<any>();
|
||||
let customerSourceOptions = ref<any>();
|
||||
let customerTypeOptions = ref<any>();
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
let depOptions = ref<any>()
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
|
|
@ -434,38 +408,48 @@ const queryParams = reactive({
|
|||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
depId: depList.value[0]?.id,
|
||||
lastContactTime: [],
|
||||
lastContactContent: undefined,
|
||||
userId: undefined,
|
||||
depId: undefined,
|
||||
lastFollowTime: [],
|
||||
lastFollowContent: undefined,
|
||||
createTime: [],
|
||||
isImport: undefined,
|
||||
isImport: 1,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getDepCustomerPage(queryParams)
|
||||
const data = await CustomerInforApi.getTeamCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
|
@ -473,15 +457,14 @@ const handleQuery = () => {
|
|||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
queryParams.isImport = 1
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
|
|
@ -489,8 +472,9 @@ const openAllocateForm = () => {
|
|||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
formRef.value.open(customerIds, queryParams.depId)
|
||||
}
|
||||
const depId =0
|
||||
formRef.value.open(customerIds, depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
|
|
@ -499,7 +483,7 @@ const openTransferForm = () => {
|
|||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds, 1)
|
||||
transferFormRef.value.open(customerIds)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
|
|
@ -507,27 +491,22 @@ const handleSelectionChange = (val: CustomerInforVO[]) => {
|
|||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepList()
|
||||
])
|
||||
|
||||
// 加载渠道列表
|
||||
importLevelList.value = await ImportLevelApi.getImportLevelList(null);
|
||||
channelList.value = await ChannelApi.getChannelList(null);
|
||||
followstatusList.value = await FollowStatusApi.getFollowStatusList(null);
|
||||
userList.value=await UserApi.getSimpleUsertList();
|
||||
depList.value=await DepApi.getDeptListByCurUser();
|
||||
if(depList.value.length==0){
|
||||
return message.error('无部门管理权限')
|
||||
}
|
||||
queryParams.depId = depList.value[0].id
|
||||
customerSourceList.value=await CustomerSourceApi.getCustomerSourceList(null);
|
||||
customerTypeList.value=await CustomerTypeApi.getCustomerTypeList(null);
|
||||
depOptions.value=buildTree(depList.value);
|
||||
customerSourceOptions.value=buildTree(customerSourceList.value);
|
||||
customerTypeOptions.value=buildTree(customerTypeList.value);
|
||||
await getList()
|
||||
depOptions.value = buildTree(depList.value)
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,518 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="所属部门" prop="depId">
|
||||
<el-cascader
|
||||
v-model="queryParams.depId"
|
||||
:show-all-levels="false"
|
||||
:options="depOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据渠道" prop="channelId">
|
||||
<el-select
|
||||
v-model="queryParams.channelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in channelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="录入时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openAllocateForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="所属部门" align="center" prop="depId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ depList.find((cs) => cs.id === scope.row.depId)?.name||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ userList.find((cs) => cs.id === scope.row.userId)?.nickname||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" width="140px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="数据渠道" align="center" prop="channelId" width="140px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (channelList.find((cs: any) => cs.id === scope.row.channelId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="录入时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'SevenCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
let depOptions = ref<any>()
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
depId: undefined,
|
||||
lastFollowTime: [],
|
||||
lastFollowContent: undefined,
|
||||
createTime: [],
|
||||
isImport: undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: [
|
||||
dayjs().subtract(7, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||
],
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getTeamCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
queryParams.latestAllocateTime = [
|
||||
dayjs().subtract(7, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||
]
|
||||
handleQuery()
|
||||
}
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要分配的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
const depId =0
|
||||
formRef.value.open(customerIds, depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepList()
|
||||
])
|
||||
|
||||
depOptions.value = buildTree(depList.value)
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,513 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="客户姓名" prop="customerName" >
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户姓名"
|
||||
clearable
|
||||
class="!w-120px "
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatusId">
|
||||
<el-select
|
||||
v-model="queryParams.followStatusId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="所属部门" prop="depId">
|
||||
<el-cascader
|
||||
v-model="queryParams.depId"
|
||||
:show-all-levels="false"
|
||||
:options="depOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据渠道" prop="channelId">
|
||||
<el-select
|
||||
v-model="queryParams.channelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in channelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否跟进" prop="isFollow">
|
||||
<el-select v-model="queryParams.isFollow" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_FOLLOW_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="录入时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openAllocateForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户姓名" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatusId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="所属部门" align="center" prop="depId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ depList.find((cs) => cs.id === scope.row.depId)?.name||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ userList.find((cs) => cs.id === scope.row.userId)?.nickname||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" width="140px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="数据渠道" align="center" prop="channelId" width="140px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (channelList.find((cs: any) => cs.id === scope.row.channelId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="录入时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'TodayCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
let depOptions = ref<any>()
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 15,
|
||||
customerName: undefined,
|
||||
channelId: undefined,
|
||||
mobile: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
customerSourceId: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
userId: undefined,
|
||||
depId: undefined,
|
||||
lastFollowTime: [],
|
||||
lastFollowContent: undefined,
|
||||
createTime: [],
|
||||
isImport: undefined,
|
||||
isFollow: undefined,
|
||||
latestAllocateTime: [
|
||||
dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||
]
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getTeamCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要分配的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
const depId =0
|
||||
formRef.value.open(customerIds, depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepList()
|
||||
])
|
||||
|
||||
depOptions.value = buildTree(depList.value)
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,525 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="客户名称" prop="customerName">
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="座机号码" prop="telephone">
|
||||
<el-input
|
||||
v-model="queryParams.telephone"
|
||||
placeholder="请输入座机号码"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户类型" prop="customerTypeId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerTypeId"
|
||||
:show-all-levels="false"
|
||||
:options="customerTypeOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="重要程度" prop="importLevelId">
|
||||
<el-select
|
||||
v-model="queryParams.importLevelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in importLevelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态" prop="followStatus">
|
||||
<el-select
|
||||
v-model="queryParams.followStatus"
|
||||
placeholder="请选择跟进状态"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in followStatusList"
|
||||
:key="dict.id"
|
||||
:label="dict.name"
|
||||
:value="dict.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否有车" prop="haveCar">
|
||||
<el-select v-model="queryParams.haveCar" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有房" prop="haveHouse">
|
||||
<el-select v-model="queryParams.haveHouse" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="公积金" prop="haveProvidentFund">
|
||||
<el-select v-model="queryParams.haveProvidentFund" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="社保" prop="haveSocialSecurity">
|
||||
<el-select v-model="queryParams.haveSocialSecurity" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保单" prop="haveGuaranteeSlip">
|
||||
<el-select v-model="queryParams.haveGuaranteeSlip" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_HAVE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="所属部门" prop="depId">
|
||||
<el-cascader
|
||||
v-model="queryParams.depId"
|
||||
:show-all-levels="false"
|
||||
:options="depOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户经理" prop="userId">
|
||||
<el-select
|
||||
v-model="queryParams.userId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
class="!w-120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in userList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.nickname"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="来源标签" prop="customerSourceId">
|
||||
<el-cascader
|
||||
v-model="queryParams.customerSourceId"
|
||||
:show-all-levels="false"
|
||||
:options="customerSourceOptions"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
class="!w-120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据渠道" prop="channelId">
|
||||
<el-select
|
||||
v-model="queryParams.channelId"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-120px margin-left:15px"
|
||||
>
|
||||
<el-option
|
||||
v-for="iml in channelList"
|
||||
:key="iml.id"
|
||||
:value="iml.id"
|
||||
:label="iml.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="重点客户" prop="isImport">
|
||||
<el-select v-model="queryParams.isImport" placeholder="请选择" clearable class="!w-120px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_IMPORT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分配时间" prop="latestAllocateTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.latestAllocateTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="录入时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openAllocateForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="openTransferForm"
|
||||
>
|
||||
<Icon icon="ep:share" class="mr-5px" /> 转公海
|
||||
</el-button>
|
||||
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="30" />
|
||||
<el-table-column label="序号" align="center" width="60" type="index" fixed/>
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" width="100px" fixed/>
|
||||
<el-table-column label="手机号码" align="center" prop="mobile" width="140px" fixed/>
|
||||
|
||||
<el-table-column label="跟进状态" align="center" prop="followStatus" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followStatusList.find((item) => item.id === scope.row.followStatus)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重要程度" align="center" prop="importLevelId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ importLevelList.find((cs) => cs.id === scope.row.importLevelId)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="重点客户" align="center" prop="isImport" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_IMPORT_STATUS" :value="scope.row.isImport" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否跟进" align="center" prop="isFollow" width="80px">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_STATUS" :value="scope.row.isFollow" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="所属部门" align="center" prop="depId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ depList.find((cs) => cs.id === scope.row.depId)?.name||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="客户经理" align="center" prop="userId" width="100px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ userList.find((cs) => cs.id === scope.row.userId)?.nickname||"未分配" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" width="140px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (customerTypeList.find((cs: any) => cs.id === scope.row.customerTypeId) as any)?.name||"未标记" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有车" align="center" prop="haveCar" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveCar" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="是否有房" align="center" prop="haveHouse" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveHouse" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="公积金" align="center" prop="haveProvidentFund" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveProvidentFund" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="社保" align="center" prop="haveSocialSecurity" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveSocialSecurity" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="保单" align="center" prop="haveGuaranteeSlip" >
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_HAVE_STATUS" :value="scope.row.haveGuaranteeSlip" />
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="来源标签" align="center" prop="customerSourceId" >
|
||||
<template #default="scope">
|
||||
<div >
|
||||
{{ (customerSourceList.find((cs: any) => cs.id === scope.row.customerSourceId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="数据渠道" align="center" prop="channelId" width="140px">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ (channelList.find((cs: any) => cs.id === scope.row.channelId) as any)?.name||"无" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="分配时间" align="center" prop="latestAllocateTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="录入时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="客户资料" align="center" prop="remark" min-width="400px" />
|
||||
<el-table-column label="操作" align="center" width="120px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openInforForm( scope.row.id)"
|
||||
>
|
||||
跟进
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 客户详情抽屉 -->
|
||||
<el-drawer v-model="drawerVisible" size="80%" :destroy-on-close="true" :with-header="false" :show-close="true">
|
||||
<CustomerDetail :customer-id="customerId" />
|
||||
</el-drawer>
|
||||
|
||||
<!-- 分配弹窗 -->
|
||||
<AllocateForm ref="formRef" @success="getList" />
|
||||
<!-- 转公海弹窗 -->
|
||||
<TransferForm ref="transferFormRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import TransferForm from '@/views/crm/components/Transfer/TransferForm.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'UnfollowCustomer' })
|
||||
|
||||
const emit = defineEmits(['update-count'])
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const loading = ref(false) // 列表的加载中状态
|
||||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, channelList, followStatusList, customerSourceList, customerTypeList, userList, depList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
let depOptions = ref<any>()
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
customerName: '',
|
||||
mobile: '',
|
||||
telephone: '',
|
||||
followStatus: undefined,
|
||||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
haveCar: undefined,
|
||||
haveHouse: undefined,
|
||||
haveProvidentFund: undefined,
|
||||
haveSocialSecurity: undefined,
|
||||
haveGuaranteeSlip: undefined,
|
||||
depId: undefined,
|
||||
userId: undefined,
|
||||
ownerUserId: undefined,
|
||||
customerSourceId: undefined,
|
||||
channelId: undefined,
|
||||
isImport: undefined,
|
||||
isFollow: 0,
|
||||
latestAllocateTime: undefined,
|
||||
lastFollowTime: undefined,
|
||||
createTime: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const formRef = ref() // 分配表单的 Ref
|
||||
const transferFormRef = ref() // 转公海表单的 Ref
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerInforApi.getTeamCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
// 发送总数更新事件
|
||||
emit('update-count', data.total)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
queryParams.isFollow = 0
|
||||
queryParams.followStatus = undefined
|
||||
queryParams.customerName = ''
|
||||
queryParams.mobile = ''
|
||||
queryParams.telephone = ''
|
||||
queryParams.ownerUserId = undefined
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
/** 打开跟进抽屉 */
|
||||
const openInforForm = (id?: number) => {
|
||||
customerId.value = id
|
||||
drawerVisible.value = true
|
||||
}
|
||||
/** 打开分配弹窗 */
|
||||
const openAllocateForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要分配的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
const depId =0
|
||||
formRef.value.open(customerIds, depId)
|
||||
}
|
||||
|
||||
/** 打开转公海弹窗 */
|
||||
const openTransferForm = () => {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message.error('请选择要转移的客户')
|
||||
return
|
||||
}
|
||||
const customerIds = multipleSelection.value.map(item => item.id)
|
||||
transferFormRef.value.open(customerIds)
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: CustomerInforVO[]) => {
|
||||
multipleSelection.value = val
|
||||
console.log(multipleSelection.value)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepList()
|
||||
])
|
||||
|
||||
depOptions.value = buildTree(depList.value)
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
||||
<el-tab-pane :label="'所有客户 (' + counts.all + ')'" name="all">
|
||||
<AllCustomer v-if="activeTab === 'all'" @update-count="updateCount('all', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'今日分配 (' + counts.today + ')'" name="today">
|
||||
<TodayCustomer v-if="activeTab === 'today'" @update-count="updateCount('today', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'未跟进客户 (' + counts.unfollow + ')'" name="unfollow">
|
||||
<UnfollowCustomer v-if="activeTab === 'unfollow'" @update-count="updateCount('unfollow', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'重点客户 (' + counts.import + ')'" name="import">
|
||||
<ImportCustomer v-if="activeTab === 'import'" @update-count="updateCount('import', $event)" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="'近七天新数据 (' + counts.seven + ')'" name="seven">
|
||||
<SevenCustomer v-if="activeTab === 'seven'" @update-count="updateCount('seven', $event)" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import AllCustomer from '@/views/crm/customer/team/tables/all.vue'
|
||||
import TodayCustomer from '@/views/crm/customer/team/tables/today.vue'
|
||||
import UnfollowCustomer from '@/views/crm/customer/team/tables/unfollow.vue'
|
||||
import ImportCustomer from '@/views/crm/customer/team/tables/import.vue'
|
||||
import SevenCustomer from '@/views/crm/customer/team/tables/seven.vue'
|
||||
defineOptions({ name: 'CustomerTabs' })
|
||||
|
||||
const activeTab = ref('all')
|
||||
const counts = reactive({
|
||||
all: 0,
|
||||
today: 0,
|
||||
unfollow: 0,
|
||||
import: 0,
|
||||
seven: 0
|
||||
})
|
||||
|
||||
// 更新各标签页的数量
|
||||
const updateCount = (type: string, count: number) => {
|
||||
counts[type] = count
|
||||
}
|
||||
|
||||
const handleTabClick = () => {
|
||||
// 可以在这里处理标签页切换的逻辑
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs__content) {
|
||||
padding: 20px 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -131,15 +131,14 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
||||
import download from '@/utils/download'
|
||||
import { ChannelApi, ChannelVO } from '@/api/crm/config/channel'
|
||||
import {CustomerSourceApi} from '@/api/crm/config/customersource'
|
||||
import { ChannelVO } from '@/api/crm/config/channel'
|
||||
import {UploadRecordApi} from '@/api/crm/data/upload/record'
|
||||
import { CrmCustomerDataBelongEnum } from '@/utils/constants'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { OpenSeaApi, OpenSeaVO } from '@/api/crm/opensea'
|
||||
|
||||
/** 导入记录 表单 */
|
||||
defineOptions({ name: 'UploadRecordForm' })
|
||||
|
|
@ -175,11 +174,9 @@ const formRules = reactive({
|
|||
customerSourceId: [{ required: true, message: '请选择来源标签', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const openSeaList = ref<OpenSeaVO[]>([])
|
||||
const customerSourceList = ref<[]>([])
|
||||
const channelList = ref<ChannelVO[]>([])
|
||||
let customerSourceOptions = ref<any>();
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
const crmStore = useCrmStore()
|
||||
const { openSeaList, customerSourceList, channelList, userList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
|
@ -284,12 +281,20 @@ const importTemplate = async () => {
|
|||
}
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
openSeaList.value=await OpenSeaApi.getOpenSeaList(null);
|
||||
customerSourceList.value=await CustomerSourceApi.getCustomerSourceList(null);
|
||||
customerSourceOptions.value=buildTree(customerSourceList.value);
|
||||
channelList.value=await ChannelApi.getChannelList(null);
|
||||
const newChannel: ChannelVO = { id: 0, name: '未知', type:'' , remark:""};
|
||||
channelList.value.unshift(newChannel);
|
||||
userList.value=await UserApi.getSimpleUsertList();
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getOpenSeaList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getUserList()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
|
||||
// 添加未知渠道选项
|
||||
const newChannel: ChannelVO = { id: 0, name: '未知', type: '', remark: "" }
|
||||
// 使用展开运算符创建新数组,避免直接修改响应式数据
|
||||
channelList.value = [newChannel, ...channelList.value]
|
||||
})
|
||||
</script>
|
||||
|
|
@ -178,7 +178,8 @@ import { OpenSeaApi, OpenSeaVO } from '@/api/crm/opensea'
|
|||
import { ChannelApi, ChannelVO } from '@/api/crm/config/channel'
|
||||
import {CustomerSourceApi} from '@/api/crm/config/customersource'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 导入记录 列表 */
|
||||
defineOptions({ name: 'UploadRecord' })
|
||||
|
|
@ -205,12 +206,10 @@ const queryParams = reactive({
|
|||
channelId:undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const openSeaList = ref<OpenSeaVO[]>([])
|
||||
const customerSourceList = ref<[]>([])
|
||||
const channelList = ref<ChannelVO[]>([])
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
let customerSourceOptions = ref<any>();
|
||||
|
||||
const crmStore = useCrmStore()
|
||||
const { openSeaList, customerSourceList, channelList, userList } = storeToRefs(crmStore)
|
||||
let customerSourceOptions = ref<any>()
|
||||
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
|
|
@ -258,15 +257,20 @@ const handleDelete = async (id: number) => {
|
|||
} catch {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getOpenSeaList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getChannelList(),
|
||||
crmStore.getUserList()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
|
||||
// 加载列表数据
|
||||
await getList()
|
||||
openSeaList.value = await OpenSeaApi.getOpenSeaList(null);
|
||||
customerSourceList.value = await CustomerSourceApi.getCustomerSourceList(null);
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value);
|
||||
channelList.value = await ChannelApi.getChannelList(null);
|
||||
userList.value = await UserApi.getSimpleUsertList();
|
||||
})
|
||||
</script>
|
||||
|
|
@ -108,8 +108,9 @@
|
|||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { FollwRecordApi, FollwRecordVO } from '@/api/crm/follw'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as DepApi from '@/api/system/dept'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
/** 跟进记录 列表 */
|
||||
defineOptions({ name: 'FollwRecord' })
|
||||
|
||||
|
|
@ -129,14 +130,15 @@ const queryParams = reactive({
|
|||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
const depList = ref<DepApi.DeptVO[]>([])
|
||||
let depOptions = ref<any>();
|
||||
|
||||
const cascaderProps= {
|
||||
emitPath: false,
|
||||
}
|
||||
// 使用 CRM store
|
||||
const crmStore = useCrmStore()
|
||||
const { userList, depList } = storeToRefs(crmStore)
|
||||
let depOptions = ref<any>()
|
||||
|
||||
const cascaderProps = {
|
||||
emitPath: false
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
|
|
@ -162,12 +164,18 @@ const resetQuery = () => {
|
|||
handleQuery()
|
||||
}
|
||||
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepList()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
depOptions.value = buildTree(depList.value)
|
||||
|
||||
// 加载列表数据
|
||||
await getList()
|
||||
userList.value=await UserApi.getSimpleUsertList();
|
||||
depList.value=await DepApi.getSimpleDeptList();
|
||||
depOptions.value=buildTree(depList.value);
|
||||
})
|
||||
</script>
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followstatusList"
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
|
|
@ -134,9 +134,9 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastContactTime">
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastContactTime"
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
|
|
@ -188,7 +188,7 @@
|
|||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followstatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未知" }}
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未知" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
|
|
@ -199,8 +199,8 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastContactTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastContactContent" width="180px"/>
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
|
|
@ -276,17 +276,11 @@
|
|||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { OpenSeaApi, OpenSeaVO } from '@/api/crm/opensea/index'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import { ImportLevelApi, ImportLevelVO } from '@/api/crm/config/Importlevel'
|
||||
import { FollowStatusApi, FollowStatusVO } from '@/api/crm/config/followstatus'
|
||||
import { CustomerSourceApi } from '@/api/crm/config/customersource'
|
||||
import {CustomerTypeApi} from '@/api/crm/config/customertype'
|
||||
import CustomerDetail from '@/views/crm/components/CustomerDetail/CustomerDetail.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as DepApi from '@/api/system/dept'
|
||||
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
|
|
@ -298,16 +292,14 @@ const loading = ref(true) // 列表的加载中
|
|||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const importLevelList = ref<ImportLevelVO[]>([])
|
||||
const followstatusList = ref<FollowStatusVO[]>([])
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
const depList = ref<DepApi.DeptVO[]>([])
|
||||
const openSeaList = ref<OpenSeaVO[]>([])
|
||||
const customerSourceList = ref<[]>([])
|
||||
const customerTypeList = ref<[]>([])
|
||||
let depOptions = ref<any>();
|
||||
let customerSourceOptions = ref<any>();
|
||||
let customerTypeOptions = ref<any>();
|
||||
|
||||
// 使用 CRM store
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, followStatusList, customerSourceList, customerTypeList, depList, openSeaList } = storeToRefs(crmStore)
|
||||
let depOptions = ref<any>()
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
|
|
@ -333,7 +325,7 @@ const queryParams = reactive({
|
|||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
lastContactTime: [],
|
||||
lastFollowTime: [],
|
||||
openSeaId: undefined as number | undefined,
|
||||
createTime: []
|
||||
})
|
||||
|
|
@ -386,22 +378,27 @@ const handleSelectionChange = (val: CustomerInforVO[]) => {
|
|||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 加载渠道列表
|
||||
importLevelList.value = await ImportLevelApi.getImportLevelList(null);
|
||||
followstatusList.value = await FollowStatusApi.getFollowStatusList(null);
|
||||
userList.value=await UserApi.getSimpleUsertList();
|
||||
depList.value=await DepApi.getSimpleDeptList();
|
||||
customerSourceList.value=await CustomerSourceApi.getCustomerSourceList(null);
|
||||
customerTypeList.value=await CustomerTypeApi.getCustomerTypeList(null);
|
||||
depOptions.value=buildTree(depList.value);
|
||||
customerSourceOptions.value=buildTree(customerSourceList.value);
|
||||
customerTypeOptions.value=buildTree(customerTypeList.value);
|
||||
openSeaList.value=await OpenSeaApi.getOpenSeaList(null);
|
||||
if (openSeaList.value?.length <=0) {
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepList(),
|
||||
crmStore.getOpenSeaList()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
depOptions.value = buildTree(depList.value)
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
|
||||
if (openSeaList.value?.length <= 0) {
|
||||
message.error('你没有适配的公海')
|
||||
}else{
|
||||
queryParams.openSeaId = openSeaList.value[0].id;
|
||||
await getList();
|
||||
} else {
|
||||
queryParams.openSeaId = openSeaList.value[0].id
|
||||
await getList()
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@
|
|||
class="!w-120px "
|
||||
>
|
||||
<el-option
|
||||
v-for="option in followstatusList"
|
||||
v-for="option in followStatusList"
|
||||
:key="option.id"
|
||||
:value="option.id"
|
||||
:label="option.name"
|
||||
|
|
@ -134,9 +134,9 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="最后跟进" prop="lastContactTime">
|
||||
<el-form-item label="最后跟进" prop="lastFollowTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.lastContactTime"
|
||||
v-model="queryParams.lastFollowTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
|
|
@ -188,7 +188,7 @@
|
|||
<el-table-column label="跟进状态" align="center" prop="followStatusId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ followstatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未知" }}
|
||||
{{ followStatusList.find((cs) => cs.id === scope.row.followStatusId)?.name||"未知" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
|
|
@ -199,8 +199,8 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-table-column >
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastContactTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastContactContent" width="180px"/>
|
||||
<el-table-column label="最后跟进时间" align="center" prop="lastFollowTime" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="最后跟进内容" align="center" prop="lastFollowContent" width="180px"/>
|
||||
<el-table-column label="客户类型" align="center" prop="customerTypeId" >
|
||||
<template #default="scope">
|
||||
<div>
|
||||
|
|
@ -276,20 +276,15 @@
|
|||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { buildTree } from '@/utils/tree'
|
||||
import { OpenSeaApi, OpenSeaVO } from '@/api/crm/opensea/index'
|
||||
import { CustomerInforApi, CustomerInforVO } from '@/api/crm/customer/customer'
|
||||
import { ImportLevelApi, ImportLevelVO } from '@/api/crm/config/Importlevel'
|
||||
import { FollowStatusApi, FollowStatusVO } from '@/api/crm/config/followstatus'
|
||||
import { CustomerSourceApi } from '@/api/crm/config/customersource'
|
||||
import {CustomerTypeApi} from '@/api/crm/config/customertype'
|
||||
import CustomerDetail from '@/views/crm/components/CustomerDetail/CustomerDetail.vue'
|
||||
import { useCrmStore } from '@/store/modules/crm'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import CustomerDetail from '@/views/crm/components/Customer/Detail.vue'
|
||||
import AllocateForm from '@/views/crm/components/Allocate/AllocateForm.vue'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as DepApi from '@/api/system/dept'
|
||||
|
||||
|
||||
/** 客户信息 列表 */
|
||||
defineOptions({ name: 'AllCustomer' })
|
||||
defineOptions({ name: 'MyOpenSea' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
|
@ -298,16 +293,14 @@ const loading = ref(true) // 列表的加载中
|
|||
const list = ref<CustomerInforVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const importLevelList = ref<ImportLevelVO[]>([])
|
||||
const followstatusList = ref<FollowStatusVO[]>([])
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
const depList = ref<DepApi.DeptVO[]>([])
|
||||
const openSeaList = ref<OpenSeaVO[]>([])
|
||||
const customerSourceList = ref<[]>([])
|
||||
const customerTypeList = ref<[]>([])
|
||||
let depOptions = ref<any>();
|
||||
let customerSourceOptions = ref<any>();
|
||||
let customerTypeOptions = ref<any>();
|
||||
|
||||
// 使用 CRM store
|
||||
const crmStore = useCrmStore()
|
||||
const { importLevelList, followStatusList, customerSourceList, customerTypeList, depList, openSeaList } = storeToRefs(crmStore)
|
||||
let depOptions = ref<any>()
|
||||
let customerSourceOptions = ref<any>()
|
||||
let customerTypeOptions = ref<any>()
|
||||
|
||||
const drawerVisible = ref(false) // 抽屉是否显示
|
||||
const customerId = ref<number>() // 当前查看的客户ID
|
||||
const multipleSelection = ref<CustomerInforVO[]>([])
|
||||
|
|
@ -333,7 +326,7 @@ const queryParams = reactive({
|
|||
customerTypeId: undefined,
|
||||
importLevelId: undefined,
|
||||
followStatusId: undefined,
|
||||
lastContactTime: [],
|
||||
lastFollowTime: [],
|
||||
openSeaId: undefined as number | undefined,
|
||||
createTime: []
|
||||
})
|
||||
|
|
@ -386,26 +379,28 @@ const handleSelectionChange = (val: CustomerInforVO[]) => {
|
|||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 加载渠道列表
|
||||
importLevelList.value = await ImportLevelApi.getImportLevelList(null);
|
||||
followstatusList.value = await FollowStatusApi.getFollowStatusList(null);
|
||||
userList.value=await UserApi.getSimpleUsertList();
|
||||
depList.value=await DepApi.getSimpleDeptList();
|
||||
customerSourceList.value=await CustomerSourceApi.getCustomerSourceList(null);
|
||||
customerTypeList.value=await CustomerTypeApi.getCustomerTypeList(null);
|
||||
depOptions.value=buildTree(depList.value);
|
||||
customerSourceOptions.value=buildTree(customerSourceList.value);
|
||||
customerTypeOptions.value=buildTree(customerTypeList.value);
|
||||
openSeaList.value=await OpenSeaApi.getOpenSeaListByCurUser();
|
||||
if (openSeaList.value?.length <=0) {
|
||||
// 加载数据
|
||||
await Promise.all([
|
||||
crmStore.getImportLevelList(),
|
||||
crmStore.getFollowStatusList(),
|
||||
crmStore.getCustomerSourceList(),
|
||||
crmStore.getCustomerTypeList(),
|
||||
crmStore.getUserList(),
|
||||
crmStore.getDepList(),
|
||||
crmStore.getOpenSeaListByCurUser()
|
||||
])
|
||||
|
||||
// 构建树形结构
|
||||
depOptions.value = buildTree(depList.value)
|
||||
customerSourceOptions.value = buildTree(customerSourceList.value)
|
||||
customerTypeOptions.value = buildTree(customerTypeList.value)
|
||||
|
||||
if (openSeaList.value?.length <= 0) {
|
||||
message.error('你没有适配的公海')
|
||||
}else{
|
||||
queryParams.openSeaId = openSeaList.value[0].id;
|
||||
await getList();
|
||||
} else {
|
||||
queryParams.openSeaId = openSeaList.value[0].id
|
||||
await getList()
|
||||
}
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
/** 领取客户 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue