Files
nuttyreading-master-html/src/views/modules/reportList/FullYearCalendar.vue
2025-09-19 17:20:09 +08:00

356 lines
11 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>
<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>