添加:邀请码管理(增加、作废、导出、批量下载)

修复:兑换码、邀请码 - 点击导出但实际却执行下载操作的问题
优化:兑换码、邀请码 - 二维码扫码内容优化,添加类型区分
优化:兑换码、邀请码 - 导出表格内容添加二维码具体内容信息
修复:增加兑换码、邀请码 - 类型为随机生成时,会进入顺序生成的步骤,然后直接报错,导致生成失败的问题
This commit is contained in:
wuhui_zzw 2024-04-02 18:17:42 +08:00
parent 3540ec75c9
commit 98155cd4dd
4 changed files with 474 additions and 9 deletions

View File

@ -468,9 +468,26 @@ export function exchangeCodeCancelForm() {
export function exchangeCodeUpdate(data) {
return request.post('user/exchangeCode/updateCode',data)
}
// 邀请码 - 列表获取
export function inviteCodeList(data) {
return request.get('user/inviteCode/getList', data)
}
// 邀请码 - 批次列表获取
export function inviteCodeBatchList() {
return request.get('user/inviteCode/getBatchList')
}
// 邀请码 - 添加表单
export function inviteCodeEditForm() {
return request.get('user/inviteCode/editForm')
}
// 邀请码 - 作废
export function inviteCodeCancelForm() {
return request.get('user/inviteCode/cancelForm')
}
// 邀请码 - 修改兑换码
export function inviteCodeUpdate(data) {
return request.post('user/inviteCode/updateCode',data)
}

View File

@ -132,6 +132,15 @@ const userRouter =
},
component: () => import('@/views/user/member/exchangeCode')
},
{
path: 'inviteCode',
name: 'inviteCode',
meta: {
title: '邀请码',
noCache: true
},
component: () => import('@/views/user/member/inviteCode')
},
]
},
]

View File

