Browse Source

feat(会员优惠券): 添加多选会员组件并支持批量发放优惠券

- 新增MemberMultiSelect组件实现会员多选功能
- 修改优惠券发放逻辑支持批量操作
- 优化重置查询表单时的参数初始化
- 格式化SQL查询语句提高可读性
master
前端-胡立永 2 weeks ago
parent
commit
da6d96f29c
4 changed files with 229 additions and 14 deletions
  1. +131
    -0
      CatmDogd-Mall-Front-test/src/views/components/MemberMultiSelect.vue
  2. +62
    -13
      CatmDogd-Mall-Front-test/src/views/marketing/wechatMemberCoupon/index.vue
  3. +33
    -0
      CatmDogd-Mall-Front-test/src/views/model/AppUsers/index.vue
  4. +3
    -1
      ruoyi-catdog/src/main/resources/mapper/model/AppUsersMapper.xml

+ 131
- 0
CatmDogd-Mall-Front-test/src/views/components/MemberMultiSelect.vue View File

@ -0,0 +1,131 @@
<template>
<div>
<el-select
v-model="selectedMembers"
multiple
filterable
remote
reserve-keyword
clearable
default-first-option
placeholder="请输入手机号或姓名搜索会员"
:remote-method="remoteMethod"
:loading="loading"
@change="handleChange"
@focus="handleFocus"
style="width: 100%"
>
<el-option
v-for="member in memberOptions"
:key="member.id"
:label="`${member.nickname}(${member.phoneHidden})`"
:value="member.phoneHidden"
>
<span style="float: left">{{ member.nickname }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ member.phoneHidden }}</span>
</el-option>
</el-select>
<div v-if="selectedMembers.length > 0" style="margin-top: 10px">
<el-tag
v-for="phone in selectedMembers"
:key="phone"
closable
@close="removeTag(phone)"
style="margin-right: 5px; margin-bottom: 5px"
>
{{ getMemberDisplayName(phone) }}
</el-tag>
</div>
</div>
</template>
<script>
import { listUmsMemberName } from "@/api/ums/member";
export default {
name: "MemberMultiSelect",
props: {
value: {
type: Array,
default: () => []
}
},
data() {
return {
loading: false,
memberOptions: [],
allMembers: [], //
selectedMembers: []
};
},
watch: {
value: {
immediate: true,
handler(newVal) {
this.selectedMembers = newVal || [];
}
}
},
methods: {
remoteMethod(query) {
if (query !== '') {
//
const filteredMembers = this.allMembers.filter(member => {
const nickname = member.nickname || '';
const phone = member.phoneHidden || '';
const queryLower = query.toLowerCase();
return nickname.toLowerCase().includes(queryLower) ||
phone.toLowerCase().includes(queryLower);
});
this.memberOptions = filteredMembers;
} else {
//
this.memberOptions = this.allMembers;
}
},
handleChange(value) {
this.selectedMembers = value;
this.$emit('input', value);
this.$emit('change', value);
},
handleFocus() {
//
if (this.memberOptions.length === 0) {
this.memberOptions = this.allMembers;
}
},
removeTag(phone) {
const index = this.selectedMembers.indexOf(phone);
if (index > -1) {
this.selectedMembers.splice(index, 1);
this.handleChange(this.selectedMembers);
}
},
getMemberDisplayName(phone) {
const member = this.allMembers.find(m => m.phoneHidden === phone);
return member ? `${member.nickname}(${member.phoneHidden})` : phone;
},
loadAllMembers() {
this.loading = true;
listUmsMemberName().then(response => {
this.allMembers = response.data || [];
this.memberOptions = this.allMembers;
this.loading = false;
}).catch(error => {
console.error('加载会员数据失败:', error);
this.loading = false;
});
}
},
created() {
//
this.loadAllMembers();
}
};
</script>
<style scoped>
.el-select {
width: 100%;
}
</style>

+ 62
- 13
CatmDogd-Mall-Front-test/src/views/marketing/wechatMemberCoupon/index.vue View File

