新增客户列表

This commit is contained in:
dengbw
2023-03-03 10:00:55 +08:00
parent 23cf8a7e36
commit 80be328353
12 changed files with 747 additions and 4 deletions
+27 -1
View File
@@ -14,6 +14,20 @@ export async function pageActivity(params) {
return Promise.reject(new Error(res.data.message));
}
/**
* 获取活动信息
* @param params 查询条件
*/
export async function getActivityInfo(params) {
const res = await request.get('/sylive/activity/info', {
params
});
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 根据id查询活动
* @param id 活动id
@@ -336,7 +350,7 @@ export async function getStatisticsStackedWatchOrder(params) {
/**
* 修改抽奖配置
* @param data 活动信息
* @param data 信息
*/
export async function updateActivityDraw(data) {
const res = await request.put('/sylive/activity/draw', data);
@@ -345,3 +359,15 @@ export async function updateActivityDraw(data) {
}
return Promise.reject(new Error(res.data.message));
}
/**
* 修改回访标签
* @param data 信息
*/
export async function updateActivityVisitTag(data) {
const res = await request.put('/sylive/activity/visit_tag', data);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
+44
View File
@@ -0,0 +1,44 @@
import request from '@/utils/request';
/**
* 查询订单列表
* @param params 查询条件
*/
export async function listGroupsCustomer(params) {
const res = await request.get('/sylive/groupsCustomer/', {
params
});
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 导出订单
* @param params 查询条件
*/
export async function exportGroupsCustomer(params) {
const res = await request.get('/sylive/groupsCustomer/export', {
params
});
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}
/**
* 导入用户
* @param file excel文件
*/
export async function importGroupsCustomer(file, activityId) {
const formData = new FormData();
formData.append('file', file);
formData.append('activityId', activityId);
const res = await request.post('/sylive/groupsCustomer/import', formData);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
@@ -80,7 +80,7 @@
</el-select>
</template>
</el-table-column>
<el-table-column label="核销" width="100px" align="center">
<el-table-column label="是否核销" width="100px" align="center">
<template v-slot="{ row }">
<el-select v-model="row.ifCode" class="ele-fluid">
<el-option label="否" value="0" />
@@ -0,0 +1,123 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
width="930px"
:visible="visible"
:append-to-body="true"
:close-on-click-modal="true"
custom-class="ele-dialog-form"
title="修改回访标签"
@update:visible="updateVisible"
>
<el-form ref="form" :model="form" label-width="82px">
<el-form-item label="回访标签:">
<el-table :data="form.visitTag" :border="true" style="width: 100%">
<el-table-column label="ID" align="center" width="70px">
<template v-slot="{ row }">{{ row.id }}</template>
</el-table-column>
<el-table-column label="回访标题" align="center" width="150px">
<template v-slot="{ row }">{{ row.title }}</template>
</el-table-column>
<el-table-column label="自定义文案" align="center">
<template v-slot="{ row }">
<el-input v-model="row.tag" placeholder="请输入自定义文案" />
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<template v-slot:footer>
<el-button @click="updateVisible(false)">取消</el-button>
<el-button type="primary" :loading="loading" @click="save">
保存
</el-button>
</template>
</ele-modal>
</template>
<script>
import { updateActivityVisitTag } from '@/api/sylive/activity';
export default {
components: {},
props: {
// 弹窗是否打开
visible: Boolean,
// 修改回显的数据
data: Object
},
data() {
const defaultForm = {
activityId: null,
visitTag: []
};
return {
defaultForm,
// 表单数据
form: { ...defaultForm },
// 提交状态
loading: false,
// 是否是修改
isUpdate: false
};
},
computed: {
// 是否开启响应式布局
styleResponsive() {
return this.$store.state.theme.styleResponsive;
}
},
methods: {
/* 保存编辑 */
save() {
this.$refs.form.validate((valid) => {
if (!valid) {
return false;
}
this.loading = true;
const data = {
...this.form
};
updateActivityVisitTag(data)
.then((msg) => {
this.loading = false;
this.$message.success(msg);
this.updateVisible(false);
this.$emit('done');
})
.catch((e) => {
this.loading = false;
this.$message.error(e.message);
});
});
},
/* 更新visible */
updateVisible(value) {
this.$emit('update:visible', value);
}
},
watch: {
visible(visible) {
if (visible) {
if (this.data) {
this.$util.assignObject(this.form, {
...this.data
});
this.isUpdate = true;
} else {
this.form.visitTag = [];
this.isUpdate = false;
}
} else {
this.$refs.form.clearValidate();
this.form = { ...this.defaultForm };
}
}
}
};
</script>
<style>
.ele-image-upload-list .ele-image-upload-item {
margin-top: 2px;
margin-bottom: 2px;
}
</style>
+20
View File
@@ -73,6 +73,7 @@
<el-dropdown-item command="order">订单列表</el-dropdown-item>
<el-dropdown-item command="win">中奖名单</el-dropdown-item>
<el-dropdown-item command="exchange">核销记录</el-dropdown-item>
<el-dropdown-item command="customer">客户列表</el-dropdown-item>
<el-dropdown-item command="code">活动链接</el-dropdown-item>
</el-dropdown-menu>
<el-dropdown-menu v-else>
@@ -93,6 +94,7 @@
<el-dropdown-item command="groups">修改分组</el-dropdown-item>
<el-dropdown-item command="copy">复制活动</el-dropdown-item>
<el-dropdown-item command="draw">抽奖配置</el-dropdown-item>
<el-dropdown-item command="visitTag">回访标签</el-dropdown-item>
<!--
<el-dropdown-item command="item">修改权益商品</el-dropdown-item>
<el-dropdown-item command="coupon">修改券</el-dropdown-item>
@@ -141,6 +143,12 @@
:visible.sync="showEditDraw"
@done="reload"
/>
<!-- 抽奖配置弹窗 -->
<activity-visit-tag
:data="current"
:visible.sync="showEditVisitTag"
@done="reload"
/>
</div>
</template>
@@ -151,6 +159,7 @@
import ActivityItem from './components/activity-item.vue';
import ActivityCoupon from './components/activity-coupon.vue';
import ActivityDraw from './components/activity-draw.vue';
import ActivityVisitTag from './components/activity-visit-tag';
import {
pageActivity,
removeActivity,
@@ -163,6 +172,7 @@
export default {
name: 'syliveActivity',
components: {
ActivityVisitTag,
QrCode,
ActivitySearch,
ActivityEdit,
@@ -254,6 +264,7 @@
showEditCoupon: false,
// 是否显示编辑券弹窗
showEditDraw: false,
showEditVisitTag: false,
// 是否显示二维码弹窗
showCode: false
};
@@ -279,6 +290,12 @@
this.current = row.draw;
this.current.activityId = row.activityId;
this.showEditDraw = true;
} else if (command === 'visitTag') {
this.current = {
visitTag: row.visitTag,
activityId: row.activityId
};
this.showEditVisitTag = true;
} else if (command === 'goods') {
this.$router.replace('/sylive/goods?id=' + row.activityId);
} else if (command === 'groups') {
@@ -327,6 +344,9 @@
} else if (command === 'exchange') {
url = '/sylive/groups-exchange?id=' + row.activityId;
this.$router.replace(url);
} else if (command === 'customer') {
url = '/sylive/groups-customer?id=' + row.activityId;
this.$router.replace(url);
} else if (command === 'code') {
this.current = row;
this.showCode = true;
@@ -80,7 +80,7 @@
class="ele-fluid"
/>
</el-form-item>
<el-form-item label="核销:">
<el-form-item label="是否核销:">
<el-radio-group v-model="form.ifCode">
<el-radio :label="0"></el-radio>
<el-radio :label="1"></el-radio>
@@ -0,0 +1,101 @@
<!-- 用户导入弹窗 -->
<template>
<ele-modal
width="520px"
title="导入客户"
:visible="visible"
@update:visible="updateVisible"
>
<el-upload
drag
action=""
class="ele-block"
v-loading="loading"
accept=".xls,.xlsx"
:show-file-list="false"
:before-upload="doUpload"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处, <em>点击上传</em></div>
<template v-slot:tip>
<div class="el-upload__tip ele-text-center">
<span>只能上传xlsxlsx文件, </span>
<el-link
download
:href="url"
type="primary"
:underline="false"
style="vertical-align: baseline"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
</ele-modal>
</template>
<script>
import { importGroupsCustomer } from '@/api/sylive/groups-customer';
import { API_BASE_URL } from '@/config/setting';
export default {
props: {
// 是否打开弹窗
visible: Boolean
},
data() {
return {
// 导入请求状态
loading: false,
// 导入模板下载地址
url: API_BASE_URL.replace('api', '') + 'temp/customer.xlsx'
};
},
methods: {
/* 上传 */
doUpload(file) {
if (
![
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
].includes(file.type)
) {
this.$message.error('只能选择 excel 文件');
return false;
}
if (file.size / 1024 / 1024 > 5) {
this.$message.error('大小不能超过 5MB');
return false;
}
this.loading = true;
const activityId = this.$route.query.id;
importGroupsCustomer(file, activityId)
.then((msg) => {
this.loading = false;
this.$message.success(msg);
this.updateVisible(false);
this.$emit('done');
})
.catch((e) => {
this.loading = false;
this.$message.error(e.message);
});
return false;
},
/* 更新visible */
updateVisible(value) {
this.$emit('update:visible', value);
}
}
};
</script>
<style lang="scss" scoped>
.ele-block {
:deep(.el-upload),
:deep(.el-upload-dragger) {
width: 100%;
}
}
</style>
@@ -0,0 +1,164 @@
<!-- 搜索表单 -->
<template>
<el-form
label-width="77px"
class="ele-form-search"
@keyup.enter.native="search"
@submit.native.prevent
>
<el-row :gutter="15">
<el-col v-bind="styleResponsive ? { lg: 5, md: 10 } : { span: 5 }">
<el-form-item label="门店:">
<ele-tree-select
:data="groupsList"
label-key="groupsName"
value-key="groupsId"
v-model="where.bizId"
:clearable="true"
placeholder="请选择"
:disabled="false"
:default-expand-all="false"
:expand-on-click-node="false"
/>
</el-form-item>
</el-col>
<el-col v-bind="styleResponsive ? { lg: 5, md: 10 } : { span: 5 }">
<el-form-item label="客户姓名:">
<el-input clearable v-model="where.name" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col v-bind="styleResponsive ? { lg: 5, md: 10 } : { span: 5 }">
<el-form-item label="手机号:">
<el-input clearable v-model="where.mobile" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col v-bind="styleResponsive ? { lg: 5, md: 10 } : { span: 5 }">
<el-form-item label="状态:">
<el-select
clearable
v-model="where.status"
placeholder="请选择"
class="ele-fluid"
>
<el-option
v-for="(option, index) in data.statusAry"
:key="index"
:value="option.value"
:label="option.label"
/>
</el-select>
</el-form-item>
</el-col>
<el-col v-bind="styleResponsive ? { lg: 5, md: 10 } : { span: 5 }">
<el-form-item label="回访标签:">
<el-select
clearable
v-model="where.visitTagId"
placeholder="请选择"
class="ele-fluid"
>
<el-option
v-for="(option, index) in data.visitTagAry"
:key="index"
:value="option.value"
:label="option.label"
/>
</el-select>
</el-form-item>
</el-col>
<el-col v-bind="styleResponsive ? { lg: 4, md: 8 } : { span: 4 }">
<div class="ele-form-actions">
<el-button
type="primary"
icon="el-icon-search"
class="ele-btn-icon"
@click="search"
>
查询
</el-button>
<el-button @click="reset">重置</el-button>
</div>
</el-col>
</el-row>
</el-form>
</template>
<script>
import { listGroups } from '@/api/sylive/groups';
export default {
props: {
data: Object
},
data() {
// 默认表单数据
const defaultWhere = {
name: '',
mobile: '',
status: '',
visitTagId: ''
};
return {
activityId: null,
// 门店数据
groupsList: [],
// 商品数据
goodsList: [],
// 表单数据
where: { ...defaultWhere }
};
},
computed: {
// 是否开启响应式布局
styleResponsive() {
return this.$store.state.theme.styleResponsive;
}
},
created() {},
methods: {
/* 搜索 */
search() {
this.$emit('search', this.where);
},
/* 重置 */
reset() {
this.where = { ...this.defaultWhere };
this.search();
},
/* 查询分组 */
groupsQuery() {
listGroups({ activityId: this.activityId })
.then((list) => {
this.groupsList = this.$util.toTreeData({
data: list,
idField: 'groupsId',
parentIdField: 'parentId'
});
})
.catch((e) => {
this.$message.error(e.message);
});
}
},
watch: {
$route: {
handler(route) {
const { path } = route;
if (path !== '/sylive/groups-customer') {
return;
}
const activityId = this.$route.query.id;
if (!activityId || activityId == this.activityId) {
return;
}
this.activityId = activityId;
if (this.$refs.table) {
this.$refs.table.reload({ page: 1 });
}
this.groupsQuery();
},
immediate: true
}
}
};
</script>
+255
View File
@@ -0,0 +1,255 @@
<template>
<div class="ele-body">
<el-card shadow="never">
<!-- 搜索表单 -->
<gro-customer-search :data="activityInfo" @search="reload" />
<!-- 数据表格 -->
<ele-pro-table
ref="table"
:columns="columns"
:datasource="datasource"
cache-key="syliveCustomerTable"
>
<!-- 表头工具栏 -->
<template v-slot:toolbar>
<el-button
size="small"
type="primary"
class="ele-btn-icon"
icon="el-icon-dcustomerload"
@click="exportData"
>
导出客户
</el-button>
<el-button
size="small"
icon="el-icon-upload2"
class="ele-btn-icon"
@click="openImport"
>
导入客户
</el-button>
</template>
<template v-slot:useStatus="{ row }">
<span
:class="row.useStatus == 2 ? 'ele-text-success' : 'ele-text-danger'"
>
{{ row.useStatusName }}
</span>
</template>
<template v-slot:action="{ row }">
<el-link
v-if="row.useStatus != 1"
type="primary"
:underline="false"
icon="el-icon-view"
@click="openDetail(row)"
>
详情
</el-link>
</template>
</ele-pro-table>
</el-card>
<!-- 导入弹窗 -->
<gro-customer-import :visible.sync="showImport" @done="reload" />
</div>
</template>
<script>
import { setPageTabTitle } from '@/utils/page-tab-util';
import GroCustomerSearch from './components/gro-customer-search.vue';
import { getActivityInfo } from '@/api/sylive/activity';
import {
listGroupsCustomer,
exportGroupsCustomer
} from '@/api/sylive/groups-customer';
import { utils, writeFile } from 'xlsx';
import GroCustomerImport from './components/gro-customer-import';
const ROUTE_PATH = '/sylive/groups-customer';
export default {
name: 'syliveGroupsCustomer',
components: {
GroCustomerImport,
GroCustomerSearch
},
data() {
return {
title: '客户列表',
activityId: null,
activityInfo: { statusAry: [], visitTagAry: [] },
// 加载状态
loading: true,
showDetail: false,
showStatus: false,
showImport: false,
current: [],
currentStatus: {},
useType: 0,
// 表格列配置
columns: [
{
prop: 'customerId',
label: 'ID',
minWidth: 45,
align: 'center',
showOverflowTooltip: true,
fixed: 'left'
},
{
prop: 'name',
label: '姓名',
showOverflowTooltip: true,
minWidth: 60
},
{
prop: 'mobile',
label: '手机号',
showOverflowTooltip: true,
minWidth: 60
},
{
prop: 'consultant',
label: '顾问',
showOverflowTooltip: true,
minWidth: 60
},
{
prop: 'levelName1',
label: '战区',
showOverflowTooltip: true,
minWidth: 80
},
{
prop: 'levelName2',
label: '战队',
showOverflowTooltip: true,
minWidth: 80
},
{
prop: 'stores',
label: '门店',
showOverflowTooltip: true,
minWidth: 80
},
{
prop: 'statusName',
label: '状态',
align: 'center',
showOverflowTooltip: true,
minWidth: 60
},
{
prop: 'visitTagName',
label: '回访标签',
align: 'center',
showOverflowTooltip: true,
minWidth: 80
}
]
};
},
methods: {
/* 打开导入弹窗 */
openImport() {
this.showImport = true;
},
/* 表格数据源 */
datasource({ page, limit, where, order }) {
const activityId = this.activityId;
this.useType = where ? where.useType : 0;
return listGroupsCustomer({
...where,
...order,
page,
limit,
activityId
});
},
query() {
this.loading = true;
getActivityInfo({
activityId: Number(this.activityId),
type: 'customer'
})
.then((data) => {
this.loading = false;
// 修改页签标题
if (this.$route.path === ROUTE_PATH) {
this.activityInfo = data;
this.title = data.title + '的客户列表';
setPageTabTitle(this.title);
}
})
.catch((e) => {
this.loading = false;
this.$message.error(e.message);
});
},
/* 刷新表格 */
reload(where) {
this.useType = where ? where.useType : 0;
this.$refs.table.reload({ page: 1, where: where });
},
/* 导出数据 */
exportData() {
const array = [
['姓名', '手机号', '顾问', '战区', '战队', '门店', '状态', '回访标签']
];
const loading = this.$loading({ lock: true });
this.$refs.table.doRequest(({ where, order }) => {
const activityId = this.activityId;
exportGroupsCustomer({ ...where, ...order, activityId })
.then((data) => {
loading.close();
data.forEach((d) => {
array.push([
d.name,
d.mobile,
d.consultant,
d.levelName1,
d.levelName2,
d.stores,
d.statusName,
d.visitTagName
]);
});
writeFile(
{
SheetNames: ['Sheet1'],
Sheets: {
Sheet1: utils.aoa_to_sheet(array)
}
},
this.title + '.xlsx'
);
})
.catch((e) => {
loading.close();
this.$message.error(e.message);
});
});
}
},
watch: {
$route: {
handler(route) {
const { path } = route;
if (path !== ROUTE_PATH) {
return;
}
const activityId = this.$route.query.id;
if (!activityId || activityId == this.activityId) {
return;
}
this.activityId = activityId;
this.query();
if (this.$refs.table) {
this.$refs.table.reload({ page: 1 });
}
},
immediate: true
}
}
};
</script>
@@ -42,6 +42,11 @@
{{ item.cfTime }}
</div>
</el-form-item>
<el-form-item label="收货地址:" v-if="item.address">
<div class="ele-text-secondary">
{{ item.address }}
</div>
</el-form-item>
<el-form-item label="描述:" v-if="item.descrip">
<el-input
disabled="true"
@@ -37,6 +37,7 @@
<script>
import { importGroupsExchange } from '@/api/sylive/groups-exchange';
import { API_BASE_URL } from '@/config/setting';
export default {
props: {
@@ -48,7 +49,7 @@
// 导入请求状态
loading: false,
// 导入模板下载地址
url: 'http://market.dev.liche.cn/temp/exchange.xlsx'
url: API_BASE_URL.replace('api', '') + 'temp/exchange.xlsx'
};
},
methods: {
@@ -70,6 +70,10 @@
methods: {
/* 保存编辑 */
save() {
if (this.form.useStatus != 2 && !this.form.descrip) {
this.$message.error('请输入驳回理由');
return;
}
this.$refs.form.validate((valid) => {
if (!valid) {
return false;