356 lines
11 KiB
Vue
356 lines
11 KiB
Vue
<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}} 年全部VIP报表</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" style="margin-bottom: 15px;">
|
||
<!-- <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" :key="d" class="day header" style="margin-bottom: 10px;">{{ d.title }}</div>
|
||
|
||
|
||
<div v-for="d in list" :key="d" 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>
|
||
</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); // 例如 ["01","02",...,"09"]
|
||
|
||
this.$message({
|
||
message: "所有月份的VIP报表正在下载,请耐心等待...",
|
||
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 = `VIP报表_${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 = `VIP报表_${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: "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: "VIP报表文件下载完成,请注意查看!",
|
||
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;
|
||
}
|
||
|
||
</style>
|
||
|