- 在新增/修改弹窗中,添加关联用户的输入框,支持手机号/邮箱的自动补全 - 更新表单验证规则,确保关联用户为必填项 - 调整表单项的样式,统一标签宽度,提升用户体验 - 增加用户列表加载功能,优化用户选择流程
667 lines
19 KiB
Vue
667 lines
19 KiB
Vue
<template>
|
||
<el-dialog
|
||
width="1200px"
|
||
:title="`${dataForm.id ? '续费' : '开通'}VIP`"
|
||
destroy-on-close
|
||
:close-on-click-modal="false"
|
||
:visible.sync="visible"
|
||
@close="handlereset"
|
||
>
|
||
<el-form
|
||
:model="dataForm"
|
||
:rules="dataRule"
|
||
ref="dataForm"
|
||
@keyup.enter.native="dataFormSubmit()"
|
||
label-width="120px"
|
||
>
|
||
<div style="overflow: hidden;">
|
||
<el-form-item
|
||
v-if="!dataForm.id"
|
||
style="width: 50%;float: left;"
|
||
label="手机号/邮箱:"
|
||
prop="userKey"
|
||
class="form_item"
|
||
>
|
||
<div style="display: flex;align-items: center;">
|
||
<el-autocomplete
|
||
size="small"
|
||
style="width: 100%;"
|
||
v-model="dataForm.userKey"
|
||
:debounce="500"
|
||
:fetch-suggestions="loadAll"
|
||
placeholder="请输入手机号/邮箱"
|
||
@select="handleSelect"
|
||
>
|
||
<template #default="{ item }">
|
||
<div class="custom-item">
|
||
<span>{{ item.tel ? item.tel : item.email }}</span>
|
||
<span style="color: gray; margin-left: 10px;" v-if="item.name"
|
||
>({{ item.name }})</span
|
||
>
|
||
</div>
|
||
</template>
|
||
</el-autocomplete>
|
||
</div>
|
||
</el-form-item>
|
||
<!-- <el-form-item
|
||
label="账户余额"
|
||
v-if="dataForm.userKey"
|
||
style="width: 50%;float: left;"
|
||
>
|
||
天医币 <em
|
||
style="font-style: normal; font-size: 16px; color: #17B3A3;"
|
||
>{{ dataForm.peanutCoin }}</em
|
||
>
|
||
   积分 <em