@ -22,7 +22,7 @@
<el-button size="small" type="success" @click="addExchangeCode">添加兑换码</el-button>
<el-button size="small" type="warning" @click="activateExchangeCode">分配给商户</el-button>
<el-button size="small" type="danger" @click="cancelExchangeCode">作废兑换码</el-button>
<el-button size="small" type="info" @click="exports">导出兑换码</el-button>
<el-button size="small" type="info" @click="exports('excel')">导出兑换码</el-button>
<el-button size="small" type="info" @click="exports('down')">下载二维码</el-button>
</div>
</div>
@ -167,7 +167,7 @@ export default {
Object.values(res.data.list).forEach(item => {
let refName = 'qrCodeImg'+item.id;
new QRCodeOld(_this.$refs[refName], {
text: item.exchange_code, //
text: item.qr_code_text, //
width: 80,
height: 80,
colorDark: '#000000',
@ -262,14 +262,14 @@ export default {
//
const data = [];
for (const item of list) {
const base64 = await this.textQrcodeToBase64(item[2]);
const base64 = await this.textQrcodeToBase64(item[3]);
data.push({
name: item[2],
value: base64,
});
}
//
this.dataUrlZip(data);
this.dataUrlZip(data, exportData.filename || 'qrcodes');
},
/**
* 将字符串生成二维码并且转成base64
@ -300,8 +300,9 @@ export default {
/**
* 将base64字符串以png格式装进jszip, 然后下载保存到本地
* @param {Array} data {value: base64字符串, name: 二维码的名字}
* @param fileName
*/
dataUrlZip(data) {
dataUrlZip(data, fileName) {
const zip = new JSZip();
for (const item of data) {
@ -313,7 +314,7 @@ export default {
// file-saver
zip.generateAsync({ type: "blob" }).then( (content) =>{
FileSaver.saveAs(content, "qrcodes.zip");
FileSaver.saveAs(content, fileName + ".zip");
});
},
//

View File

@ -0,0 +1,438 @@
<template>
<div class="divBox">
<!--主要内容-->
<el-card class="box-card">
<!--顶部搜索栏-->
<div slot="header" class="clearfix">
<div class="container">
<el-form inline size="small" label-width="80px">
<el-form-item label="">
<el-select v-model="tableFrom.status" class="selWidth" clearable>
<el-option label="未激活" :value="0"></el-option>
<el-option label="已激活" :value="1"></el-option>
<el-option label="已使用" :value="2"></el-option>
<el-option label="已作废" :value="3"></el-option>
</el-select>
<el-select v-if="Object.values(batch_list).length > 0" v-model="tableFrom.batch_unique" class="selWidth" clearable>
<el-option v-for="(item,index) in batch_list" :label="index" :value="item"></el-option>
</el-select>
</el-form-item>
</el-form>
<el-button size="small" type="primary" @click="getList(1)">搜索</el-button>
<el-button size="small" type="success" @click="addExchangeCode">添加兑换码</el-button>
<el-button size="small" type="danger" @click="cancelExchangeCode">作废兑换码</el-button>
<el-button size="small" type="info" @click="exports('excel')">导出兑换码</el-button>
<el-button size="small" type="info" @click="exports('down')">下载二维码</el-button>
</div>
</div>
<!--表格信息-->
<el-table v-loading="listLoading" :data="tableData.data" style="width: 100%" size="mini">
<el-table-column label="ID" prop="id" min-width="80" align="center"/>
<el-table-column label="归属批次" min-width="130" align="center">
<template slot-scope="scope">
{{ scope.row.batch_title }}<br />
{{ scope.row.batch_unique }}
</template>
</el-table-column>
<el-table-column label="兑换码" min-width="150" align="center">
<template slot-scope="scope">
<el-tooltip v-if="scope.row.status == 0" class="item" effect="dark" content="点击修改兑换码" placement="top-start">
<el-button type="info" size="small" icon="el-icon-edit" @click="updateExchangeCode(scope.row)">
{{ scope.row.exchange_code }}
</el-button>
</el-tooltip>
<el-tag v-else-if="scope.row.status == 1" type="primary" effect="dark">{{ scope.row.exchange_code }}</el-tag>
<el-tag v-else-if="scope.row.status == 2" type="success" effect="dark">{{ scope.row.exchange_code }}</el-tag>
<el-tag v-else-if="scope.row.status == 3" type="danger" effect="dark">{{ scope.row.exchange_code }}</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" min-width="150" align="center">
<template slot="header" slot-scope="scope">
状态<br />
激活时间 / 使用时间
</template>
<template slot-scope="scope">
<el-tag v-if="scope.row.status == 0" type="info">{{ scope.row.mer_id > 0 ? '待激活' : '待分配' }}</el-tag>
<template v-else-if="scope.row.status == 1">
<el-tag type="primary">
已激活<br />
{{scope.row.activate}}
</el-tag>
</template>
<template v-else-if="scope.row.status == 2">
<el-tag type="success">
已使用<br />
{{scope.row.use_time}}
</el-tag>
</template>
<template v-else-if="scope.row.status == 3">
<el-tag type="danger">已作废</el-tag>
</template>
</template>
</el-table-column>
<el-table-column label="激活用户" min-width="230" align="center">
<template slot-scope="scope">
<div class="user-content" v-if="scope.row.activateUser">
<div class="user-avatar">
<img :src="scope.row.activateUser.avatar || moren" />
</div>
<div class="user-info">
<div class="nickname">{{ scope.row.activateUser.nickname }}</div>
<div class="agent-type-text">
<el-tag type="info" effect="dark" size="small">{{ scope.row.activateUser.uid }}</el-tag>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="添加时间" prop="create_time" min-width="130" align="center"/>
<el-table-column label="绑定会员卡" prop="svip_name" min-width="100" align="center"/>
<el-table-column label="使用人员" min-width="230" align="center">
<template slot-scope="scope">
<div class="user-content" v-if="scope.row.useUser">
<div class="user-avatar">
<img :src="scope.row.useUser.avatar || moren" />
</div>
<div class="user-info">
<div class="nickname">{{ scope.row.useUser.nickname }}</div>
<div class="agent-type-text">
<el-tag type="info" effect="dark" size="small">{{ scope.row.useUser.uid }}</el-tag>
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="二维码" min-width="100" align="center">
<template slot-scope="scope">
<div :ref="'qrCodeImg'+scope.row.id" class="qr-code-img"></div>
</template>
</el-table-column>
</el-table>
<!--分页-->
<div class="block">
<el-pagination
:page-sizes="[20, 40, 60, 80]"
:page-size="tableFrom.limit"
:current-page="tableFrom.page"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="handleUserSizeChange"
@current-change="pageUserChange"
/>
</div>
</el-card>
</div>
</template>
<script>
import {inviteCodeBatchList, inviteCodeCancelForm, inviteCodeEditForm, inviteCodeList, inviteCodeUpdate} from "@/api/user";
import createWorkBook from "@/utils/newToExcel";
import QRCodeOld from "qrcodejs2";
import QRCode from "qrcode";
import JSZip from "jszip";
import FileSaver from "file-saver";
export default {
name: "preSaleProductList",
components: {},
data() {
return {
moren: require("@/assets/images/f.png"),
//
listLoading: false,
tableFrom: {
page: 1,
limit: 20,
status: '',
batch_unique: '',
},
tableData: {
data: [],
total: 0,
},
batch_list: {},
};
},
watch: {},
mounted() {
this.getList();
this.getBatchList();
},
methods: {
//
getList(num = ''){
let _this = this;
_this.listLoading = true;
_this.tableFrom.page = num ? num : _this.tableFrom.page;
inviteCodeList(_this.tableFrom).then((res) => {
_this.tableData.data = res.data.list;
_this.tableData.total = res.data.count;
_this.listLoading = false;
//
_this.$nextTick(function () {
Object.values(res.data.list).forEach(item => {
let refName = 'qrCodeImg'+item.id;
new QRCodeOld(_this.$refs[refName], {
text: item.qr_code_text, //
width: 80,
height: 80,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCodeOld.CorrectLevel.H,
});
})
});
}).catch((res) => {
_this.listLoading = false;
_this.$message.error(res.message);
});
},
pageUserChange(page) {
this.tableFrom.page = page;
this.getList('');
},
handleUserSizeChange(val) {
this.tableFrom.limit = val;
this.getList('');
},
//
getBatchList(){
let _this = this;
inviteCodeBatchList().then((res) => {
_this.batch_list = res.data || {};
}).catch((res) => {
_this.$message.error(res.message);
});
},
//
addExchangeCode(){
this.$modalForm(inviteCodeEditForm()).then(() => this.getList(''));
},
//
cancelExchangeCode(){
this.$modalForm(inviteCodeCancelForm()).then(() => this.getList(''));
},
// -
async exports(type = 'excel'){
let _this = this;
//
let params = Object.assign({}, _this.tableFrom);
params.page = 1;
params.limit = 20;
params.is_export = 1;
let exportData = await this.exportsAllList(params);
if(type == 'excel') createWorkBook(exportData.header || {}, exportData.title || {}, exportData.list || {}, exportData.foot || '',exportData.filename || '');
else _this.downloadQrCode(exportData);
},
// -
exportsAllList(params, exportData = {}){
let _this = this;
return new Promise((resolve, reject) => {
inviteCodeList(params).then((res) => {
let data = res.data || {};
if(params.page == 1){
exportData.title = data.title || {};
exportData.header = data.header || {};
exportData.filename = data.filename || '';
}
//
let totalPage = Math.ceil(data.count / params.limit);
let exportList = exportData.list || {};
exportData.list = Object.values(exportList).concat(data.list);
if(params.page < totalPage){
//
params.page++;
_this.exportsAllList(params, exportData).then(res => {
return resolve(res)
});
}else{
//
return resolve(exportData)
}
}).catch((res) => {
console.log('错误',res)
return resolve(list)
});
})
},
//
async downloadQrCode(exportData){
let _this = this;
let list = exportData.list || {};
//
const data = [];
for (const item of list) {
const base64 = await this.textQrcodeToBase64(item[3]);
data.push({
name: item[2],
value: base64,
});
}
//
this.dataUrlZip(data, exportData.filename || 'qrcodes');
},
/**
* 将字符串生成二维码并且转成base64
* @param {要生成二维码的字符串} text
* @returns
*/
textQrcodeToBase64(text) {
return new Promise((res, rej) => {
QRCode.toDataURL(
text, //
{
type: "image/png", // dataurl
width: 200, //
quality: 0.8, //
margin: 1, //
color: {
dark: "#000", //
light: "#fff", //
},
},
(err, dataUrl)=> {
if (err) rej(err);
res(dataUrl.substring(22)); // substring(22)base64
}
);
});
},
/**
* 将base64字符串以png格式装进jszip, 然后下载保存到本地
* @param {Array} data {value: base64字符串, name: 二维码的名字}
* @param fileName
*/
dataUrlZip(data, fileName) {
const zip = new JSZip();
for (const item of data) {
const { value, name } = item;
zip.file(name + ".png", value, {
base64: true,
});
}
// file-saver
zip.generateAsync({ type: "blob" }).then( (content) =>{
FileSaver.saveAs(content, fileName + ".zip");
});
},
//
updateExchangeCode(info){
let _this = this;
_this.$prompt(`当前兑换码:${info.exchange_code}`, {
confirmButtonText: "提交修改",
cancelButtonText: "取消修改",
inputErrorMessage: "请输入新的兑换码",
inputType: "text",
inputPlaceholder: "请输入新的兑换码",
inputValidator: value => {
if (!value) return "输入不能为空";
if(value.length > 10) return "兑换码长度不能超过10个字符";
const regex = /^[A-Za-z0-9]+$/;
if (!regex.test(value)) return "请输入英文字母或者数字";
}
}).then(({value}) => {
let params = {
id: info.id,
new_exchange_code: value
};
inviteCodeUpdate(params).then(res => {
_this.$message.success(res.message)
_this.getList()
}).catch((err) => {
_this.$message.error(err.message)
});
}).catch(() => {});
}
},
};
</script>
<style scoped lang="scss">
.qr-code-img{
width: 90px!important;
height: 90px!important;
padding-top: 5px;
padding-left: 5px;
}
/deep/ .el-table .cell{
padding: 0!important;
}
.selWidth{
margin-bottom: 10px!important;
}
.user-content{
--user-content-height-: 80px;
height: var(--user-content-height-);
width: 100%;
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
.user-avatar{
height: var(--user-content-height-);
width: var(--user-content-height-);
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
img{
height: 80%!important;
width: 80%!important;
border-radius: 50% !important;
}
}
.user-info{
max-width: calc(100% - var(--user-content-height-));
height: var(--user-content-height-);
display: inline-flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: center;
align-items: flex-start;
.nickname{
width: 100%;
height: 25px;
line-height: 25px;
text-align: left;
font-size: 15px;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
.user-id{
color: #fff;
background-color: #409eff;
border-color: #409eff;
height: 18px;
line-height: 20px;
font-size: 13px;
padding: 0 5px;
border-radius: 5px;
margin-left: 10px;
width: max-content!important;
}
}
.user-id{
width: 100%;
text-align: left;
font-size: 13px;
line-height: calc(var(--user-content-height- * 35%));
}
}
}
</style>