@ -311,8 +311,8 @@
<el-form-item label="优惠券" prop="stockId"> <el-form-item label="优惠券" prop="stockId">
<CouponSelect v-model="form2.stockId" @change="onCouponChange"></CouponSelect> <CouponSelect v-model="form2.stockId" @change="onCouponChange"></CouponSelect>
</el-form-item> </el-form-item>
<el-form-item label="会员" prop="phone">
<MemberSelect v-model="form2.phone" @change="onMemberChange"></MemberSelect>
<el-form-item label="会员" prop="phones">
<MemberMultiSelect v-model="form2.phones" @change="onMemberChange"></MemberMultiSelect>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
@ -327,11 +327,12 @@
import { listWechatMemberCoupon, getWechatMemberCoupon, delWechatMemberCoupon, addWechatMemberCoupon, updateWechatMemberCoupon, exportWechatMemberCoupon, sendCoupon } from "@/api/marketing/wechatMemberCoupon"; import { listWechatMemberCoupon, getWechatMemberCoupon, delWechatMemberCoupon, addWechatMemberCoupon, updateWechatMemberCoupon, exportWechatMemberCoupon, sendCoupon } from "@/api/marketing/wechatMemberCoupon";
import CouponSelect from "@/views/components/CouponSelector/index.vue"; import CouponSelect from "@/views/components/CouponSelector/index.vue";
import MemberSelect from "@/views/components/MemberSelect.vue"; import MemberSelect from "@/views/components/MemberSelect.vue";
import MemberMultiSelect from "@/views/components/MemberMultiSelect.vue";
export default { export default {
name: "WechatMemberCoupon", name: "WechatMemberCoupon",
components: { components: {
CouponSelect, MemberSelect
CouponSelect, MemberSelect, MemberMultiSelect
}, },
data() { data() {
return { return {
@ -379,7 +380,10 @@ export default {
// //
form: {}, form: {},
// //
form2: {},
form2: {
stockId: null,
phones: []
},
// //
rules: { rules: {
stockId: [ stockId: [
@ -409,8 +413,8 @@ export default {
openid: [ openid: [
{ required: true, message: "小程序openid不能为空", trigger: "blur" } { required: true, message: "小程序openid不能为空", trigger: "blur" }
], ],
phone: [
{ required: true, message: "手机号不能为空", trigger: "blur" }
phones: [
{ required: true, message: "请选择会员", trigger: "change" }
], ],
}, },
columns: [ columns: [
@ -472,7 +476,12 @@ export default {
updateTime: null, updateTime: null,
updateBy: null updateBy: null
}; };
this.form2 = {
stockId: null,
phones: []
};
this.resetForm("form"); this.resetForm("form");
this.resetForm("form2");
}, },
/** 搜索按钮操作 */ /** 搜索按钮操作 */
handleQuery() { handleQuery() {
@ -482,6 +491,25 @@ export default {
/** 重置按钮操作 */ /** 重置按钮操作 */
resetQuery() { resetQuery() {
this.resetForm("queryForm"); this.resetForm("queryForm");
this.queryParams = {
pageNum: 1,
pageSize: 10,
orderByColumn: "create_time",
isAsc: "desc",
stockId: null,
stockName: null,
stockType: null,
couponCode: null,
availableStartTime: null,
expireTime: null,
receiveTime: null,
sendRequestNo: null,
useRequestNo: null,
useTime: null,
couponState: null,
openid: null,
phone: null
};
this.handleQuery(); this.handleQuery();
}, },
// //
@ -506,7 +534,7 @@ export default {
this.form2.stockId = value this.form2.stockId = value
}, },
onMemberChange(value){ onMemberChange(value){
this.form2.phone = value
this.form2.phones = value
}, },
/** 修改按钮操作 */ /** 修改按钮操作 */
handleUpdate(row) { handleUpdate(row) {
@ -541,13 +569,34 @@ export default {
/** send按钮 */ /** send按钮 */
sendForm() { sendForm() {
this.$refs["form2"].validate(valid => { this.$refs["form2"].validate(valid => {
console.log(this.form2);
sendCoupon(this.form2).then(response => {
this.$modal.msgSuccess("修改成功");
this.open2 = false;
this.getList();
if (valid) {
if (!this.form2.phones || this.form2.phones.length === 0) {
this.$modal.msgError("请选择至少一个会员");
return;
}
//
const promises = this.form2.phones.map(phone => {
return sendCoupon({
stockId: this.form2.stockId,
phone: phone
}); });
})
});
Promise.all(promises).then(responses => {
this.$modal.msgSuccess(`成功为 ${this.form2.phones.length} 个会员发放优惠券`);
this.open2 = false;
this.form2 = {
stockId: null,
phones: []
};
this.getList();
}).catch(error => {
this.$modal.msgError("发放失败,请重试");
console.error(error);
});
}
});
}, },
/** 删除按钮操作 */ /** 删除按钮操作 */
handleDelete(row) { handleDelete(row) {


+ 33
- 0
CatmDogd-Mall-Front-test/src/views/model/AppUsers/index.vue View File

@ -77,6 +77,15 @@
@keyup.enter.native="handleQuery" @keyup.enter.native="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="邀请码" prop="invitationCode">
<el-input
v-model="queryParams.invitationCode"
placeholder="请输入邀请码"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<!-- <el-form-item label="只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段" prop="unionid">--> <!-- <el-form-item label="只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段" prop="unionid">-->
<!-- <el-input--> <!-- <el-input-->
<!-- v-model="queryParams.unionid"--> <!-- v-model="queryParams.unionid"-->
@ -543,6 +552,30 @@ export default {
/** 重置按钮操作 */ /** 重置按钮操作 */
resetQuery() { resetQuery() {
this.resetForm("queryForm"); this.resetForm("queryForm");
this.queryParams = {
pageNum: 1,
pageSize: 10,
updatorBy: null,
userImage: null,
userName: null,
userState: null,
userTelephone: null,
unionid: null,
openid: null,
accessToken: null,
expiresIn: null,
refreshToken: null,
expireTime: null,
userHh: null,
userBcs: null,
userBcsRole: null,
price: null,
baoPrice: null,
invitationCode: null,
userHhRole: null,
money: null,
hhStartTime: null
};
this.handleQuery(); this.handleQuery();
}, },
// //


+ 3
- 1
ruoyi-catdog/src/main/resources/mapper/model/AppUsersMapper.xml View File

@ -34,7 +34,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap> </resultMap>
<sql id="selectAppUsersVo"> <sql id="selectAppUsersVo">
select user_id, create_by, create_time, del_flag, remark, update_time, updator_by, user_image, user_name, user_state, user_telephone, unionid, openid, access_token, expires_in, refresh_token, expire_time, user_hh, user_bcs, price, invitation_code, bao_price, user_bcs_role,user_hh_role, money, hh_start_time from app_users
select user_id, create_by, create_time, del_flag, remark, update_time, updator_by, user_image, user_name, user_state,
user_telephone, unionid, openid, access_token, expires_in, refresh_token, expire_time, user_hh, user_bcs, price,
invitation_code, bao_price, user_bcs_role,user_hh_role, money, hh_start_time from app_users
</sql> </sql>
<select id="selectAppUsersList" parameterType="AppUsers" resultMap="AppUsersResult"> <select id="selectAppUsersList" parameterType="AppUsers" resultMap="AppUsersResult">


Loading…
Cancel
Save