|
||
style="font-style: normal; font-size: 16px; color: #17B3A3;"
|
||
>{{ dataForm.jf }}</em
|
||
>
|
||
</el-form-item> -->
|
||
</div>
|
||
|
||
<el-form-item label="VIP类型" prop="type" class="type_block">
|
||
<el-radio-group v-model="dataForm.type" size="mini">
|
||
<el-radio
|
||
style="margin-right: 10px;"
|
||
v-for="(option, index) in typeList"
|
||
:disabled="vipTypeDisabled(option.value)"
|
||
:key="index"
|
||
:label="option.value"
|
||
>
|
||
{{ option.label }}
|
||
</el-radio>
|
||
</el-radio-group>
|
||
<el-alert
|
||
v-if="showSelectVipTips"
|
||
:title="dataForm.id ? '只能续费已开通的VIP' : '已开通的VIP不允许再次开通,如需续费请使用续费功能。'"
|
||
class="select-vip-tips"
|
||
type="warning"
|
||
close-text="不再提示"
|
||
@close="closeVipSelectedTips"
|
||
show-icon
|
||
>
|
||
</el-alert>
|
||
</el-form-item>
|
||
<el-form-item label="VIP年限" prop="year" class="year_block">
|
||
<div style="height: 40px;">
|
||
<el-radio-group
|
||
v-model="dataForm.year"
|
||
size="mini"
|
||
|
||
>
|
||
<el-radio
|
||
v-for="(option, index) in yearList"
|
||
:key="index"
|
||
:label="option.value"
|
||
>
|
||
{{ option.label }}
|
||
</el-radio>
|
||
</el-radio-group>
|
||
</div >
|
||
|
||
<span class="qita"> </span>
|
||
<!-- 其他:<el-input v-model="dataForm.day"></el-input>  天 -->
|
||
</el-form-item>
|
||
<!-- <el-form-item label="预计费用" class="price_block">
|
||
<span style=" color: #f56c6c;">{{ dataForm.price }}元</span>
|
||
</el-form-item> -->
|
||
|
||
<el-form-item v-if="dataForm.type && dataForm.year" label="有效期预览">
|
||
<el-table :data="periodPreview" class="periodPreview-table" row-class-name="periodPreview-table-row" header-row-class-name="periodPreview-table-row">
|
||
<el-table-column prop="vipType" label="VIP类型" width="180"></el-table-column>
|
||
<el-table-column v-if="dataForm.id" prop="oldPeriod" label="续费前有效期"></el-table-column>
|
||
<el-table-column prop="currentPeriod" :label="`本次${dataForm.id ? '续费' : '开通'}的时长`"></el-table-column>
|
||
</el-table>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="支付方式" class="coin_block">
|
||
<div>
|
||
<el-radio-group v-model="dataForm.payType" @input="payTypeChange">
|
||
<el-radio label="微信">微信</el-radio>
|
||
<el-radio label="支付宝">支付宝</el-radio>
|
||
|
||
<el-radio label="天医币">天医币</el-radio>
|
||
<el-radio label="银行支付">银行支付</el-radio>
|
||
<el-radio label="海外支付">海外支付</el-radio>
|
||
<el-radio label="其他">其他</el-radio>
|
||
</el-radio-group>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="交易流水号" v-if="dataForm.payType == '微信'||'支付宝'">
|
||
<el-input
|
||
|
||
|
||
v-model="dataForm.orderSn"
|
||
placeholder="交易流水号"
|
||
>
|
||
</el-input>
|
||
</el-form-item>
|
||
|
||
|
||
<el-form-item label="付款时间" class="coin_block">
|
||
<div>
|
||
<el-date-picker
|
||
size="small"
|
||
v-model="dataForm.payTime"
|
||
type="datetime"
|
||
format="yyyy-MM-dd HH:mm:ss"
|
||
value-format="yyyy-MM-dd HH:mm:ss"
|
||
placeholder="选择付款时间"
|
||
|
||
>
|
||
</el-date-picker>
|
||
|
||
</div>
|
||
|
||
</el-form-item>
|
||
<el-form-item label="金额" class="coin_block">
|
||
<div>
|
||
<el-input
|
||
clearable
|
||
v-model="dataForm.fee"
|
||
placeholder="请输入需要金额"
|
||
>
|
||
</el-input>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item v-if="dataForm.payType == '天医币'" label="抵扣积分" class="coin_block">
|
||
<div>
|
||
<el-input
|
||
clearable
|
||
v-model="dataForm.jf"
|
||
placeholder="请输入需要抵扣的积分"
|
||
>
|
||
</el-input>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="备注">
|
||
<el-input
|
||
type="textarea"
|
||
rows="5"
|
||
v-model="dataForm.remark"
|
||
placeholder="备注"
|
||
>
|
||
</el-input>
|
||
</el-form-item>
|
||
</el-form>
|
||
<span slot="footer" class="dialog-footer">
|
||
<el-button @click="handlereset" size="mini">取消</el-button>
|
||
<el-button type="primary" @click="dataFormSubmit()" size="mini" >确定</el-button>
|
||
</span>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script>
|
||
import global from "../../common/common.vue"; //引入共用组间
|
||
import { parseTime } from "@/utils";
|
||
export default {
|
||
data() {
|
||
return {
|
||
baseUrl: global.baseUrl,
|
||
visible: false,
|
||
|
||
dataForm: {
|
||
id: 0,
|
||
name: "",
|
||
tel: "",
|
||
email: "",
|
||
|
||
pageType: "", //vip类型
|
||
type: "", //vip类型
|
||
year: "",
|
||
orderSn:'',
|
||
remark: "",
|
||
fee: 0, //金额
|
||
jf: 0 //积分
|
||
},
|
||
peanutCoin: 0, //天医币和积分总和判断是否为0
|
||
dataRule: {
|
||
type: [
|
||
{
|
||
required: true,
|
||
message: "类型不能为空",
|
||
trigger: "blur"
|
||
}
|
||
],
|
||
year: [
|
||
{
|
||
required: true,
|
||
message: "年限不能为空",
|
||
trigger: "blur"
|
||
}
|
||
]
|
||
},
|
||
typeList: [
|
||
{ value: "11", label: "超v" },
|
||
{ value: "12", label: "简易超v" },
|
||
{ value: "1", label: "医学超v" },
|
||
{ value: "2", label: "国学与心理学超v" },
|
||
{ value: "4", label: "中医学" },
|
||
{ value: "5", label: "针灸学" },
|
||
{ value: "6", label: "肿瘤学" },
|
||
|
||
{ value: "9", label: "中西汇通学" },
|
||
{ value: "7", label: "国学" },
|
||
{ value: "8", label: "心理学" }
|
||
],
|
||
yearList: [
|
||
{ value: "1", label: "1年" },
|
||
{ value: "3", label: "3年" },
|
||
{ value: "4", label: "4年" }
|
||
],
|
||
superVipTypeDict: {
|
||
"11": ["4", "5", "6", "9", "7", "8"],
|
||
"12": ["4", "5", "6", "9", "8"],
|
||
"1": ["4", "5", "6", "9"],
|
||
"2": ["7", "8"]
|
||
},
|
||
hasVipList: [],
|
||
hasVipMaps: {}, // 初始化为空对象
|
||
vipTypeSet: new Set(), // 添加 vipTypeSet
|
||
isShowSelectVipTips_open: !!localStorage.getItem("closeVipSelectedTips_open"),
|
||
isShowSelectVipTips_renewal: !!localStorage.getItem("closeVipSelectedTips_renewal"),
|
||
};
|
||
},
|
||
computed: {
|
||
periodPreview() {
|
||
let types = [], result = []
|
||
if (this.superVipTypeDict[this.dataForm.type]) {
|
||
types = this.superVipTypeDict[this.dataForm.type]
|
||
} else {
|
||
types = [this.dataForm.type]
|
||
}
|
||
|
||
// 获取当前时间
|
||
const now = new Date();
|
||
// 计算本次开通的时长
|
||
const years = parseInt(this.dataForm.year) || 0;
|
||
|
||
types.forEach(item => {
|
||
const vipData = this.hasVipMaps[item];
|
||
// 确定开始日期
|
||
let startDate;
|
||
if (vipData && vipData.endTime) {
|
||
const endTime = new Date(vipData.endTime);
|
||
if (endTime > now) {
|
||
// 如果原有效期结束时间大于当前时间,开始日期为原结束时间
|
||
startDate = new Date(endTime);
|
||
startDate.setDate(startDate.getDate());
|
||
} else {
|
||
// 否则使用当前时间
|
||
startDate = new Date(now);
|
||
}
|
||
} else {
|
||
// 没有原有效期信息,使用当前时间
|
||
startDate = new Date(now);
|
||
}
|
||
|
||
// 计算结束日期
|
||
const endDate = new Date(startDate);
|
||
endDate.setFullYear(endDate.getFullYear() + years);
|
||
// 格式化日期
|
||
const formattedStartDate = parseTime(startDate, '{y}-{m}-{d}');
|
||
const formattedEndDate = parseTime(endDate, '{y}-{m}-{d}');
|
||
|
||
result.push({
|
||
vipType: this.typeList.find(e => e.value === item).label || item,
|
||
currentPeriod: `${formattedStartDate} ~ ${formattedEndDate}`,
|
||
oldPeriod: vipData ? (parseTime(vipData.startTime, '{y}-{m}-{d}') + " ~ " + parseTime(vipData.endTime, '{y}-{m}-{d}')) : '无'
|
||
});
|
||
});
|
||
|
||
return result;
|
||
},
|
||
showSelectVipTips() {
|
||
if (this.dataForm.id) {
|
||
return !this.isShowSelectVipTips_renewal
|
||
} else {
|
||
return !this.isShowSelectVipTips_open
|
||
}
|
||
}
|
||
},
|
||
methods: {
|
||
init(data) {
|
||
this.dataForm = {
|
||
id: 0,
|
||
name: "",
|
||
tel: "",
|
||
email: "",
|
||
|
||
pageType: "", //vip类型
|
||
type: "", //vip类型
|
||
year: "",
|
||
orderSn:'',
|
||
remark: "",
|
||
fee: 0, //金额
|
||
jf: 0 //积分
|
||
}
|
||
this.visible = true;
|
||
this.$http({
|
||
url: this.$http.adornUrl("/master/userVip/getUserVipInfoByUserId"),
|
||
method: "post",
|
||
data: this.$http.adornData({
|
||
userId: data.id
|
||
})
|
||
}).then(({ data }) => {
|
||
if (data && data.code === 0) {
|
||
this.dataForm = data.userVipInfo;
|
||
|
||
} else {
|
||
cb([]);
|
||
}
|
||
});
|
||
// this.dataForm.userKey = data.tel || data.email;
|
||
// this.dataForm.userId = data.id;
|
||
// this.dataForm.name = data.name;
|
||
// this.dataForm.tel = data.tel;
|
||
// this.dataForm.email = data.email;
|
||
// this.dataForm.peanutCoin = data.peanutCoin;
|
||
// this.dataForm.jf = data.jf;
|
||
},
|
||
renewalInit(userId, data) {
|
||
this.dataForm = {
|
||
id: userId,
|
||
name: "",
|
||
tel: "",
|
||
email: "",
|
||
userId: userId,
|
||
pageType: "", //vip类型
|
||
type: "", //vip类型
|
||
year: "",
|
||
orderSn:'',
|
||
remark: "",
|
||
fee: 0, //金额
|
||
jf: 0 //积分
|
||
}
|
||
this.visible = true;
|
||
this.$http({
|
||
url: this.$http.adornUrl("/master/userVip/getUserVipInfoByUserId"),
|
||
method: "post",
|
||
data: this.$http.adornData({
|
||
state: "",
|
||
userId,
|
||
})
|
||
}).then(({ data }) => {
|
||
if (data && data.code === 0) {
|
||
this.hasVipList = data.userVips || [];
|
||
// 在这里生成 hasVipMaps 和 vipTypeSet
|
||
this.generateVipMaps();
|
||
}
|
||
});
|
||
},
|
||
generateVipMaps() {
|
||
const hasVipMaps = {};
|
||
const vipTypeSet = new Set();
|
||
|
||
// 确保 this.hasVipList 是数组
|
||
(this.hasVipList || []).forEach(vip => {
|
||
const type = vip.type.toString();
|
||
vipTypeSet.add(type);
|
||
if (!hasVipMaps[type]) {
|
||
hasVipMaps[type] = vip;
|
||
}
|
||
});
|
||
|
||
this.hasVipMaps = hasVipMaps;
|
||
this.vipTypeSet = vipTypeSet;
|
||
},
|
||
vipTypeDisabled(type) {
|
||
let typeArr = [];
|
||
if (type === "11" || type === "12" || type === "1" || type === "2") {
|
||
typeArr = this.superVipTypeDict[type];
|
||
} else {
|
||
typeArr = [type];
|
||
}
|
||
|
||
// 如果是新开通(没有id)
|
||
if (!this.dataForm.id) {
|
||
// 如果用户已经拥有该类型(或超V包含的任何一个类型),则禁用
|
||
return typeArr.some(type => this.vipTypeSet.has(type));
|
||
}
|
||
// 如果是续费(有id)
|
||
else {
|
||
// 如果用户缺少该类型(或超V包含的任何一个类型),则禁用
|
||
return typeArr.some(type => !this.vipTypeSet.has(type));
|
||
}
|
||
},
|
||
handleSelect(data) {
|
||
console.log("data at line 161:", data);
|
||
this.dataForm.userKey = data.tel || data.email;
|
||
this.dataForm.userId = data.id;
|
||
this.dataForm.name = data.name;
|
||
this.dataForm.tel = data.tel;
|
||
this.dataForm.email = data.email;
|
||
|
||
// 更改用户后清空已选vip类型
|
||
this.dataForm.type = "";
|
||
|
||
// 赋值当前用户已经拥有的vip,用于禁用已开通过的vip类型
|
||
this.hasVipList = data.userVips;
|
||
this.generateVipMaps();
|
||
},
|
||
loadAll(queryString, cb) {
|
||
this.dataForm.userId = "";
|
||
this.dataForm.userName = "";
|
||
if (queryString == "") {
|
||
return false;
|
||
}
|
||
this.$http({
|
||
// url: this.$http.adornUrl('/book/user/list'),
|
||
url: this.$http.adornUrl("/book/user/getUserList"),
|
||
method: "post",
|
||
data: this.$http.adornData({
|
||
page: 1,
|
||
limit: 20,
|
||
key: queryString
|
||
})
|
||
}).then(({ data }) => {
|
||
if (data && data.code === 0) {
|
||
var arr = data.user.records;
|
||
console.log("arr at line 467:", arr);
|
||
cb(arr);
|
||
} else {
|
||
cb([]);
|
||
}
|
||
});
|
||
},
|
||
payTypeChange(e){
|
||
document.activeElement && document.activeElement.blur();
|
||
|
||
if(this.dataForm.payType){
|
||
delete this.dataForm.orderSn;
|
||
|
||
}
|
||
console.log('e at line 261:', e)
|
||
if (this.dataForm.payType == "海外支付") {
|
||
this.dataForm.fee=0;
|
||
this.dataForm.jf=0;
|
||
}
|
||
},
|
||
|
||
//表单提交
|
||
dataFormSubmit() {
|
||
this.$refs["dataForm"].validate(valid => {
|
||
if (valid) {
|
||
if (!this.dataForm.userId) {
|
||
this.$message.error("请选择用户");
|
||
return;
|
||
}
|
||
if (!this.dataForm.payType) {
|
||
this.$message.error("请输入支付方式");
|
||
return;
|
||
}
|
||
// if (this.dataForm.jf == 0 && this.dataForm.fee == 0) {
|
||
// this.$message.error("请重新填写金额或者积分,不能都为0");
|
||
// return;
|
||
// }
|
||
if (this.dataForm.payType == "海外支付") {
|
||
this.dataForm.fee=0;
|
||
this.dataForm.jf=0;
|
||
}
|
||
if (this.dataForm.payType == "其他") {
|
||
if (!this.dataForm.remark) {
|
||
this.$message.error("请输入备注");
|
||
return;
|
||
}
|
||
}
|
||
if ((this.dataForm.payType == "微信" || this.dataForm.payType == "支付宝") && !this.dataForm.orderSn) {
|
||
this.$message.error("请输入交易流水号");
|
||
return;
|
||
}
|
||
if (this.dataForm.payTime == "" || this.dataForm.payTime == null) {
|
||
this.$message.error("请选择付款时间");
|
||
return;
|
||
}
|
||
var data = {
|
||
...this.dataForm,
|
||
|
||
jf: this.dataForm.payType === "天医币" ? this.dataForm.jf || 0 : 0,
|
||
fee: this.dataForm.fee || 0,
|
||
orderSn: this.dataForm.orderSn || '',
|
||
};
|
||
|
||
data.adminId = 1;
|
||
console.log("...this.dataForm at line 273:", this.dataForm);
|
||
|
||
const submitHttpUrl = this.dataForm.id ? "/master/userVip/renewUserVipByAdmin" : "/master/userVip/addUserVipByAdmin";
|
||
this.$http({
|
||
url: this.$http.adornUrl(submitHttpUrl),
|
||
method: "post",
|
||
data: this.$http.adornData({
|
||
...data
|
||
})
|
||
}).then(({ data }) => {
|
||
if (data && data.code === 0) {
|
||
this.$message({
|
||
message: "操作成功",
|
||
type: "success",
|
||
duration: 1500,
|
||
onClose: () => {
|
||
this.visible = false;
|
||
this.$emit("refreshDataList");
|
||
}
|
||
});
|
||
} else {
|
||
this.$message.error(data.msg);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
},
|
||
//取消
|
||
handlereset() {
|
||
this.visible = false;
|
||
|
||
// 重置 VIP 相关状态
|
||
this.hasVipList = [];
|
||
this.hasVipMaps = {};
|
||
this.vipTypeSet = new Set();
|
||
|
||
// 重置表单数据
|
||
this.dataForm = {
|
||
id: 0,
|
||
name: "",
|
||
tel: "",
|
||
email: "",
|
||
userId: "",
|
||
pageType: "",
|
||
type: "",
|
||
year: "",
|
||
orderSn: "",
|
||
remark: "",
|
||
fee: 0,
|
||
jf: 0
|
||
};
|
||
|
||
// 重置表单验证
|
||
if (this.$refs.dataForm) {
|
||
this.$refs.dataForm.resetFields();
|
||
}
|
||
},
|
||
closeVipSelectedTips() {
|
||
if (this.dataForm.id) {
|
||
localStorage.setItem("closeVipSelectedTips_renewal", "true");
|
||
this.isShowSelectVipTips_renewal = true;
|
||
} else {
|
||
localStorage.setItem("closeVipSelectedTips_open", "true");
|
||
this.isShowSelectVipTips_open = true;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.year_block {
|
||
/deep/.el-form-item__content {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
/deep/.el-radio {
|
||
margin-left: 0;
|
||
margin-right: 0;
|
||
}
|
||
/deep/.el-radio__label {
|
||
padding-left: 3px;
|
||
padding-right: 30px;
|
||
}
|
||
|
||
.qita {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 12px;
|
||
|
||
/deep/.el-input {
|
||
width: 60px;
|
||
.el-input__inner {
|
||
height: 30px;
|
||
line-height: 30px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.price_block {
|
||
/deep/.el-form-item__content {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
/deep/.el-checkbox-group {
|
||
margin-left: 30px;
|
||
}
|
||
/deep/.el-checkbox {
|
||
padding-top: 5px;
|
||
margin-left: 20px;
|
||
margin-right: 10px;
|
||
}
|
||
/deep/.el-input {
|
||
width: 130px;
|
||
}
|
||
}
|
||
.type_block {
|
||
/deep/.el-radio {
|
||
margin-left: 0;
|
||
margin-right: 0;
|
||
}
|
||
/deep/.el-radio__label {
|
||
padding-left: 3px;
|
||
padding-right: 10px;
|
||
}
|
||
}
|
||
.periodPreview-table {
|
||
/deep/ tr,
|
||
/deep/ th {
|
||
background-color: #F8FAFF;
|
||
}
|
||
/deep/ .periodPreview-table-row .cell {
|
||
padding: 0 20px !important;
|
||
}
|
||
}
|
||
.select-vip-tips {
|
||
line-height: 1;
|
||
/deep/.el-alert__content {
|
||
height: 16px;
|
||
}
|
||
}
|
||
</style>
|
||
|