This commit is contained in:
2025-10-20 17:12:55 +08:00
parent b73e0f8385
commit cec7b03d71
15 changed files with 1759 additions and 571 deletions

59
src/common/js/debounce.js Normal file
View File

@@ -0,0 +1,59 @@
let timeout = null
/**
* 防抖原理一定时间内只有最后一次操作再过wait毫秒后才执行函数
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function debounce(func, wait = 500, immediate = false) {
// 清除定时器
if (timeout !== null) clearTimeout(timeout)
// 立即执行,此类情况一般用不到
if (immediate) {
const callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) typeof func === 'function' && func()
} else {
// 设置定时器当最后一次操作后timeout不会再被清除所以在延时wait毫秒后执行func回调方法
timeout = setTimeout(() => {
typeof func === 'function' && func()
}, wait)
}
}
function throttle(fn, delay = 1000, immediate = false) {
console.log("进入节流对象")
let timer
let status = false // 是否为重复点击状态
return function () {
let _this = this
let args = arguments
if (immediate) {
console.log("立即执行参数 执行一次方法")
fn.apply(_this, args)
immediate = false
return
}
if (status) {
console.log("当前点击状态为正在重复点击,请稍等片刻后在点击执行")
return
}
console.log("执行节流:当前执行了一次点击方法")
fn.apply(_this, args)
status = true // 修改状态
timer = setTimeout(() => {
console.log("规定时间到,重置状态,可以重新调用")
status = false
}, delay)
}
}
export {
debounce,
throttle
}

View File

@@ -90,7 +90,9 @@ const mainRoutes = {
{ path: '/vipList-userList', component: _import('modules/vipList/userList'), name: 'vipList-userList', meta: { title: 'VIP用户列表', isTab: true } },
{ path: '/vipList-vipDetail', component: _import('modules/vipList/vipDetail'), name: 'vipList-vipDetail', meta: { title: 'VIP详情', isTab: true } },
{ path: '/reportList-vipList', component: _import('modules/reportList/vipList'), name: 'reportList-vipList', meta: { title: 'VIP报表', isTab: true } },
{ path: '/reportList-courseList', component: _import('modules/reportList/courseList'), name: 'reportList-courseList', meta: { title: 'VIP报表', isTab: true } },
{ path: '/reportList-trainingCourseList', component: _import('modules/reportList/trainingCourseList'), name: 'reportList-trainingCourseList', meta: { title: '培训班月度报表', isTab: true } },
{ path: '/reportList-trainingCourseClassList', component: _import('modules/reportList/trainingCourseClassList'), name: 'reportList-trainingCourseClassList', meta: { title: '培训班报表', isTab: true } },
{ path: '/reportList-courseList', component: _import('modules/reportList/courseList'), name: 'reportList-courseList', meta: { title: '课程报表', isTab: true } },
],
beforeEnter (to, from, next) {

View File

@@ -1,8 +1,8 @@
<script type="text/javascript">
// const baseUrl = "http://192.168.110.100:9200/pb"; //张川川后端
const baseUrl = "http://192.168.110.100:9200/pb"; //张川川后端
// const baseUrl = 'http://59.110.212.44:9100/pb'
// const baseUrl = "https://testapi.nuttyreading.com";
const baseUrl = 'https://api.nuttyreading.com'
// const baseUrl = 'https://api.nuttyreading.com'
// function commonFun() {
// console.log("公共方法")
// }

View File

@@ -1,443 +0,0 @@
<template>
<div
class="calendar"
style="min-width: 1000px;overflow-x: auto;min-height: 100%;"
>
<div
v-if="allMonthData.length > 0"
@click="handleExportAll()"
style="position: absolute;left: 360px;top:30px;cursor: pointer;color: #006699;font-size: 14px;"
>
<i class="el-icon-download"></i>下载 {{ year }} 年全部天医币报表
</div>
<el-card
class="box-card"
v-for="(month, mIndex) in allMonthData"
:key="mIndex"
>
<div slot="header" class="clearfix">
<div
style="color: #888;font-weight: 700;text-align: center;position: relative;font-size: 20px;"
>
{{ month.month }}
<el-button
v-if="month && month.total && month.total.length > 0"
@click="handleExport(Number(month.month))"
style="float: right; padding: 3px 0;position: absolute;right: 0px;top: -2px;"
type="text"
><i class="el-icon-download"></i>下载报表</el-button
>
</div>
</div>
<div
class="days"
style=""
v-if="month && month.total && month.total.length > 0"
:style="{
'grid-template-columns': `repeat(${
month.total.length && month.total.length > 2
? 2
: month.total.length
}, 1fr)`
}"
>
<div
v-for="d in month.total"
:key="d"
class="day header"
style="margin-bottom: 4px;text-align:left;"
>
{{ d.type }}
<span style="font-size: 16px;" v-if="d.payType">
<img
src="/static/img/oder_chong.png"
alt=""
style="width: 20px;"
v-if="d.payType == '天医币'"
/>
<img
src="/static/img/pingguo.png"
alt=""
style="width: 18px;margin-top: -6px;"
v-if="d.payType == '苹果'"
/>
<icon-svg
name="weixin"
v-if="d.payType == '微信'"
style=""
></icon-svg>
<icon-svg name="zhifubao" v-if="d.payType == '支付宝'"></icon-svg>
</span>
<div class="day" style="display: inline-block;">: {{ d.amount }}</div>
</div>
<!-- <div
v-for="day in month.days"
:key="day.date"
template
:class="day.color"
>
{{ day.number }}
</div> -->
</div>
<div
v-else
style="font-size: 20px;color: #888;text-align: center;line-height: 100px;"
>
暂无数据
</div>
</el-card>
</div>
</template>
<script>
// 获取当年到当前月的所有月份数组
function getMonths(year) {
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1; // 月份从0开始
// 如果是今年只取到当前月否则取12个月
const endMonth = year == currentYear ? currentMonth - 1 : 12;
let months = [];
for (let i = 1; i <= endMonth; i++) {
let monthStr = i < 10 ? "0" + i : i; // 补零
months.push(`${monthStr}`);
}
return months;
}
export default {
name: "FullYearCalendar",
props: {
year: {
type: Number,
default: new Date().getFullYear()
},
// 标记数据,例如:{ "2019-01-05": "red", "2019-03-12": "yellow" }
marks: {
type: Object,
default: () => ({})
},
urlList: {
type: Object,
default: () => ({})
}
},
data() {
return {
showCurrentMonth: "",
showCurrentMonthDate: "",
allMonthData: [],
list: [
{ title: "收入", val: "fee" },
{ title: "月摊销", val: "currentTanxiao" },
{ title: "已摊销", val: "alreadyTanxiao" },
{ title: "剩余摊销", val: "notyetTanxiao" }
],
weekDays: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
monthNames: [
"一",
"二",
"三",
"四",
"五",
"六",
"七",
"八",
"九",
"十",
"十 一",
"十 二"
]
};
},
computed: {
// months() {
// let months = [];
// for (let m = 0; m < 12; m++) {
// let firstDay = new Date(this.year, m, 1);
// let lastDay = new Date(this.year, m + 1, 0);
// let daysInMonth = lastDay.getDate();
// let startWeek1 = (firstDay.getDay() + 6) % 7; // 转换为 Mon=0
// let startWeek = (firstDay.getDay() + 6) % 7; // 转换为 Mon=0
// let days = [];
// for (let d = 1; d <= daysInMonth; d++) {
// let dateStr = `${this.year}-${String(m+1).padStart(2,"0")}-${String(d).padStart(2,"0")}`;
// days.push({
// number: d,
// date: dateStr,
// color: this.marks[dateStr] || ""
// });
// }
// months.push({ startWeek, days });
// }
// return months;
// }
},
async created() {
await this.getMonthsByYear(this.year);
await this.fetchAllMonthData();
},
methods: {
async handleExportAll() {
const months = this.allMonthData.map(m => m.month);
this.$message({
message: "所有月份的天医币报表正在下载,请耐心等待...",
type: "info",
duration: 6000,
showClose: true
});
// 构造请求数组
const requests = months.map(m => {
return this.$http({
url: this.$http.adornUrl(this.urlList.export),
method: "post",
data: this.$http.adornData({ date: `${this.year}-${m}` }),
responseType: "blob"
})
.then(res => {
const filename = `天医币报表_${this.year}${m}月下载文件.xlsx`;
const blob = new Blob([res.data], {
type:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = filename;
link.click();
window.URL.revokeObjectURL(link.href);
return { month: m, success: true };
})
.catch(() => {
return { month: m, success: false };
});
});
// 等待所有请求完成
const results = await Promise.all(requests);
// 统计成功/失败
const successMonths = results.filter(r => r.success).map(r => r.month);
const failMonths = results.filter(r => !r.success).map(r => r.month);
let message = `报表下载完成:成功 ${successMonths.length}`;
if (failMonths.length > 0) {
message += `,失败 ${failMonths.length} 个(失败月份: ${failMonths.join(
"、"
)}`;
}
this.$message({
message,
type: failMonths.length === 0 ? "success" : "warning",
duration: 5000,
showClose: true
});
},
async handleExport(mIndex) {
console.log("mIndex at line 120:", mIndex);
var filename = `天医币报表_${this.year}${this.pad(
mIndex
)}月下载文件.xlsx`;
this.$message({
message: "请耐心等待...",
type: "info",
duration: 3000,
showClose: true
});
try {
this.$http({
url: this.$http.adornUrl(this.urlList.export),
method: "post",
data: this.$http.adornData({
date: `${this.year}-${this.pad(mIndex)}`
}),
responseType: "blob" // ⚡⚡⚡一定要加上
}).then(res => {
// this.$message({
// message: "天医币报表文件下载中,下载完成后文件会自动下载,请耐心等待...",
// type: "info",
// duration: 6000,
// showClose: true
// });
const blob = new Blob([res.data], {
type:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = filename;
link.click();
window.URL.revokeObjectURL(link.href); // 释放内存
this.$message({
message: "天医币报表文件下载完成,请注意查看!",
type: "success",
duration: 3000,
showClose: true
});
});
// const res = await axios.post(
// window.SITE_CONFIG.baseUrl+"/master/userContribution/exportContributionStatQuery",
// {
// current: this.current,
// limit: this.limit
// },
// {
// responseType: "blob" // 关键点:告诉 axios 返回的是二进制文件
// }
// );
} catch (err) {
this.$message.error("文件下载失败!");
// console.error("文件下载失败:", err);
}
},
async init() {
this.showCurrentMonth = "";
this.showCurrentMonthDate = "";
await this.getMonthsByYear(this.year);
await this.fetchAllMonthData();
},
getMonthsByYear(year) {
this.months = getMonths(year);
},
pad(num) {
return num < 10 ? "0" + num : num;
},
fetchAllMonthData() {
const loading = this.$loading({
lock: true,
text: "统计中",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
});
const year = this.year;
const months = getMonths(year);
console.log("months", months);
// 构造请求数组
const requests = months.map(m => {
const monthNum = m; // 拿到 1,2,3...
return this.$http({
url: this.$http.adornUrl(this.urlList.list),
method: "post",
data: this.$http.adornData({ date: `${year}-${m}` })
})
.then(res => {
if (res.data && res.data.code === 0) {
return { month: monthNum, total: res.data.total };
} else {
return { month: monthNum, total: null, error: "获取失败" };
}
})
.catch(err => {
return {
month: monthNum,
total: null,
error: err.message || "请求出错"
};
});
});
// 并发请求
Promise.all(requests)
.then(resArr => {
console.log("所有月份结果:", resArr);
loading.close();
this.allMonthData = resArr.filter(
r => r.total !== null && r.total.length !== 0
);
if (this.allMonthData.length == 0) {
this.$message({
message: "该年无数据",
type: "warning",
duration: 3000,
showClose: true
});
}
if (this.year == new Date().getFullYear()) {
const now = new Date();
const year = now.getFullYear();
const month = this.pad(now.getMonth() + 1);
const day = this.pad(now.getDate());
this.showCurrentMonth = now.getMonth() + 1;
console.log(
"this.showCurrentMonth at line 111:",
this.showCurrentMonth
);
this.showCurrentMonthDate = `${year}-${month}-${day}`;
console.log("当前完整日期:", this.showCurrentMonthDate);
}
// resArr 是每个月接口返回结果的数组
})
.catch(err => {
console.error("获取所有月份数据失败:", err);
loading.close();
});
}
}
};
</script>
<style scoped>
.calendar {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.month {
background: #fff;
border: 1px solid #ddd;
border-radius: 6px;
padding: 10px;
text-align: center;
}
.month h3 {
margin: 0 0 5px;
font-size: 16px;
}
.days {
display: grid;
grid-template-columns: repeat(4, 1fr);
font-size: 16px;
}
.day {
text-align: center;
padding: 4px;
margin: 2px;
border-radius: 4px;
min-height: 20px;
}
.day.header {
font-weight: bold;
color: #555;
}
.day.red {
background: #ffdddd;
color: #d00;
}
.day.blue {
background: #ddeeff;
color: #004;
}
.day.yellow {
background: #fff6cc;
color: #665500;
}
/deep/ .el-card .el-card__header {
padding: 8px 20px !important;
}
/deep/ .el-card .el-card__body {
padding: 10px !important;
}
</style>

View File

@@ -0,0 +1,347 @@
<template>
<div class="calendar" style="min-width: 1000px;overflow-x: auto;min-height: 100%;">
<div v-if="allMonthData.length>0" @click="handleExportAll()" style="position: absolute; left: 360px;top:30px;cursor: pointer;color: #006699;font-size: 14px;"><i class="el-icon-download"></i>下载 {{selectYear}} 年全部培训班报表</div>
<el-card class="box-card" v-for="(month, mIndex) in allMonthData"
:key="mIndex" >
<div slot="header" class="clearfix" >
<div style="color: #888;font-weight: 700;text-align: center;position: relative;font-size: 20px;">{{ month.month}} <el-button @click="handleExport(Number(month.month))" style="float: right; padding: 3px 0;position: absolute;right: 0px;top: -2px;" type="text"><i class="el-icon-download"></i>下载报表</el-button>
</div>
</div>
<div class="days" >
<!-- <div v-for="d in weekDays" :key="d" class="day header">{{ d }}</div> -->
<!-- <div v-for="blank in month.startWeek" :key="'b'+blank" class="day"></div> -->
<div v-for="d in list" class="day header" style="margin-bottom: 10px;color:#888;">{{ d.title }}</div>
<div v-for="d in list" style="font-weight: bold;font-size: 18px;" class="day" :style="`${d.val=='currentTanxiao'?'background-color:#11cdba30;color:#d00':''}`">{{
month.total[d.val]
}}</div>
<!-- <div
v-for="day in month.days"
:key="day.date"
template
:class="day.color"
>
{{ day.number }}
</div> -->
</div>
<!-- <p
style="margin-top: 10px;text-align: left;color: #006699;margin-left: -4px;margin-bottom: 0;"
>
合计 : 222
</p> -->
</el-card>
</div>
</template>
<script>
// 获取当年到当前月的所有月份数组
function getMonths(year) {
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1; // 月份从0开始
// 如果是今年只取到当前月否则取12个月
const endMonth = year == currentYear ? currentMonth : 12;
let months = [];
for (let i = 1; i <= endMonth; i++) {
let monthStr = i < 10 ? "0" + i : i; // 补零
months.push(`${monthStr}`);
}
return months;
}
export default {
name: "FullYearCalendar",
props: {
selectYear: {
type: Number,
default: new Date().getFullYear()
},
// 标记数据,例如:{ "2019-01-05": "red", "2019-03-12": "yellow" }
marks: {
type: Object,
default: () => ({})
},
urlList: {
type: Object,
default: () => ({})
}
},
data() {
return {
// 支付宝: 4320, 海外: 0, 银行: 0, 天医币: 20575, 微信: 9782
showCurrentMonth: '',
showCurrentMonthDate: '',
allMonthData: [],
list: [ {title:'微信',val:'微信'},
{title:'支付宝',val:'支付宝'} ,
{title:'天医币',val:'天医币'},
{title:'银行',val:'银行'},
{title:'海外',val:'海外'},
],
weekDays: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
monthNames: [
"一", "二", "三", "四",
"五", "六", "七", "八",
"九", "十", "十 一", "十 二"
]
};
},
computed: {
},
async created() {
await this.getMonthsByYear(this.selectYear)
await this.fetchAllMonthData()
},
methods: {
async handleExportAll() {
const months = this.allMonthData.map(m => m.month); // 例如 ["01","02",...,"09"]
this.$message({
message: "所有月份的培训班报表正在下载,请耐心等待...",
type: "info",
duration: 6000,
showClose: true
});
// 构造请求数组
const requests = months.map(m => {
return this.$http({
url: this.$http.adornUrl(this.urlList.export),
method: "post",
data: this.$http.adornData({ date: this.selectYear+'-'+m }),
responseType: "blob"
}).then(res => {
const filename = `培训班报表_${this.selectYear}${m}月下载文件.xlsx`
const blob = new Blob([res.data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
})
const link = document.createElement("a")
link.href = window.URL.createObjectURL(blob)
link.download = filename
link.click()
window.URL.revokeObjectURL(link.href)
return { month: m, success: true }
}).catch(() => {
return { month: m, success: false }
})
})
// 等待所有请求完成
const results = await Promise.all(requests)
// 统计成功/失败
const successMonths = results.filter(r => r.success).map(r => r.month)
const failMonths = results.filter(r => !r.success).map(r => r.month)
let message = `报表下载完成:成功 ${successMonths.length}`
if (failMonths.length > 0) {
message += `,失败 ${failMonths.length} 个(失败月份: ${failMonths.join("、")}`
}
this.$message({
message,
type: failMonths.length === 0 ? "success" : "warning",
duration: 5000,
showClose: true
});
},
async handleExport(mIndex) {
console.log('mIndex at line 120:', mIndex)
var filename = `培训班报表_${this.selectYear}${this.pad(mIndex)}月下载文件.xlsx`;
this.$message({
message: "请耐心等待...",
type: "info",
duration: 3000,
showClose: true
});
try {
this.$http({
url: this.$http.adornUrl(
this.urlList.export,
),
method: "post",
data: this.$http.adornData({
date: this.selectYear+'-'+this.pad(mIndex)
}),
responseType: "blob" // ⚡⚡⚡一定要加上
}).then(res => {
// this.$message({
// message: "培训班报表文件下载中,下载完成后文件会自动下载,请耐心等待...",
// type: "info",
// duration: 6000,
// showClose: true
// });
const blob = new Blob([res.data], {
type:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = filename;
link.click();
window.URL.revokeObjectURL(link.href); // 释放内存
this.$message({
message: "培训班报表文件下载完成,请注意查看!",
type: "success",
duration: 3000,
showClose: true
});
});
// const res = await axios.post(
// window.SITE_CONFIG.baseUrl+"/master/userContribution/exportContributionStatQuery",
// {
// current: this.current,
// limit: this.limit
// },
// {
// responseType: "blob" // 关键点:告诉 axios 返回的是二进制文件
// }
// );
} catch (err) {
this.$message.error("文件下载失败!");
// console.error("文件下载失败:", err);
}
},
async init(){
this.showCurrentMonth = ''
this.showCurrentMonthDate = ''
await this.getMonthsByYear(this.selectYear)
await this.fetchAllMonthData()
},
getMonthsByYear(year) {
this.months = getMonths(year)
}
,pad(num) {
return num < 10 ? "0" + num : num
}
,
fetchAllMonthData() {
const loading = this.$loading({
lock: true,
text: "统计中",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
});
const year = this.selectYear
const months = getMonths(year)
console.log('months', months)
// 构造请求数组
const requests = months.map(m => {
const monthNum = m // 拿到 1,2,3...
return this.$http({
url: this.$http.adornUrl(this.urlList.list),
method: "post",
data: this.$http.adornData({ date: year+'-'+m })
}).then(res => {
//
if (res.data && res.data.code === 0) {
return { month: monthNum, total: res.data.list }
} else {
return { month: monthNum, total: null, error: "获取失败" }
}
}).catch(err => {
return { month: monthNum, total: null, error: err.message || "请求出错" }
})
})
// 并发请求
Promise.all(requests).then(resArr => {
console.log("所有月份结果:", resArr)
loading.close()
this.allMonthData = resArr.filter(
r => r.total !== null && r.total.length !== 0
);
if(this.allMonthData.length==0){
this.$message({
message: "该年无数据",
type: "warning",
duration: 3000,
showClose: true
});
}
if (this.selectYear == new Date().getFullYear()) {
const now = new Date()
const year = now.getFullYear()
const month = this.pad(now.getMonth() + 1)
const day = this.pad(now.getDate())
this.showCurrentMonth = now.getMonth() + 1
console.log('this.showCurrentMonth at line 111:', this.showCurrentMonth)
this.showCurrentMonthDate = `${year}-${month}-${day}`
console.log('当前完整日期:', this.showCurrentMonthDate)
}
// resArr 是每个月接口返回结果的数组
}).catch(err => {
console.error("获取所有月份数据失败:", err)
loading.close()
})
}
},
};
</script>
<style scoped>
.calendar {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.month {
background: #fff;
border: 1px solid #ddd;
border-radius: 6px;
padding: 10px;
text-align: center;
}
.month h3 {
margin: 0 0 5px;
font-size: 16px;
}
.days {
display: grid;
grid-template-columns: repeat(5, 1fr);
font-size: 15px;
}
.day {
text-align: center;
padding: 4px;
margin: 2px;
border-radius: 4px;
min-height: 20px;
}
.day.header {
font-weight: bold;
color: #555;
}
.day.red { background: #ffdddd; color: #d00; }
.day.blue { background: #ddeeff; color: #004; }
.day.yellow { background: #fff6cc; color: #665500; }
/deep/ .el-card .el-card__header{
padding: 4px 20px !important;
}
/deep/ .el-card .el-card__body {
padding: 15px !important;
overflow-x: auto !important;
}
</style>

View File

@@ -0,0 +1,676 @@
<template>
<div class="mod-config">
<el-button
@click="
pageIndex = 1;
getDataList();
"
>刷新</el-button
>
<!-- <div v-if="dataList.length>0" @click="handleExportAll()" style="position: absolute; left: 120px;top:34px;cursor: pointer;color: #006699;font-size: 14px;"><i class="el-icon-download"></i>下载培训班报表</div> -->
<el-table
:data="dataList"
border
v-loading="dataListLoading"
style="width: 100%;margin-top: 20px;"
@sort-change="handleSortChange"
>
<el-table-column
label="序号"
type="index"
align="center"
width="60">
</el-table-column>
<el-table-column label="培训班名称" align="center" >
<template slot-scope="scope">{{ scope.row.title }}</template>
</el-table-column>
<el-table-column label="微信" align="center" width="140" prop="微信">
</el-table-column>
<el-table-column label="支付宝" align="center" width="140" prop="支付宝">
</el-table-column>
<el-table-column label="天医币" align="center" width="140" prop="天医币">
</el-table-column>
<el-table-column label="银行" align="center" width="140" prop="银行">
</el-table-column>
<el-table-column label="海外" align="center" width="140" prop="海外">
</el-table-column>
<el-table-column label="赠送" align="center" width="140" prop="赠送">
</el-table-column>
<el-table-column label="操作" align="center" width="140" prop="操作">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleExport(scope.row)">导出报表</el-button>
</template>
</el-table-column>
</el-table>
<!-- 弹窗, 新增 / 修改 -->
</div>
</template>
<script>
import global from "../../common/common.vue"; //引入共用组间
export default {
data() {
// 通用校验规则:正数且大于 0
const validatePositiveNumber = (rule, value, callback) => {
if (!value) {
return callback(new Error('原价不能为空'));
}
if (!/^\d+(\.\d+)?$/.test(value)) {
return callback(new Error('必须是正数或小数'));
}
if (parseFloat(value) <= 0) {
return callback(new Error('必须大于 0'));
}
callback();
};
return {
baseUrl: global.baseUrl,
dataForm: {
title: '',
type: '', //1线上 2线下
year: '', //日期
trainingDate: '',
singupFlag: '', //1可报名 0不可报名
displayFlag: '', //0上架 1下架
displayApp: ''
},
addForm: {
id: "", //新增不传
title: "", //标题
type: 1, //1线上 2线下
icon: '',
year: '',
trainingDate: '', //开始日期
endDate: '', //截止日期
fee: '',
vipType: '',
vipFee: '',
svipType: '',
svipFee: '',
threeHuFee: '',
fiveHuFee: '',
singupFlag: 1, //1可报名 0不可报名
displayFlag: 0, //0上架 1下架
displayApp: '',
sort: ''
},
editId: "",
addFormRule: {
title: [
{
required: true,
message: "请输入标题",
},
],
trainingDate: [
{
required: true,
message: "请选择日期",
trigger: 'change'
},
],
fee: [
{
required: true,
validator: validatePositiveNumber
},
],
},
dateRange: [], //日期
dataList: [],
pageIndex: 1,
pageSize: 10,
total: 0,
dataListLoading: false,
addOrUpdateVisible: false,
titlesub: '新增',
statusType: 0, //区分新增还是修改
value: '', //选择日期
fileList: [], //图片数组
dialogVisible: false,
//保存数据
vipList: [],
svipList: [],
appList: [
{
type: '1',
title: '众妙之门'
},
{
type: '2',
title: '吴门医述'
},
{
type: '3',
title: '心灵空间'
}
],
//勾选值
vipType: [],
svipType: [],
appType: [],
//日期排序
sortParams: {
date1: '',
date2: ''
},
//疯子读书-0 众妙之门-1 吴门医述-2 心灵空间-3 太湖云医-4
};
},
activated(){
this.getDataList();
this.getVipData();
},
methods: {
formatDateTime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day}`;
},
async handleExport(data) {
console.log('data at line 181:', data)
var time=this.formatDateTime(new Date())
var filename = `${data.title}_培训班报表下载文件_${time}.xlsx`;
this.$message({
message: "请耐心等待...",
type: "info",
duration: 3000,
showClose: true
});
try {
this.$http({
url: this.$http.adornUrl(
'/master/statistics/getTrainingClassInfo'
),
method: "post",
data: this.$http.adornData({
trainingId:data.id
}),
responseType: "blob" // ⚡⚡⚡一定要加上
}).then(res => {
// this.$message({
// message: "VIP报表文件下载中下载完成后文件会自动下载请耐心等待...",
// type: "info",
// duration: 6000,
// showClose: true
// });
const blob = new Blob([res.data], {
type:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = filename;
link.click();
window.URL.revokeObjectURL(link.href); // 释放内存
this.$message({
message: "培训班报表文件下载完成,请注意查看!",
type: "success",
duration: 3000,
showClose: true
});
});
// const res = await axios.post(
// window.SITE_CONFIG.baseUrl+"/master/userContribution/exportContributionStatQuery",
// {
// current: this.current,
// limit: this.limit
// },
// {
// responseType: "blob" // 关键点:告诉 axios 返回的是二进制文件
// }
// );
} catch (err) {
this.$message.error("文件下载失败!");
// console.error("文件下载失败:", err);
}
},
//app名称展示的逻辑
getAppNames(displayApp) {
if (!displayApp) return ''
return displayApp.split(',')
.map(code => code.trim())
.filter(code => code !== '')
.map(code => {
const app = this.appList.find(item => item.type === code);
return app ? app.title : '';
})
.join(' ');
},
//排序变化事件
handleSortChange({ column, prop, order }) {
// 更新当前列的排序状态
var name = column.label;
if(name=='培训日期'){
this.sortParams.date1 = order
? (order === 'ascending' ? '2' : '1')
: null;
}else{
this.sortParams.date2 = order
? (order === 'ascending' ? '2' : '1')
: null;
}
this.getDataList();
},
//时间选择器
handleDateChange(val){
this.addForm.trainingDate = this.formatDate(val[0]);
this.addForm.endDate = this.formatDate(val[1]);
},
//日期格式转换
formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份补零
const day = String(date.getDate()).padStart(2, '0'); // 日期补零
return `${year}-${month}-${day}`;
},
// 获取数据列表
getDataList() {
this.dataListLoading = true;
this.$http({
url: this.$http.adornUrl("/master/statistics/getTrainingClassTotal"),
method: "post",
data: this.$http.adornData({
// current: this.pageIndex,
// limit: this.pageSize,
// trainingDateSort: this.sortParams.date1,
// createTimeSort: this.sortParams.date2,
// title: this.dataForm.title,
// type: this.dataForm.type,
// year: this.dataForm.year,
// singupFlag: this.dataForm.singupFlag,
// //displayFlag: this.dataForm.displayFlag,
// displayApp: this.dataForm.displayApp
}),
}).then(({ data }) => {
if (data && data.code === 0) {
this.dataList = data.list;
// this.total = data.trainingClassList.total;
}
this.dataListLoading = false;
});
},
//设置开关 修改数据
changeSingupFlag(data){
this.editCate(data, '1');
},
//上下架开关
changeDisplayFlag(data){
this.editCate(data, '1');
},
//新增
addOrUpdateHandle(){
this.addOrUpdateVisible = true;
this.statusType = 0; //新增
this.titlesub = '新增';
this.addForm = {
title: "", //标题
type: 1, //1线上 2线下
icon: '',
year: '',
trainingDate: '',
endDate: '',
fee: '',
vipType: '',
vipFee: '',
svipType: '',
appType: '',
svipFee: '',
threeHuFee: '',
fiveHuFee: '',
singupFlag: 1, //1可报名 0不可报名
displayFlag: 0,
sort: ''
}
this.fileList = [];
this.vipType = [];
this.svipType = [];
this.appType = [];
this.$nextTick(() => {
this.$refs.addFormRef.clearValidate();
});
},
//点击确定
addOrEditCate(){
if(this.statusType==0){ //如果是新增
this.addCate();
}else if(this.statusType==1){ //如果是修改
this.editCate(this.addForm, '0');
}
},
//取消
cancleClose(){
this.addOrUpdateVisible = false;
},
//修改
editCate(data, type){
if(type=='0'){
this.$refs["addFormRef"].validate(valid => {
if (valid) {
this.submitData(data);
}
})
}else{
this.submitData(data);
}
},
//修改
submitData(data){
var icon = '';
var vipType = '';
var svipType = '';
var appType = '';
if(this.fileList&&this.fileList.length>0){
icon = this.fileList[0].url
}
console.log('vipType-submit', this.vipType)
console.log('svipType-submit', this.svipType)
console.log('appType-submit', this.appType)
if(this.vipType&&this.vipType.length>0){
vipType = String(this.vipType);
}
if(this.svipType&&this.svipType.length>0){
svipType = String(this.svipType);
}
if(this.appType&&this.appType.length>0){
appType = String(this.appType);
}
this.$http({
url: this.$http.adornUrl("/master/trainingClass/editTrainingClass"),
method: "post",
data: this.$http.adornData({
id: data.id,
title: data.title,
type: data.type,
year: data.year,
trainingDate: data.trainingDate,
endDate: data.endDate,
singupFlag: data.singupFlag,
//displayFlag: data.displayFlag,
icon: icon,
fee: data.fee,
vipType: vipType,
vipFee: data.vipFee,
svipType: svipType,
displayApp: appType,
svipFee: data.svipFee,
threeHuFee: data.threeHuFee,
fiveHuFee: data.fiveHuFee,
sort: data.sort
}),
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
message: "操作成功",
type: "success"
});
this.addOrUpdateVisible = false;
this.getDataList();
}
});
},
// 每页数
sizeChangeHandle(val) {
this.pageSize = val;
this.pageIndex = 1;
this.getDataList();
},
// 当前页
currentChangeHandle(val) {
this.pageIndex = val;
this.getDataList();
},
//新增
addCate(){
this.$refs["addFormRef"].validate(valid => {
if (valid) {
var icon = '';
var vipType = '';
var svipType = '';
var appType = '';
if(this.fileList&&this.fileList.length>0){
icon = this.fileList[0].url
}
if(this.vipType&&this.vipType.length>0){
vipType = String(this.vipType);
}
if(this.svipType&&this.svipType.length>0){
svipType = String(this.svipType);
}
if(this.appType&&this.appType.length>0){
appType = String(this.appType);
}
this.$http({
url: this.$http.adornUrl("/master/trainingClass/addTrainingClass"),
method: "post",
data: this.$http.adornData({
id: this.addForm.id,
title: this.addForm.title,
type: String(this.addForm.type),
trainingDate: this.addForm.trainingDate,
endDate: this.addForm.endDate,
singupFlag: String(this.addForm.singupFlag),
//displayFlag: String(this.addForm.displayFlag),
icon: icon,
fee: this.addForm.fee,
vipType: vipType,
vipFee: this.addForm.vipFee,
svipType: svipType,
displayApp: appType,
svipFee: this.addForm.svipFee,
threeHuFee: this.addForm.threeHuFee,
fiveHuFee: this.addForm.fiveHuFee,
sort: this.addForm.sort
}),
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
message: "操作成功",
type: "success"
});
this.addOrUpdateVisible = false;
this.getDataList();
}
});
}
})
},
//操作
//点击修改
editHandle(data){
this.fileList = [];
this.addOrUpdateVisible = true;
this.titlesub = '修改';
this.statusType = 1; //修改
this.addForm.id = data.id;
this.addForm.title = data.title;
this.addForm.type = Number(data.type);
this.addForm.year = data.year;
this.addForm.trainingDate = data.trainingDate;
this.addForm.endDate = data.endDate;
this.addForm.singupFlag = Number(data.singupFlag);
//this.addForm.displayFlag = Number(data.displayFlag);
this.addForm.icon = data.icon;
//图片赋值
if(data.icon){
this.fileList.push({
url: data.icon
});
}
this.addForm.fee = data.fee;
this.addForm.vipFee = data.vipFee;
if(data.vipType){
this.vipType = data.vipType.split(',').map(item => parseInt(item, 10));
}else{
this.vipType = [];
}
if(data.svipType){
this.svipType = data.svipType.split(',').map(item => parseInt(item, 10));
}else{
this.svipType = [];
}
if(data.displayApp){
this.appType = data.displayApp.split(',').map(item => item.trim());
}else{
this.appType = [];
}
this.addForm.svipFee = data.svipFee;
this.addForm.threeHuFee = data.threeHuFee;
this.addForm.fiveHuFee = data.fiveHuFee;
this.addForm.sort = data.sort;
this.$nextTick(() => {
this.$refs.addFormRef.clearValidate();
});
},
//上传图片
handlePreview(file) {
this.fileList = [];
this.addForm.icon = file.url;
this.fileList.push({
url: file.url
});
this.dialogVisible = true;
},
//图片上传成功
handleSuccess(res, file) {
if (res.msg == "success") {
this.fileList.push({
url: res.url
});
this.$message.success("图片上传成功");
} else {
this.$message.error("图片上传失败");
}
},
//图片删除操作
handleRemove(file, fileList) {
this.fileList = [];
},
//获取vip数据
getVipData(){
this.$http({
url: this.$http.adornUrl("/master/trainingClass/getVipBuyConfigList"),
method: "post",
data: this.$http.adornData({}),
}).then(({ data }) => {
if (data && data.code === 0) {
this.vipList = data.vipBuyConfigList.filter(item => item.type >= 4 && item.type <= 9);
this.svipList = data.vipBuyConfigList.filter(item => item.type === 1 || item.type === 2);
}
});
},
//勾选vip
handleCheckedVip(value){
console.log('vip的值', value)
},
//勾选svip
handleCheckedSvip(value){
console.log('svip的值', value)
},
//勾选app
handleCheckedApp(value){
console.log('app的值', value)
}
},
};
</script>
<style lang="less" >
::v-deep.custom-height {
height: 370px;
}
.avatar-uploader {
height: 1px;
}
.general_editor {
height: 420px;
width: 1060px !important;
}
.editor_form {
height: 500px;
}
.dialog_box {
.el-form-item__content {
width: calc(100% - 120px) !important;
}
.form_item {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.el-form-item {
width: 290px;
}
}
}
.checkbox-group .el-checkbox{
margin-left: 0;
margin-right: 20px;
.el-checkbox__label{
padding-left: 6px;
font-size: 13px;
}
}
.hufen_block{
display: flex;
align-items: center;
span{
width: 70px;
display: inline-block;
}
.el-input{
width: 90%;
line-height: 34px;
}
.el-input__inner{
width: 220px;
height: 30px;
line-height: 34px;
padding: 0 8px;
}
.el-input__inner::placeholder {
font-size: 12px;
}
}
.custom-flatpickr .el-range-input{
margin-left: 5px;
}
</style>

View File

@@ -0,0 +1,100 @@
<template>
<div class="mod-config">
<el-form
:inline="true"
:model="dataForm"
@keyup.enter.native="getDataList()"
>
<el-form-item>
<div class="block">
<el-date-picker
v-model="currentYear"
type="year"
format="yyyy"
value-format="yyyy" :picker-options="pickerOptions"
placeholder="选择年份"
@change="handleYearChange"
/>
</div>
</el-form-item>
<el-button
type="primary"
size="small"
@click="
handleYearChange();
"
>刷新</el-button
>
</el-form>
<FullYearCalendar :urlList="urlList" ref="refCalendar" :selectYear="Number(currentYear)" :marks="markedDates" v-if="currentYear" />
</div>
</template>
<script>
import FullYearCalendar from "./FullYearCalendarTrainingCourse.vue";
export default {
data() {
return {
urlList:{
list:'/master/statistics/getTrainingToUserMonthTotal',
export:'/master/statistics/getTrainingToUserMonthInfo',
},
currentYear: '',
dataForm:{},
pickerOptions: {
disabledDate(time) {
// 禁用今年以后的年份
return time.getFullYear() > new Date().getFullYear()
}
},
markedDates: {
"2019-01-01": "red",
"2019-02-14": "blue",
"2019-03-12": "yellow",
"2019-05-20": "blue",
"2019-06-10": "yellow"
}
};
},
components: { FullYearCalendar },
activated() {
this.currentYear = new Date().getFullYear().toString();
this.handleYearChange();
},
methods: {
handleYearChange() {
this.$nextTick(() => {
this.$refs.refCalendar.init();
})
},
}
};
</script>
<style scoped>
/deep/ .mod-config .el-table .el-table__cell {
padding: 4px 0 !important;
box-sizing: border-box;
}
/deep/ .mod-config .el-table .cell,
.el-table th div {
padding: 0 !important;
box-sizing: border-box;
}
/deep/ .el-form .el-form-item{
margin-bottom:10px !important;
}
</style>

View File

@@ -0,0 +1,90 @@
<template>
<div>
<el-autocomplete
v-model.trim="name"
clearable
:fetch-suggestions="querySearch"
:placeholder="placeholder"
@select="handleSelectSearch"
@clear="clearSearch"
/>
</div>
</template>
<script>
import { debounce, throttle } from '@/common/js/debounce';
export default {
name: "MyElAutocomplete",
props: {
placeholder: {
type: String,
default: "请输入项目名称",
},
},
data() {
return {
name: "",
names: [],
};
},
created() {
// 初始化数据
this.name = "";
this.names = this.loadAll();
},
methods: {
clearSearch(val) {
this.$emit("restCode", val);
},
// 使用 throttle 包装 querySearch 方法
querySearch: throttle(async function (queryString, cb) {
await this.loadAll(queryString).then((names) => {
const results = names;
cb(results);
});
}, 100),
async loadAll(queryString = null) {
try {
await this.$http({
url: this.$http.adornUrl("/master/trainingClass/getUserInfoByTelEmail"),
method: "post",
data: this.$http.adornData({
content:this.name
})
}).then(res => {
console.log('res at line 55:', res)
if (res && res.data.code === 0) {
this.names = res.data.userList.records.map((item) => {
return {
value: item.tel||item.email,
tel: item.tel,
email: item.email,
name: item.name,
nickname: item.nickname,
id: item.id,
};
});
console.log('this.names at line 57:', this.names)
return this.names;
}
})
} catch (err) {
this.names = [];
}
return this.names;
},
handleSelectSearch(item) {
console.log("这是变化的值", item);
this.$emit("model", item);
},
},
};
</script>

View File

@@ -7,11 +7,7 @@
style="position: relative;"
>
<el-form-item label="手机号">
<el-input
v-model="dataForm.tel"
placeholder="请输入手机号"
clearable
>
<el-input v-model="dataForm.tel" placeholder="请输入手机号" clearable>
</el-input>
</el-form-item>
<el-form-item>
@@ -22,17 +18,12 @@
"
>查询</el-button
>
<el-button
type="primary"
@click="addHandle()"
>报名</el-button
>
<el-button type="primary" @click="addHandle()">报名</el-button>
</el-form-item>
<el-form-item style="position: absolute; right: 0; top: 0; margin-right: 0;">
<el-button
type="primary"
@click="handleExport()">
导出</el-button>
<el-form-item
style="position: absolute; right: 0; top: 0; margin-right: 0;"
>
<el-button type="primary" @click="handleExport()"> 导出</el-button>
</el-form-item>
</el-form>
<el-table
@@ -42,42 +33,69 @@
style="width: 100%"
@sort-change="handleSortChange"
>
<el-table-column label="序号" type="index" align="center" width="60">
</el-table-column>
<el-table-column
label="序号"
type="index"
label="报名时间"
align="center"
width="60">
prop="createTime"
width="170"
sortable="custom"
:sort-orders="['ascending', 'descending']"
>
</el-table-column>
<el-table-column label="报名时间" align="center" prop="createTime" width="170" sortable="custom" :sort-orders="['ascending', 'descending']">
</el-table-column>
<el-table-column label="订单编号" align="center" prop="orderSn"></el-table-column>
<el-table-column label="培训姓名" align="center">
<el-table-column label="用户信息" align="left" width="200">
<template slot-scope="scope">
<span>{{ scope.row.user.name ? scope.row.user.name : "暂无姓名" }}</span>
<p style="margin: 0;">
姓名
<span style="color: #333;font-weight: 700;">{{
scope.row.user.name ? scope.row.user.name : "暂无姓名"
}}</span>
</p>
<p style="margin: 0;">
身份:
<span
:style="{
color: scope.row.identity == '普通' ? '#888' : '#006699'
}"
>{{ scope.row.identity || "-" }}</span
>
</p>
</template>
</el-table-column>
<el-table-column label="联系方式" align="center">
<template slot-scope="scope">{{ scope.row.user.tel||'-' }}</template>
<el-table-column label="联系方式" align="center" width="160">
<template slot-scope="scope">{{ scope.row.user.tel || "-" }}</template>
</el-table-column>
<el-table-column label="用户身份" align="center">
<el-table-column label="支付方式" align="center" width="120">
<template slot-scope="scope">
{{ splitIdentity(scope.row.identity).type }}
</template>
</el-table-column>
<el-table-column label="费用" align="center">
<template slot-scope="scope">
{{ splitIdentity(scope.row.identity).price ? `¥${splitIdentity(scope.row.identity).price}` : '' }}
{{ scope.row.payType }}
</template>
</el-table-column>
<el-table-column label="支付方式" align="center">
<template slot-scope="scope">{{ scope.row.payMethod }}{{ scope.row.realMoney }}</template>
<el-table-column label="金额/海外金额" align="center" width="120">
<template slot-scope="scope">
<span v-if="scope.row.payType != '海外'">
{{ scope.row.fee ? `${scope.row.fee}` : "0" }}</span
>
<span v-else>
{{ scope.row.abroadFee ? `${scope.row.abroadFee}` : "0" }}</span
>
</template>
</el-table-column>
<el-table-column label="积分抵扣" align="center">
<template slot-scope="scope">积分{{ scope.row.jfDeduction }}</template>
<el-table-column label="积分抵扣" align="center" width="120">
<template slot-scope="scope">{{ scope.row.jf }}</template>
</el-table-column>
<el-table-column
label="交易流水号"
align="center"
prop="orderSn"
></el-table-column>
<el-table-column
fixed="right"
width="80"
header-align="center"
align="center"
label="操作"
@@ -86,6 +104,7 @@
<el-button
type="text"
size="small"
style="color: #FF4D4F;"
@click="deleteHandle(scope.row)"
>删除</el-button
>
@@ -109,25 +128,196 @@
title="添加用户"
class="dialog_box"
@close="cancleClose"
width="600px"
append-to-body
width="880px"
>
<el-form
:model="addForm"
ref="addFormRef"
:rules="addFormRule"
label-width="100px"
label-width="140px"
>
<el-form-item label="手机号/邮箱" prop="userKey">
<MyElAutocomplete v-if="addVisible"
ref="myelautocomplete"
:placeholder="'请输入内容'"
@model="handValue"
@restCode="resetCode"
/>
<!-- <el-autocomplete
v-model="addForm.userKey"
:fetch-suggestions="querySearchAsync"
placeholder="请输入内容"
@select="handleSelect"
></el-autocomplete> -->
<!-- <el-input
v-model="addForm.userKey"
placeholder="请输入手机号/邮箱"
clearable
@input="handleSearch"
style="width: 700px"
></el-input> -->
<div
class="data-list"
v-if="showSearch"
v-loading="searchListLoading"
>
<span
v-for="(v, i) in searchList"
:key="i"
@click="checkTitle(v)"
>{{ v.tel }}</span
>
<el-form-item label="手机号/邮箱" prop="userKey" style="width: 530px;">
<el-input v-model="addForm.userKey" placeholder="请输入手机号/邮箱" clearable @input="handleSearch"></el-input>
<div class="data-list" v-if="showSearch" v-loading="searchListLoading">
<span v-for="(v,i) in searchList" :key="i" @click="checkTitle(v)">{{ v.tel }}</span>
</div>
</el-form-item>
<el-form-item label="用户身份" v-if="infoStatus">
{{ info.identity }}
<el-form-item label="用户姓名" v-if="infoStatus" class="edit-form-item">
<div style="display: flex;align-items: center;width: 700px">
<div class="content-wrapper" v-if="!isEditing">
<span v-if="addForm.name">{{ addForm.name }}</span>
<span v-if="!addForm.name">暂无姓名</span>
</div>
<!-- 编辑模式输入框 -->
<div
class="edit-wrapper"
style="display: flex;align-items: center;max-width: 700px"
>
<el-input
v-if="isEditing"
v-model="addForm.name"
placeholder="请输入真实姓名"
style="width: 400px; "
@blur="handleSave"
></el-input>
<div class="button-group" style="width: 180px;margin-left: 20px;">
<el-button
size="small"
v-if="!isEditing"
type="text"
@click="handleEdit"
>
编辑
</el-button>
<template v-if="isEditing">
<el-button size="small" type="primary" @click="handleSave">
保存
</el-button>
<el-button
size="small"
type="text"
@click="handleCancel"
style="margin-left: 10px;color: #888;"
>
取消
</el-button>
</template>
</div>
</div>
</div>
<!-- 显示模式内容 -->
<!-- 操作按钮组 -->
</el-form-item>
<el-form-item label="最终价格" v-if="infoStatus">
{{ info.fee }}
<el-form-item label="用户身份" class="coin_block" v-if="infoStatus">
<div style="width: 700px">
<el-radio-group v-model="addForm.identity" style="width: 700px;" >
<el-radio :label="v" v-for="(v,i) in identityList" :key="i">{{ v }}</el-radio>
<!--
<el-radio label="大学生">大学生</el-radio> -->
</el-radio-group>
</div>
</el-form-item>
<el-form-item label="支付方式" class="coin_block" v-if="infoStatus">
<div style="width: 700px">
<el-radio-group
v-model="addForm.payType"
style="width: 700px;"
@input="handlePayMethod"
>
<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="infoStatus && addForm.payType != '赠送'"
>
<el-input
style="width: 700px"
v-model="addForm.orderSn"
placeholder="请输入交易流水号"
>
</el-input>
</el-form-item>
<el-form-item
label="海外金额"
v-if="infoStatus && addForm.payType == '海外'"
>
<el-input
style="width: 700px"
v-model="addForm.abroadFee"
placeholder="请输入海外金额"
>
</el-input>
</el-form-item>
<el-form-item label="金额" v-if="infoStatus">
<el-input
style="width: 700px"
placeholder="请输入金额"
v-model="addForm.fee"
clearable
>
</el-input>
</el-form-item>
<!-- <el-form-item label="天医币" v-if="infoStatus">
<el-input style="width: 700px"
placeholder="请输入天医币"
v-model="addForm.fee"
clearable>
</el-input>
</el-form-item> -->
<el-form-item label="积分" v-if="infoStatus">
<el-input
style="width: 700px"
placeholder="请输入积分"
v-model="addForm.jf"
clearable
>
</el-input>
</el-form-item>
<el-form-item label="报名时间" class="coin_block" v-if="infoStatus">
<div>
<el-date-picker
size="small"
v-model="addForm.createTime"
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="备注" v-if="infoStatus">
<el-input
style="width: 700px"
type="textarea"
rows="5"
v-model="addForm.remark"
placeholder="请输入备注"
>
</el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
@@ -138,25 +328,53 @@
</div>
</template>
<script>
import MyElAutocomplete from "./MyElAutocomplete";
export default {
data() {
return {
restaurants: [],
identityList: [],
state: "",
timeout: null,
isEditing: false, // 编辑状态标记
// 暂存原始数据用于取消编辑时恢复
originalData: {
name: ""
},
options: [
{
value: "普通",
label: "普通"
},
{
value: "大学生",
label: "大学生"
}
],
trainingId: null,
trainingTitle: '',
trainingTitle: "",
dataForm: {
tel: ''
tel: ""
},
addForm: {
userId: '',
userKey: ''
userId: "",
userKey: "",
name: "",
identity: "",
createTime: "",
abroadFee: 0,
fee: 0,
jf: 0
},
editId: "",
addFormRule: {
userKey: [
{
required: true,
message: "请输入手机号/邮箱",
},
message: "请输入手机号/邮箱"
}
]
},
dataList: [],
@@ -169,37 +387,127 @@ export default {
//新增-模糊查询
showSearch: false,
searchList: [],
sortParams: { // 排序参数
field: 'date', // 默认排序字段
order: 'desc' // 默认降序
sortParams: {
// 排序参数
field: "date", // 默认排序字段
order: "desc" // 默认降序
},
//获取身份和价格
infoStatus: false,
info: {},
//日期排序
sortParams: {
date: ''
date: ""
}
};
},
activated(){
components: {
MyElAutocomplete
},
async created() {
this.identityList = [];
// this.restaurants =await this.loadAll();
console.log('this.restaurants at line 389:', this.restaurants)
},
async activated() {
this.identityList = [];
// this.restaurants =await this.loadAll();
console.log('this.restaurants at line 389:', this.restaurants)
this.trainingId = this.$route.query.id;
this.trainingTitle = this.$route.query.title;
this.getDataList();
},
methods: {
handValue(val) {
console.log(val,'这是点击的');
this.checkTitle(val)
},
resetCode(code){
this.addForm={
userId: "",
userKey: "",
name: "",
identity: "",
createTime: "",
abroadFee: 0,
fee: 0,
jf: 0
}
this.identityList = [];
this.infoStatus = false;
},
// 进入编辑模式
handleEdit() {
// 保存当前数据快照
this.originalData = {
name: this.addForm.name
};
this.isEditing = true;
},
// 取消编辑
handleCancel() {
// 恢复原始数据
this.addForm.name = this.originalData.name;
this.isEditing = false;
},
// 保存编辑内容
async handleSave() {
if (!this.addForm.name) {
return;
}
if (this.addForm.name == this.originalData.name) {
return false;
}
try {
// 调用保存接口
const response = await this.$http({
url: this.$http.adornUrl(
"/master/trainingClass/trainingClassUpdateUserName"
),
method: "post",
data: this.$http.adornData({
userId: this.addForm.userId,
name: this.addForm.name
})
});
console.log("response.success at line 345:", response);
if (response.data.code == 0) {
this.$message.success("保存成功");
this.isEditing = false;
} else {
this.$message.error(response.message || "保存失败");
}
} catch (error) {
console.error("保存用户信息失败:", error);
}
},
handlePayMethod(data) {
console.log("data at line 254:", data);
if (this.addForm.abroadFee) {
this.addForm.abroadFee = 0;
}
if (this.addForm.orderSn) {
this.addForm.orderSn = "";
}
},
splitIdentity(identity) {
const match = identity.match(/([^\d]+)()([\d.]+)/);
return {
type: match ? match[1].trim() : identity,
price: match ? match[3] : ''
price: match ? match[3] : ""
};
},
//排序变化事件
handleSortChange({ column, prop, order }) {
this.sortParams.date = order
? (order === 'ascending' ? '2' : '1')
: null;
this.sortParams.date = order ? (order === "ascending" ? "2" : "1") : null;
this.getDataList();
},
// 获取数据列表
@@ -214,7 +522,7 @@ export default {
trainingId: this.trainingId,
tel: this.dataForm.tel,
createTimeSort: this.sortParams.date
}),
})
}).then(({ data }) => {
if (data && data.code === 0) {
this.dataList = data.trainingClassUserList.records;
@@ -223,16 +531,35 @@ export default {
this.dataListLoading = false;
});
},
formatDateTime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
//添加用户
addHandle() {
this.addVisible = true;
this.$refs["addFormRef"].resetFields();
this.addForm = {
userId:'',
userKey: ''
}
this.searchListLoading = false;
this.searchList = [];
this.infoStatus = false;
this.info = {};
this.addForm={}
this.addForm = {
userId: "",
userKey: "",
name: "",
identity: "",
abroadFee: 0,
fee: 0,
jf: 0,
createTime: this.formatDateTime(new Date())
};
console.log("at line 472:", this.addForm);
this.addVisible = true;
this.$refs["addFormRef"].resetFields();
},
//取消
cancleClose() {
@@ -249,13 +576,17 @@ export default {
this.pageIndex = val;
this.getDataList();
},
//模糊查询
handleSearch(val) {
if (val) {
this.showSearch = true;
this.searchListLoading = true;
this.infoStatus = false;
this.info = {}
this.info = {};
this.$http({
url: this.$http.adornUrl("/book/user/getUserList"),
method: "post",
@@ -275,17 +606,22 @@ export default {
}
});
} else {
console.log('输入框已被清空');
console.log("输入框已被清空");
this.infoStatus = false;
this.info = {}
this.info = {};
}
},
//选中模糊值
checkTitle(v) {
this.addForm.userId = v.id;
this.addForm.userKey = v.tel;
this.addForm.nickname = v.nickname;
this.addForm.name = v.name;
this.addForm.userKey = v.tel||v.email;
this.showSearch = false;
this.$refs.addFormRef.clearValidate("userKey");
this.getFinalPriceByUser();
},
//获取角色和价格
getFinalPriceByUser() {
@@ -295,12 +631,15 @@ export default {
data: this.$http.adornData({
userId: this.addForm.userId,
classId: Number(this.trainingId)
}),
})
}).then(({ data }) => {
if (data && data.code === 0) {
if (data.info) {
this.infoStatus = true;
this.infoStatus = true;this.identityList=[data.info.identity,"大学生"]
this.info = data.info;
this.addForm.identity = data.info.identity;
this.addForm.fee = data.info.fee;
} else {
this.infoStatus = false;
}
@@ -311,16 +650,27 @@ export default {
},
//添加用户
addCate() {
console.log("this.addForm at line 406:", this.addForm);
this.$refs["addFormRef"].validate(valid => {
if (valid) {
this.$http({
url: this.$http.adornUrl("/master/trainingClass/addUserToTrainingClass"),
url: this.$http.adornUrl(
"/master/trainingClass/addUserToTrainingClass"
),
method: "post",
data: this.$http.adornData({
userId: this.addForm.userId,
trainingId: this.trainingId,
identity: this.info.identity+''+this.info.fee
}),
orderSn: this.addForm.orderSn, //订单号
identity: this.addForm.identity, //身份
payType: this.addForm.payType, //支付方式微信支付宝天医币银行海外赠送
fee: this.addForm.fee, //金额
jf: this.addForm.jf, //积分
remark: this.addForm.remark, //备注
abroadFee: this.addForm.abroadFee, //海外金额
createTime: this.addForm.createTime //创建时间
})
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
@@ -335,7 +685,7 @@ export default {
}
});
}
})
});
},
//删除
deleteHandle(row) {
@@ -346,11 +696,13 @@ export default {
})
.then(() => {
this.$http({
url: this.$http.adornUrl("/master/trainingClass/delUserToTrainingClass"),
url: this.$http.adornUrl(
"/master/trainingClass/delUserToTrainingClass"
),
method: "post",
data: this.$http.adornData({
id: row.id
}),
})
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
@@ -369,7 +721,9 @@ export default {
this.dataListLoading = true;
try {
this.$http({
url: this.$http.adornUrl("/master/trainingClass/exportTrainingClassUser"),
url: this.$http.adornUrl(
"/master/trainingClass/exportTrainingClassUser"
),
method: "post",
data: this.$http.adornData({
trainingId: this.trainingId,
@@ -385,7 +739,7 @@ export default {
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = '['+ this.trainingTitle +']-用户数据文件';
link.download = "[" + this.trainingTitle + "]-用户数据文件";
link.click();
window.URL.revokeObjectURL(link.href); // 释放内存
@@ -396,14 +750,13 @@ export default {
duration: 3000,
showClose: true
});
});
} catch (err) {
this.$message.error("文件下载失败!");
this.dataListLoading = true;
}
},
},
}
}
};
</script>
@@ -439,7 +792,7 @@ export default {
.data-list {
min-height: 200px;
width: 100%;
width: 700px;
border-radius: 4px;
background: #fff;
position: absolute;

View File

@@ -36,11 +36,12 @@
<el-form-item label="支付方式" class="coin_block" prop="payType">
<div>
<div style="width: 700px">
<el-radio-group v-model="dataForm.payType" >
<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>

View File

@@ -552,7 +552,7 @@
<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>

View File

@@ -222,6 +222,7 @@
<el-radio-group v-model="youForm.payType" >
<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>

View File

@@ -342,7 +342,7 @@ export default {
this.$message.error("请输入交易流水号");
return;
}
if (this.dataForm.payTime == "") {
if (this.dataForm.payTime == "" || this.dataForm.payTime == null) {
this.$message.error("请选择付款时间");
return;
}

View File

@@ -592,7 +592,7 @@ export default {
this.$message.error("请输入交易流水号");
return;
}
if (this.dataForm.payTime == "") {
if (this.dataForm.payTime == "" || this.dataForm.payTime == null) {
this.$message.error("请选择付款时间");
return;
}
@@ -715,6 +715,7 @@ if (this.dataForm.type == "master") {
this.tracesShow = true;
},
handleEdit(item, data) {
console.log('item at line 717:', item)
this.editType = "edit";
console.log("data at line 263:", data);
@@ -734,8 +735,9 @@ if (this.dataForm.type == "master") {
};
this.dataForm = {
...item,
jf: this.dataForm.jf || 0,
fee: this.dataForm.fee || 0
jf: item.jf || 0,
fee: item.fee || 0,
};
this.tracesShow = true;
},

View File

@@ -5,8 +5,8 @@
window.SITE_CONFIG = {};
// api接口请求地址
window.SITE_CONFIG['baseUrl'] = 'https://api.nuttyreading.com'; // 线上正式环境
// window.SITE_CONFIG['baseUrl'] = 'http://192.168.110.100:9200/pb'; //川
// window.SITE_CONFIG['baseUrl'] = 'https://api.nuttyreading.com'; // 线上正式环境
window.SITE_CONFIG['baseUrl'] = 'http://192.168.110.100:9200/pb'; //川
// cdn地址 = 域名 + 版本号
window.SITE_CONFIG['domain'] = './'; // 域名