|
|
<script setup> import { onMounted, reactive, ref } from 'vue' import { ElIcon, ElMessage, ElMessageBox } from 'element-plus' import { Plus, WarnTriangleFilled } from '@element-plus/icons-vue' import axios from 'axios' import API from '@/util/http.js' import moment from 'moment' import Cookies from 'js-cookie'; // 定义 fixedAdminId
// const fixedAdminId = 1;
// 精网号去空格
const trimJwCode = () => { if (recharge.value.jwcode) { recharge.value.jwcode = recharge.value.jwcode.replace(/\s/g, ''); } } // 上传图片前的验证函数
const beforeAvatarUpload = (file) => { const validTypes = ['image/jpeg', 'image/png']; const isImage = validTypes.includes(file.type); const isLt1M = file.size / 1024 / 1024 < 1;
if (!isImage) { ElMessage.error('只能上传 JPG/PNG 图片!'); return false; } if (!isLt1M) { ElMessage.error('图片大小不能超过 1MB!'); return false; } return true; }; // 这是添加上传图片的接口
const imageUrl = ref('') const voucher = ref('') // const rateName = ref()
const adminData = ref({}) // 获取管理员信息
const getAdminData = async function () { try { const result = await API({ url: '/admin/userinfo', data: {} }) adminData.value = result recharge.value.adminId = adminData.value.id recharge.value.market = adminData.value.market console.log('请求成功', result) console.log('用户信息', user.value) } catch (error) { console.log('请求失败', error) } } //提交禁止重复点击
const addDisabled = ref(false) // 这是添加充值信息的表单
const recharge = ref({ jwcode: '', // jwcode 字段
activity: '', // activity 字段
voucher: '', rechargeWay: '客服充值', freeGold: "", money: null, permanentGold: "", rateName: null, rateId: null, payModel: '', // payModel 字段
payTime: null, // payTime 字段
remark: '', // remark 字段
rechargeRatio: '' }) // 用来写的 cookie 的 key
const WriteCookies = ref(null) // 用来写的 cookie 的 value
const WriteCookiesTime = ref(null) // 用来读的 cookie 的 key
const ReadCookies = ref(null) // 用来读的 cookie 的 value
const ReadCookiesTime = ref(null) // 这是添加充值信息的接口
const add = async function () { try {
const formattedRecharge = { ...recharge.value }
// 将永久金币数、免费金币数和充值金额数乘以 100
if (formattedRecharge.permanentGold) { formattedRecharge.permanentGold = Number(formattedRecharge.permanentGold) * 100; }
if (formattedRecharge.freeGold) { formattedRecharge.freeGold = Number(formattedRecharge.freeGold) * 100; } if (formattedRecharge.money) { formattedRecharge.money = Number(formattedRecharge.money) * 100; }
if (formattedRecharge.payTime) { // 使用 moment.js 格式化 payTime
formattedRecharge.payTime = moment(formattedRecharge.payTime).format('YYYY-MM-DD HH:mm:ss') }
console.log('开始添加充值信息', recharge.value) //存一下 用户的jwcode
// 拼接 jwcode:permanentGold:freeGold
WriteCookies.value = `coinRecharge:${recharge.value.jwcode}:${recharge.value.permanentGold}:${recharge.value.freeGold}` //value 为充值时间
WriteCookiesTime.value = recharge.value.payTime // 设置cookies,用户jwcode为key,value也是jwcode,过期时间为1天
Cookies.set(WriteCookies.value, WriteCookiesTime.value, { expires: 1, path: '/' }); // 发送POST请求
addDisabled.value = true const result = await API({ url: '/recharge/add', data: formattedRecharge }) addDisabled.value = false if (result.code === 0) { ElMessage.error(result.msg) return } // 将响应结果存储到响应式数据中
console.log('请求成功', result) // 显示成功消息
ElMessage.success('添加成功') // 重置表单
deleteRecharge() user.value = {} } catch (error) { console.log('请求失败', error) // 在这里可以处理错误逻辑,比如显示错误提示等
} }
// 二次校验充值对话框显示状态
const RechargeDialogVisible = ref(false); // 第一次弹窗
const FirstRechargeDialogVisible = ref(false);
// 关闭第一次对话框
const FirstRechargeDialogVisiblehandleClose = () => { FirstRechargeDialogVisible.value = false; // 重置表单数据
deleteRecharge() user.value = {} };
// 关闭第二次对话框
const RechargeDialogVisiblehandleClose = () => { RechargeDialogVisible.value = false; // 重置表单数据
deleteRecharge() user.value = {} };
// 确认使用cookie继续充值
const RechargeDialogVisibleContinue = () => { RechargeDialogVisible.value = false; add(); };
const RechargeDialogVisibleCancel = () => { RechargeDialogVisible.value = false deleteRecharge() user.value = {} };
// 第一次实际执行充值操作
// const proceedWithRecharge = () => {
// FirstRechargeDialogVisible.value = false
// add()
// };
const FistRechargeDialogVisibleContinue = () => { FirstRechargeDialogVisible.value = false add() }; // 第一次取消
const FirstRechargeDialogVisibleCancel = () => { FirstRechargeDialogVisible.value = false deleteRecharge() user.value = {} };
// 添加充值信息前的按钮点击事件,进行表单验证和用户确认操作
const addBefore = () => { // 为未输入的金币字段设置默认值
if (!recharge.value.permanentGold) { recharge.value.permanentGold = '0'; } if (!recharge.value.freeGold) { recharge.value.freeGold = '0'; }
// 表单验证
Ref.value.validate(async (valid) => { if (!valid) { ElMessage({ type: 'error', message: '请检查输入内容' }); return; }
// 验证金币不能同时为0
if (Number(recharge.value.permanentGold) === 0 && Number(recharge.value.freeGold) === 0) { ElMessage({ type: 'error', message: '永久金币和免费金币不能同时为0' }); return; }
// 验证币种选择
if (!recharge.value.rateName) { ElMessage({ type: 'error', message: '请选择币种' }); return; }
// 验证充值金额
if (!recharge.value.money) { ElMessage({ type: 'error', message: '请输入充值金额' }); return; }
// 设置对应的rateId
const selectedRate = rateName.find(item => item.value === recharge.value.rateName); if (selectedRate) { recharge.value.rateId = selectedRate.rateId; }
// 检查是否有用户信息
if (!user.value.jwcode) { ElMessage.warning('请先查询用户信息') return }
// 检查cookie
// 拼接 jwcode:permanentGold:freeGold
ReadCookies.value = `coinRecharge:${recharge.value.jwcode}:${recharge.value.permanentGold}:${recharge.value.freeGold}`
// 获取cookie
const cookie = Cookies.get(ReadCookies.value) console.log("time", WriteCookiesTime.value) // 格式化时间
ReadCookiesTime.value = moment(cookie).format('YYYY-MM-DD HH:mm:ss') console.log('cookie', cookie) // 如果存在cookie,显示确认对话框;否则直接进入充值确认
if (cookie) { RechargeDialogVisible.value = true; } else { FirstRechargeDialogVisible.value = true; } }); };
// 表单验证
// 开始时间改变时,重新验证结束时间
const Ref = ref(null) const rules = reactive({ jwcode: [{ required: true, validator: (rule, value, callback) => { if (!value) { callback(new Error('精网号不能为空')); return; } if (/[^0-9]/.test(value)) { callback(new Error('精网号只能包含数字')); return; } callback(); }, trigger: 'blur' }], activity: [{ required: true, message: '请输入活动名称', trigger: 'blur' }], permanentGold: [ { required: true, message: '请输入永久金币数', trigger: 'change' }, { validator: (rule, value, callback) => { if (!value) { value = '0' } // 检查是否包含特殊符号
if (/[^0-9.]/.test(value)) { callback(new Error('不能包含特殊符号或负数')); return; }
// 检查整数位数
const integerPart = value.split('.')[0]; if (integerPart.length > 6) { callback(new Error('整数位数不能超过6位')); return; }
// 检查小数位数
if (value.includes('.')) { const decimalPart = value.split('.')[1]; if (decimalPart.length > 2) { callback(new Error('小数位数不能超过两位')); return; } }
const numValue = Number(value); if (isNaN(numValue)) { callback(new Error('请输入有效的数字')); } else if (numValue < 0) { callback(new Error('输入金额不能小于0')); } else { callback(); } }, trigger: 'blur' } ], freeGold: [ { required: true, message: '请输入免费金币数', trigger: 'change' }, { validator: (rule, value, callback) => { if (!value) { value = '0' } // 检查是否包含特殊符号
if (/[^0-9.]/.test(value)) { callback(new Error('不能包含特殊符号或负数')); return; }
// 检查整数位数
const integerPart = value.split('.')[0]; if (integerPart.length > 6) { callback(new Error('整数位数不能超过6位')); return; }
// 检查小数位数
if (value.includes('.')) { const decimalPart = value.split('.')[1]; if (decimalPart.length > 2) { callback(new Error('小数位数不能超过两位')); return; } }
const numValue = Number(value); if (isNaN(numValue)) { callback(new Error('请输入有效的数字')); } else if (numValue < 0) { callback(new Error('输入金额不能小于0')); } else { callback(); } }, trigger: 'blur' } ], rateName: [{ required: true, message: '请选择货币名称', trigger: 'blur' }],
money: [ { required: true, message: '请输入充值金额', trigger: 'blur' }, { validator: (rule, value, callback) => { // 检查是否包含特殊符号
if (/[^0-9.]/.test(value)) { callback(new Error('不能包含特殊符号或负数')); return; }
// 检查整数位数
const integerPart = value.split('.')[0]; if (integerPart.length > 6) { callback(new Error('整数位数不能超过6位')); return; }
// 检查小数位数
if (value.includes('.')) { const decimalPart = value.split('.')[1]; if (decimalPart.length > 2) { callback(new Error('小数位数不能超过两位')); return; } }
const numValue = Number(value); if (isNaN(numValue)) { callback(new Error('请输入有效的数字')); } else if (numValue < 0) { callback(new Error('输入金额不能小于0')); } else { callback(); } }, trigger: 'blur' } ], payModel: [{ required: true, message: '请选择付款方式', trigger: 'blur' }], payTime: [{ required: true, message: '请选择交款时间', trigger: 'blur' }] });
// 查找客户信息的方法
const user = ref({}) const getUser = async function (jwcode) { trimJwCode(); // 验证精网号
if (!jwcode) { ElMessage.warning('精网号不能为空'); return; }
// 验证精网号是否为数字
if (!/^\d{1,9}$/.test(jwcode)) { ElMessage.warning('精网号必须为数字且不超过九位'); deleteRecharge() return; }
try { const result = await API({ url: '/user/selectUser', data: { jwcode: recharge.value.jwcode } })
if (result.code === 0) { ElMessage.error(result.msg); } else if (result.data === null) { ElMessage.error("用户不存在"); } else { user.value = result.data; console.log("用户信息", user.value); ElMessage.success("查询成功"); } } catch (error) { console.log("请求失败", error); ElMessage.error("精网号错误"); } }
// 这是查询活动的接口,一期没有调用这个接口
const activity = ref([]) // const getActivity = async function () {
// try {
// // 发送POST请求
// const result = await API({
// url: '/general/activity',
// data: {
// }
// })
// // 将响应结果存储到响应式数据中
// console.log('请求成功', result)
// // 存储表格数据
// activity.value = result.data
// console.log('活动信息', activity.value)
// } catch (error) {
// console.log('activity请求失败', error)
// // 在这里可以处理错误逻辑,比如显示错误提示等
// }
// }
//货币条目
const rateName = [ { value: 'USD', label: 'USD', rateId: 1 }, { value: 'HKD', label: 'HKD', rateId: 2 }, { value: 'THB', label: 'THB', rateId: 3 }, { value: 'VND', label: 'VND', rateId: 4 }, { value: 'CAD', label: 'CAD', rateId: 5 }, { value: 'MYR', label: 'MYR', rateId: 6 }, { value: 'KRW', label: 'KRW', rateId: 7 }, { value: 'JPY', label: 'JPY', rateId: 8 }, { value: 'CNY', label: 'CNY', rateId: 9 } ]
// 修改上传处理
const customUpload = async (options) => { try { const formData = new FormData(); formData.append('file', options.file);
const response = await axios.post(import.meta.env.VITE_UPLOAD_URL, formData, { headers: { 'Content-Type': 'multipart/form-data', 'Authorization': `Bearer ${localStorage.getItem('token')}` } });
if (response.data.code === 200 && response.data.data) { // 传递原始文件对象和响应数据
handleAvatarSuccess(response.data, options.file); ElMessage.success('上传成功'); } else { ElMessage.error(response.data.msg || '上传失败'); } } catch (error) { console.error('上传错误:', error); ElMessage.error(`上传失败: ${error.response?.data?.message || error.message}`); } };
// 获取环境变量
// const uploadUrl = import.meta.env.VITE_UPLOAD_URL;
// 上传图片成功的回调函数
const handleAvatarSuccess = (response, file) => { // 直接使用 file 对象创建 Object URL
imageUrl.value = URL.createObjectURL(file);
// 使用服务器返回的文件路径(根据实际响应结构调整)
if (response && response.filePath) { recharge.value.voucher = response.filePath; } else if (response && response.url) { recharge.value.voucher = response.url; } else if (response && response.data) { // 假设响应结构为 { code: 200, data: { filePath: ... } }
recharge.value.voucher = response.data.filePath || response.data.url; } else { // 后备方案:使用环境变量中的上传URL
recharge.value.voucher = import.meta.env.VITE_UPLOAD_URL; } }
//支付方式条目
const payModel = [ { value: '银行转账', label: '银行转账' }, { value: '现金', label: '现金' }, { value: '支票', label: '支票' }, { value: '刷卡', label: '刷卡' }, { value: 'Grabpay', label: 'Grabpay' }, { value: 'Nets', label: 'Nets' }, { value: 'PayPal', label: 'PayPal' }, { value: 'Stripe-链接收款', label: 'Stripe-链接收款' }, { value: 'Ipay88-链接收款', label: 'Ipay88-链接收款' }, { value: 'PaymentAsia-链接收款', label: 'PaymentAsia-链接收款' }, { value: '其他', label: '其他' } ]
// }
function handleActivityChange(value) { // 在这里执行你的逻辑,例如获取选中的值
console.log('选中的值:', value) // getActivityById(value)
console.log('看看', recharge.value) }
//这是重置重置表单的方法
const deleteRecharge = function () { Ref.value.resetFields(); recharge.value = { adminId: adminData.value.id, //adminId: fixedAdminId,
market: adminData.value.market, voucher: '', rechargeWay: '客服充值', freeGold: '', money: null, permanentGold: '', rateId: null } imageUrl.value = '' recharge.value.rateName = '' }
onMounted(async function () { await getAdminData() // await getCurrency()
// await getActivity()// 现在的活动就是文字输入框,不需要请求接口,具体等后续需求
}) onMounted(() => { console.log('上传URL:', import.meta.env.VITE_UPLOAD_URL); }); </script>
<template> <div> <el-form :model="recharge" ref="Ref" :rules="rules" label-width="auto" label-position="right" style="max-width: 600px" class="add-form"> <el-form-item prop="jwcode" label="精网号"> <el-input v-model="recharge.jwcode" style="width: 220px" /> <el-button type="primary" @click="getUser(recharge.jwcode)" style="margin-left: 20px">查询</el-button> </el-form-item> <el-form-item prop="activity" label="活动名称"> <el-input v-model="recharge.activity" placeholder="请输入活动名称" style="width: 300px" /> </el-form-item> <el-form-item prop="permanentGold" label="永久金币"> <el-input v-model="recharge.permanentGold" placeholder="0" style="width: 100px" /> <p> 个</p> </el-form-item> <el-form-item prop="freeGold" label="免费金币"> <el-input v-model="recharge.freeGold" placeholder="0" style="width: 100px" /> <p> 个</p> </el-form-item> <el-form-item label="充值金额" required> <el-form-item prop="rateName" style="display: inline-block; margin-left:0;"> <el-select v-model="recharge.rateName" placeholder="货币名称" style="width: 100px"> <el-option v-for="item in rateName" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <el-form-item prop="money" style="display: inline-block; margin-left:10px;"> <el-input v-model="recharge.money" style="width: 190px" /> </el-form-item> </el-form-item>
<el-form-item prop="payModel" label="收款方式"> <el-select v-model="recharge.payModel" placeholder="请选择" style="width: 300px"> <el-option v-for="item in payModel" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <el-form-item prop="payTime" label="交款时间"> <!-- 修改 type 属性为 datetime 以支持时分秒选择 --> <el-date-picker v-model="recharge.payTime" type="datetime" style="width: 300px" /> </el-form-item> <el-form-item prop="voucher" label="交款凭证" style="margin-bottom: 5px"> <el-upload :http-request="customUpload" class="avatar-uploader" :show-file-list="false" :before-upload="beforeAvatarUpload" style="width: 100px; height: 115px"> <img v-if="imageUrl" :src="imageUrl" class="avatar" style="width: 100px; height: 115px" /> <el-icon v-else class="avatar-uploader-icon" style="width: 100px; height: 100px"> <Plus /> </el-icon> </el-upload> <p style="margin-left: 10px; color: rgb(177, 176, 176)"> 仅支持.jpg .png格式,文件≤1MB </p> </el-form-item> <el-form-item prop="remark" label="备注"> <el-input v-model="recharge.remark" style="width: 300px" :rows="4" maxlength="100" show-word-limit type="textarea" /> </el-form-item>
<el-button @click="deleteRecharge" style="margin-left: 220px;margin-top:20px" type="success">重置</el-button> <el-button type="primary" style="margin-top:20px" :disabled="addDisabled" @click="addBefore"> 提交</el-button> </el-form>
<!-- 客户信息栏 --> <el-card v-if="user.jwcode" style="width: 800px" class="beautiful"> <el-form :model="user" label-width="auto" style="max-width: 1000px" label-position="left"> <el-text size="large" style="margin-left: 20px">客户信息</el-text>
<!-- 第一行:姓名 + 历史金币 --> <el-row style="margin-top: 20px"> <el-col :span="9"> <el-form-item label="姓名"> <p>{{ user.name }}</p> </el-form-item> </el-col> <el-col :span="14"> <el-form-item label="当前金币总数" style="width: 500px"> <span style="color: #2fa1ff; margin-right: 5px" v-if="user.nowSumGold !== undefined">{{ user.nowSumGold }}</span> </el-form-item> <!-- 金币详情独立显示 --> <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 --> <span style="color: #b1b1b1; margin-left: 0px" v-if="user.nowPermanentGold !== undefined">(永久金币:{{ user.nowPermanentGold }}; 免费金币:{{ user.nowFreeGold }}; 任务金币:{{ user.nowTaskGold }})</span> </el-form-item> </el-col> </el-row>
<!-- 第二行:精网号 + 当前金币(独立行) --> <el-row> <el-col :span="9"> <el-form-item label="精网号"> <p>{{ user.jwcode }}</p> </el-form-item> </el-col> <el-col :span="14"> <el-form-item label="充值次数"> <p style="color: #2fa1ff">{{ user.rechargeNum }}</p> </el-form-item> </el-col> </el-row>
<!-- 第三行:首次充值日期 + 充值次数 --> <el-row > <el-col :span="9"> <el-form-item label="首次充值日期"> <p v-if="user.firstRecharge"> {{ moment(user.firstRecharge).format('YYYY-MM-DD HH:mm:ss') }} </p> </el-form-item> </el-col> <el-col :span="14"> <el-form-item label="消费次数"> <p style="color: #2fa1ff">{{ user.consumeNum }}</p> </el-form-item> </el-col> </el-row>
<!-- 第四行:消费次数 + 所属门店 --> <el-row> <el-col :span="9"> <el-form-item label="所属门店"> <p>{{ user.market }}</p> </el-form-item> </el-col> </el-row> </el-form> </el-card>
<el-dialog v-model="FirstRechargeDialogVisible" title="操作确认" :before-close="FirstRechargeDialogVisiblehandleClose" :close-on-click-modal="false" width="400px"> <!-- 内容整体居中且收窄 --> <div class="confirm-body"> <!-- 用户信息 --> <div> <div class="field-label">用户信息</div> <el-input :model-value="user.jwcode + (user.name ? '【' + user.name + '】' : '')" disabled/> </div> <!-- 活动名称 --> <div class="field"> <div class="field-label">活动名称</div> <el-input v-model="recharge.activity" disabled/> </div> <!-- 金币信息(同一行左右排列) --> <el-row :gutter="20" class="coins-row"> <el-col :span="12"> <div class="field"> <div class="field-label">永久金币</div> <el-input v-model="recharge.permanentGold" disabled/> </div> </el-col> <el-col :span="12"> <div class="field"> <div class="field-label">免费金币</div> <el-input v-model="recharge.freeGold" disabled/> </div> </el-col> </el-row>
<div class="field"> <div class="field-label">备注</div> <el-input v-model="recharge.remark" disabled/> </div>
</div>
<!-- 底部按钮(居中) --> <template #footer> <div class="dialog-footer-center"> <el-button @click="FirstRechargeDialogVisibleCancel">取 消</el-button> <el-button type="primary" @click="FistRechargeDialogVisibleContinue">确认充值</el-button> </div> </template> </el-dialog>
<el-dialog v-model="RechargeDialogVisible" title="操作确认" :before-close="RechargeDialogVisiblehandleClose" :close-on-click-modal="false" width="480px"> <!-- 内容整体居中且收窄 --> <div class="confirm-body"> <!-- 用户信息 --> <div> <div class="field-label">用户信息</div> <el-input :model-value="user.jwcode + (user.name ? '【' + user.name + '】' : '')" disabled /> </div> <!-- 活动名称 --> <div class="field"> <div class="field-label">活动名称</div> <el-input v-model="recharge.activity" disabled /> </div> <!-- 金币信息(同一行左右排列) --> <el-row :gutter="20" class="coins-row"> <el-col :span="12"> <div class="field"> <div class="field-label">永久金币</div> <el-input v-model="recharge.permanentGold" disabled /> </div> </el-col> <el-col :span="12"> <div class="field"> <div class="field-label">免费金币</div> <el-input v-model="recharge.freeGold" disabled /> </div> </el-col> </el-row> <!-- 风险提示 --> <div style="display: flex; align-items: center; margin-top: 20px;"> <el-icon :size="24" color="#FFD700"> <WarnTriangleFilled /> </el-icon> <p>重复充值风险提示</p> </div> <!-- 记录 + 虚线分隔 --> <div> <el-divider border-style="dashed" /> <p>检测到该用户近期有相似充值记录:</p> · {{ ReadCookiesTime }} 充值永久金币: {{ recharge.permanentGold }} 免费金币: {{ recharge.freeGold }}(操作人:{{ adminData.adminName }}) </div> <div style="margin-top: 10px"> <p>是否继续操作?</p>
</div> </div>
<!-- 底部按钮(居中) --> <template #footer> <div class="dialog-footer-center"> <el-button @click="RechargeDialogVisibleCancel">取 消</el-button> <el-button type="primary" @click="RechargeDialogVisibleContinue">确认充值</el-button> </div> </template> </el-dialog>
</div> </template>
<style scoped> p { margin: 0px; }
.batch-btn { margin-top: 20px; margin-left: auto; }
.el-form-item { margin-left: 50px; }
.add-form { margin-top: 50px; max-width: 50%; float: left; }
.beautiful { width: 700px; float: right; margin-right: 300px; margin-top: 150px; }
.field-label { font-size: 14px; color: #606266; margin-bottom: 6px; }
/* 金币行紧凑 */ .coins-row .field { margin-bottom: 0; }
/* 底部按钮居中 */ .dialog-footer-center { display: flex; justify-content: center; gap: 12px; } </style>
<style> /* 上传图片的格式 */ .avatar-uploader .avatar { width: 50px; height: 50px; display: block; } .avatar-uploader .el-upload { border: 1px dashed var(--el-border-color); border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; transition: var(--el-transition-duration-fast); }
.avatar-uploader .el-upload:hover { border-color: var(--el-color-primary); }
.el-icon.avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 50px; height: 50px; text-align: center; } </style>
|