Files
nuttyreading-master-html/src/views/modules/vipList/user-open-vip.vue
chenghuan 30237639c7 feat(医疗模块): 更新新增/修改弹窗表单,添加关联用户功能
- 在新增/修改弹窗中,添加关联用户的输入框,支持手机号/邮箱的自动补全
- 更新表单验证规则,确保关联用户为必填项
- 调整表单项的样式,统一标签宽度,提升用户体验
- 增加用户列表加载功能,优化用户选择流程
2026-03-23 16:02:59 +08:00

667 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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;"
>
天医币&nbsp<em
style="font-style: normal; font-size: 16px; color: #17B3A3;"
>{{ dataForm.peanutCoin }}</em
>
&nbsp &nbsp积分&nbsp<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>&nbsp&nbsp天 -->
</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>