You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1803 lines
46 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="page">
<view class="headcon" :style="{ paddingTop: geStatusBarHeight() + 'px'}">
<view class="hback">
<uni-icons type="back" size="24" color="#000" @tap="gotoBack"></uni-icons>
<text class="htxt">我的课程</text>
<view style="flex:1;"></view>
<view class="save-btn" @click="generateWeekScheduleImage" v-show="1==tabCurrentIndex0">
<uni-icons type="download" size="24" color="#00A99A"></uni-icons>
<text style="font-size:24rpx;color:#00A99A;margin-left:6rpx;">保存周课表</text>
</view>
<view class="tabtip" @click="openDialog">
<uni-icons class="rtop" type="info" size="21" color="#e96900"></uni-icons>
</view>
</view>
<scroll-view class="scrolllist0" scroll-with-animation>
<view class="tabicl" :class="0==tabCurrentIndex0?'cur':''" @tap="tabSelect0(0)">
<view class="ptext">日课表</view>
<view class="pline"></view>
</view>
<view class="tabicl" :class="1==tabCurrentIndex0?'cur':''" @tap="tabSelect0(1)">
<view class="ptext">周课表</view>
<view class="pline"></view>
</view>
</scroll-view>
</view>
<view v-show="0==tabCurrentIndex0">
<scroll-view class="scrolllist" scroll-x="true" scroll-with-animation :scroll-left="scrollLeft">
<view class="tabcell" v-for="(item, index) in list" :key="index" :class="index==tabCurrentIndex?'cur':''" @tap="tabSelect" :data-id="index">
<view class="wtxt">{{item.week}}</view>
<view class="dtxt">{{item.date}}</view>
</view>
</scroll-view>
<swiper class="swiper-box" :duration="300" :current="tabCurrentIndex" @animationfinish="animationfinish">
<swiper-item class="swiper-item" v-for="tabItem in list" :key="tabItem.id">
<scroll-view scroll-y style="height: 100%;width: 100%;">
<view class="scell" v-if="tabItem.slist.length>0">
<view v-for="(item, index) in tabItem.slist" :key="index" class="oitem">
<view class="otop">
<view class="hleft">
{{item.storeName||'-'}}
</view>
<view class="hright">
{{formatTime(item.startTime)+' - '+formatTime(item.endTime)}}
</view>
</view>
<view class="oscon">
<view class="osimg imgload">
<image v-if="item.pic" :src="getimgRemoteFile(item.pic)" mode="aspectFill"></image>
</view>
<view class="osname">
<view class="oscell">
<!-- <view v-if="item.claName=='团课'" class="txtbg">{{item.claName}}</view>
<view v-else class="txtbg1">{{item.claName}}</view> -->
<!-- <view v-if="item.claName" class="txtbg1">{{item.claName}}</view>-->
<uni-rate :size="15" :readonly="true" :value="item.star?item.star:0" :max="5"/>
</view>
<view class="oscell" v-if="item.storeName">
<uni-icons type="paperclip" size="18" color="#00A99A"></uni-icons>
<view class="ntip">{{item.courseName}}</view>
</view>
<view class="oscell">
<uni-icons type="calendar" size="18" color="#00A99A"></uni-icons>
<view class="ntip">{{item.claDate}}</view>
<view class="txt">{{item.weekDay }}</view>
</view>
<view class="oscell">
<uni-icons type="person" size="18" color="#666"></uni-icons>
<view class="ntxt">{{item.staffName}}</view>
<!-- <view class="txt">{{item.teacherposition }}</view> -->
</view>
<view class="oscell">
<uni-icons type="map-pin-ellipse" size="18" color="#00A99A"></uni-icons>
<view class="ntxt">{{item.roomName}}</view>
</view>
<view class="oscell">
<uni-icons type="checkbox" size="18" color="#00A99A"></uni-icons>
<view class="ntxt">已预约{{item.bookAttendCnt}}人/限{{item.atClassCnt}}人</view>
</view>
<view class="oscell">
<view class="txt" style="margin-left: 46rpx;">满{{item.lessCnt}}人开课</view>
</view>
</view>
<view class="rcpm">
<button type="primary" class="orderbtn" @click="gotoorder(item)">预约</button>
<!-- <button type="primary" class="sharebtn" @click="gotoshare(item)">分享</button>-->
</view>
</view>
</view>
</view>
<view v-if="tabItem.slist.length==0 && tabItem.loadStatus=='noMore'" class="nodata"></view>
<uni-load-more v-if="tabItem.slist.length>0" iconType="circle" :status="tabItem.loadStatus" />
</scroll-view>
</swiper-item>
</swiper>
</view>
<view v-show="1==tabCurrentIndex0">
<uni-table class="courcon" ref="table" :loading="loading" emptyText="暂无更多数据">
<view class="table_head">
<uni-tr>
<uni-th width="50" align="center" class="leftfix">
<view class="leftcell">
<view class="ctitle">日期</view>
<view class="ctitle">15日内</view>
</view>
</uni-th>
<uni-th width="80" align="center" v-for="(item, index) in columnTitles" :key="index">
<view class="rightcell">
<view class="ctitle">{{item.day?item.day.substring(5):""}}</view>
<view class="ctitle">{{item.weekName}}</view>
<view class="cnum" v-if="item.count>0">{{item.count}}</view>
</view>
</uni-th>
</uni-tr>
</view>
<view class="table_body">
<uni-tr v-for="(item, index) in claTimeContainer" :key="index">
<uni-td width="50" align="center" class="leftfix">
<view class="courfix">
{{item.time}}
</view>
</uni-td>
<uni-td width="80" align="center" v-for="(cell, ii) in columnTitles" :key="ii">
<block v-for="(cc, kk) in item.claTimeWeekDayMap" :key="kk">
<block v-for="(dd, mm) in cc" :key="mm">
<!-- :style="{color:colorlist[index].fcolor,backgroundColor:colorlist[index].color,borderLeftColor:colorlist[index].fcolor}" -->
<view class="courheadtext" v-if="dd.claDate==cell.day"
:style="{
fontSize:(dd.fontSize?dd.fontSize+'px':'26rpx'),
color: (dd.fontColor?dd.fontColor:'#000'),
backgroundColor: ( dd.claColor?dd.claColor:colorlist[index].color),
borderLeftColor: '#fef2ce'
}">
<!-- (dd.fontColor?dd.fontColor:'#000') -->
<view class="ctxt">{{dd.startTime}}-{{dd.endTime}}</view>
<view class="ctxt" style="font-weight: 600;">{{dd.courseName}}</view>
<view class="ctxt">{{dd.claName}}</view>
<view class="ctxt" v-if="dd.staffName">{{dd.staffName}}</view>
<view class="ctxt" v-if="dd.roomName">{{dd.roomName}}</view>
<button v-if="dd.claTimeStatus=='1'" type="primary" class="orderbtn" style="width: 80%;" @click="gotoorder(dd)">预约</button>
<!-- <view v-else class="noorderbtn">不可预约</view>-->
</view>
</block>
</block>
</uni-td>
</uni-tr>
</view>
</uni-table>
</view>
<!-- 购买须知 -->
<uni-popup ref="pfDialog" :mask-click="false">
<view class="pcon">
<view class="phtxt">购买须知</view>
<view class="ptxt">
①购买后即可自动预约本次课程。<br>
②购买后不可退款,若开课前未能履约,可在自行取消后预约其它课程。<br>
③若开课前预约人数未达到最低人数,系统将自动取消课程,并返还费用到你的储值余额中,你可继续预约其它课程。
</view>
<view class="btn" @click="closeDialog">
关闭
</view>
</view>
</uni-popup>
<!-- 是否登录 -->
<openlogin ref="loginId" @getPhoneNumber="getPhoneNumber"></openlogin>
<canvas canvas-id="weekCanvas" id="weekCanvas" :style="{position:'fixed',left:'-9999px',top:'0',width:canvasWidth+'px',height:canvasHeight+'px',pointerEvents:'none'}"></canvas>
</view>
</template>
<script>
import { myCache,getRemoteFile } from '../../utils/utils.js';
import openlogin from "../components/openlogin.vue";
export default {
components: {
openlogin
},
data() {
return {
canvasWidth: 1, // 新增初始非0避免canvas无效
canvasHeight: 1, // 新增
previewImagePath: '', // 预览图片临时路径
openId:"",
phone:"",
userid:"",
tabCurrentIndex0: 0,
tabCurrentIndex: 0,
scrollLeft: 0,//tabs
list:[],
loading:false,
colorlist:[
{
time:'08:30',
fcolor:'#ce4b62',
color:'#ffdbdf'
},
{
time:'10:00',
fcolor:'#0c7e07',
color:'#f4fff3'
},
{
time:'11:00',
fcolor:'#b871d7',
color:'#faebfe'
},
{
time:'14:00',
fcolor:'#95440f',
color:'#ffe9cb'
},
{
time:'15:00',
fcolor:'#116c94',
color:'#cbefff'
},
{
time:'16:00',
fcolor:'#867ae0',
color:'#f4f0ff'
},
{
time:'17:00',
fcolor:'#37acc0',
color:'#e0f8fa'
},
{
time:'18:00',
fcolor:'#e8739d',
color:'#feecfa'
},
{
time:'19:00',
fcolor:'#4d8ae3',
color:'#eaf0ff'
},
{
time:'20:00',
fcolor:'#f2960f',
color:'#fbeacc'
}
],
columnTitles: [
{
"weekName": "星期二",
"day": "08-19",
"count": 1
},
{
"weekName": "星期三",
"day": "08-20",
"count": 3
},
{
"weekName": "星期四",
"day": "08-21",
"count": 0
},
{
"weekName": "星期五",
"day": "08-22",
"count": 1
},
{
"weekName": "星期六",
"day": "08-23",
"count": 1
},
{
"weekName": "星期日",
"day": "08-24",
"count": 2
},
{
"weekName": "星期一",
"day": "08-25",
"count": 2
},
{
"weekName": "星期二",
"day": "08-26",
"count": 1
},
{
"weekName": "星期三",
"day": "08-27",
"count": 3
},
{
"weekName": "星期四",
"day": "08-28",
"count": 0
},
{
"weekName": "星期五",
"day": "08-29",
"count": 1
},
{
"weekName": "星期六",
"day": "08-30",
"count": 1
},
{
"weekName": "星期日",
"day": "08-31",
"count": 2
},
{
"weekName": "星期一",
"day": "09-01",
"count": 2
}
],
claTimeContainer: [
{
"time": "08:30",
"claTimeWeekDayMap": {
"1": [
{
"courseTimeId": 1935590320385343489,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-08-24",
"weekDay": "星期日",
"startTime": "08:00",
"endTime": "09:30",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
},
{
"courseTimeId": 1935590320385343492,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-08-31",
"weekDay": "星期日",
"startTime": "08:00",
"endTime": "09:30",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
}
],
"2": [
{
"courseTimeId": 1935590320385343490,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-08-25",
"weekDay": "星期一",
"startTime": "08:00",
"endTime": "09:30",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
},
{
"courseTimeId": 1935590320385343493,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-09-01",
"weekDay": "星期一",
"startTime": "08:00",
"endTime": "09:30",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
}
],
"3": [],
"4": [
{
"courseTimeId": 1935590320322428930,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-08-20",
"weekDay": "星期三",
"startTime": "08:00",
"endTime": "09:30",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
},
{
"courseTimeId": 1935596230449086465,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-08-27",
"weekDay": "星期三",
"startTime": "08:00",
"endTime": "09:00",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
},
{
"courseTimeId": 1935590320385343491,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-09-03",
"weekDay": "星期三",
"startTime": "08:00",
"endTime": "09:30",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
}
],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": [],
"11": [],
"12": [],
"13": [],
"14": [],
"15": []
}
},
{
"time": "10:00",
"claTimeWeekDayMap": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": [],
"11": [],
"12": [],
"13": [],
"14": [],
"15": []
}
},
{
"time": "11:00",
"claTimeWeekDayMap": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": [],
"11": [],
"12": [],
"13": [],
"14": [],
"15": []
}
},
{
"time": "14:00",
"claTimeWeekDayMap": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": [],
"11": [],
"12": [],
"13": [],
"14": [],
"15": []
}
},
{
"time": "15:00",
"claTimeWeekDayMap": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": [],
"11": [],
"12": [],
"13": [],
"14": [],
"15": []
}
},
{
"time": "16:00",
"claTimeWeekDayMap": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": [],
"11": [],
"12": [],
"13": [],
"14": [],
"15": []
}
},
{
"time": "17:00",
"claTimeWeekDayMap": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": [],
"11": [],
"12": [],
"13": [],
"14": [],
"15": []
}
},
{
"time": "18:00",
"claTimeWeekDayMap": {
"1": [],
"2": [],
"3": [],
"4": [],
"5": [],
"6": [],
"7": [],
"8": [],
"9": [],
"10": [],
"11": [],
"12": [],
"13": [],
"14": [],
"15": []
}
},
{
"time": "19:30",
"claTimeWeekDayMap": {
"1": [],
"2": [],
"3": [
{
"courseTimeId": 1935590110594646018,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-08-19",
"weekDay": "星期二",
"startTime": "16:00",
"endTime": "17:30",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
}
],
"4": [],
"5": [],
"6": [
{
"courseTimeId": 1935590110657560578,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-08-29",
"weekDay": "星期五",
"startTime": "16:00",
"endTime": "17:30",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
}
],
"7": [
{
"courseTimeId": 1935590110657560579,
"claName": "普拉提A班",
"courseName": "普拉提",
"claDate": "2025-08-30",
"weekDay": "星期六",
"startTime": "16:00",
"endTime": "17:30",
"staffName": "教师A",
"studentCount": 0,
"roomName": null,
"claColor": "#409EFF",
"claTimeStatus": "1"
}
],
"8": [],
"9": [],
"10": [],
"11": [],
"12": [],
"13": [],
"14": [],
"15": []
}
}
],
};
},
onLoad(option) {
if(option.id||option.id==0){
if(parseInt(option.id)==0){
this.tabCurrentIndex=0;
}
else{
this.tabCurrentIndex=parseInt(option.id);
}
this.scrollLeft = (this.tabCurrentIndex - 1) * 60;
}
// 先获取周课表
this.loadweekcourse();
},
onShow(){
},
onReady(){
this.openId = myCache('openId');
var user = myCache('user');
this.userid = user.userid? user.userid:'';
this.phone = user.userphone;
if(this.userid==""||this.userid=="0"){
uni.navigateTo({
url: `/pages/login/login`
});
}
},
onPullDownRefresh() {
setTimeout(()=>{
uni.stopPullDownRefresh()
},100);
},
methods: {
generateWeekScheduleImage() {
if (!this.columnTitles || this.columnTitles.length === 0 || !this.claTimeContainer || this.claTimeContainer.length === 0) {
uni.showToast({ title: '暂无数据可生成图片', icon: 'none' });
return;
}
const padding = 10;
const rowHeaderWidth = 80;
const cellWidth = 120;
const cellHeight = 90;
const headerHeight = 60;
const cols = this.columnTitles.length;
const rows = this.claTimeContainer.length;
const canvasWidth = padding * 2 + rowHeaderWidth + cols * cellWidth;
const canvasHeight = padding * 2 + headerHeight + rows * cellHeight;
// 先更新 canvas 实际尺寸关键必须非0
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
uni.showLoading({ title: '生成中...' });
// 超时兜底8秒后强制关闭loading
const timeoutTimer = setTimeout(() => {
uni.hideLoading();
uni.showToast({ title: '生成超时,请重试', icon: 'none' });
}, 8000);
const clearTimer = () => clearTimeout(timeoutTimer);
// #ifdef H5
try {
const canvas = document.createElement('canvas');
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const ctx = canvas.getContext('2d');
if (!ctx) {
clearTimer();
uni.hideLoading();
uni.showToast({ title: '无法创建 Canvas 上下文', icon: 'none' });
return;
}
this.drawSchedule(ctx, canvasWidth, canvasHeight);
const dataURL = canvas.toDataURL('image/png');
clearTimer();
uni.hideLoading();
this.previewImagePath = dataURL;
this.$nextTick(() => {
this.$refs.previewPopup.open();
});
} catch (e) {
clearTimer();
uni.hideLoading();
console.error(e);
uni.showToast({ title: '生成失败', icon: 'none' });
}
// #endif
// #ifndef H5
// 等待 canvas 尺寸更新到 DOM 后再绘制
this.$nextTick(() => {
setTimeout(() => {
const ctx = uni.createCanvasContext('weekCanvas', this);
this.drawSchedule(ctx, canvasWidth, canvasHeight);
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'weekCanvas',
width: canvasWidth,
height: canvasHeight,
destWidth: canvasWidth * 2,
destHeight: canvasHeight * 2,
success: (res) => {
clearTimer();
uni.hideLoading();
this.previewImagePath = res.tempFilePath;
this.$nextTick(() => {
this.$refs.previewPopup.open();
});
this.saveImage();
},
fail: (err) => {
clearTimer();
uni.hideLoading();
console.error('canvasToTempFilePath fail:', err);
uni.showToast({ title: '生成失败:' + (err.errMsg || ''), icon: 'none' });
}
}, this);
});
}, 50);
});
// #endif
},
drawSchedule(ctx, canvasWidth, canvasHeight) {
// 判断是否为旧式 CanvasContext (uni-app 的 CanvasContext)
const isLegacy = typeof ctx.setFillStyle === 'function' && typeof ctx.fillStyle === 'undefined';
// 辅助函数,统一设置样式
const setFill = (color) => isLegacy ? ctx.setFillStyle(color) : (ctx.fillStyle = color);
const setStroke = (color) => isLegacy ? ctx.setStrokeStyle(color) : (ctx.strokeStyle = color);
const setFontSize = (size) => isLegacy ? ctx.setFontSize(size) : (ctx.font = size + 'px sans-serif');
const setTextAlign = (align) => isLegacy ? ctx.setTextAlign(align) : (ctx.textAlign = align);
const setTextBaseline = (baseline) => isLegacy ? ctx.setTextBaseline(baseline) : (ctx.textBaseline = baseline);
const padding = 10;
const rowHeaderWidth = 80;
const cellWidth = 120;
const cellHeight = 90;
const headerHeight = 60;
const cols = this.columnTitles.length;
const rows = this.claTimeContainer.length;
// 白色背景
setFill('#ffffff');
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// 绘制表头
setFill('#e4f9f7');
ctx.fillRect(padding, padding, rowHeaderWidth, headerHeight);
for (let i = 0; i < cols; i++) {
const col = this.columnTitles[i];
const x = padding + rowHeaderWidth + i * cellWidth;
setFill('#e4f9f7');
ctx.fillRect(x, padding, cellWidth, headerHeight);
setFill('#000000');
setFontSize(12);
setTextAlign('center');
setTextBaseline('middle');
ctx.fillText(col.day, x + cellWidth / 2, padding + headerHeight / 2 - 8);
ctx.fillText(col.weekName, x + cellWidth / 2, padding + headerHeight / 2 + 8);
if (col.count > 0) {
setFill('#ff0000');
setFontSize(10);
ctx.fillText(col.count, x + cellWidth - 20, padding + 12);
}
}
// 绘制每一行
for (let i = 0; i < rows; i++) {
const row = this.claTimeContainer[i];
const y = padding + headerHeight + i * cellHeight;
// 左侧时间
setFill('#F2F2F2');
ctx.fillRect(padding, y, rowHeaderWidth, cellHeight);
setFill('#000000');
setFontSize(12);
setTextAlign('center');
setTextBaseline('middle');
ctx.fillText(row.time, padding + rowHeaderWidth / 2, y + cellHeight / 2);
// 各列数据
for (let j = 0; j < cols; j++) {
const col = this.columnTitles[j];
const x = padding + rowHeaderWidth + j * cellWidth;
let course = null;
// 修复后:兼容 YYYY-MM-DD 与 MM-DD 两种格式
for (let key in row.claTimeWeekDayMap) {
const list = row.claTimeWeekDayMap[key];
if (list && list.length > 0) {
const claDate = list[0].claDate || '';
const day = col.day || '';
const matched = claDate === day ||
(day.length === 5 && claDate.length === 10 && claDate.slice(5) === day);
if (matched) {
course = list[0];
break;
}
}
}
if (course) {
setFill(course.claColor || '#409EFF');
ctx.fillRect(x, y, cellWidth, cellHeight);
setFill('#000000');
setFontSize(10);
setTextAlign('center');
setTextBaseline('middle');
let textY = y + 12;
ctx.fillText(course.startTime + '-' + course.endTime, x + cellWidth / 2, textY);
textY += 16;
ctx.fillText(course.courseName, x + cellWidth / 2, textY);
textY += 16;
if (course.staffName) {
ctx.fillText(course.staffName, x + cellWidth / 2, textY);
textY += 16;
}
if (course.roomName) {
ctx.fillText(course.roomName, x + cellWidth / 2, textY);
}
} else {
setFill('#ffffff');
ctx.fillRect(x, y, cellWidth, cellHeight);
}
setStroke('#cccccc');
ctx.strokeRect(x, y, cellWidth, cellHeight);
}
setStroke('#cccccc');
ctx.strokeRect(padding, y, rowHeaderWidth, cellHeight);
}
// 外边框
setStroke('#cccccc');
ctx.strokeRect(padding, padding, rowHeaderWidth, headerHeight);
ctx.strokeRect(padding, padding, rowHeaderWidth + cols * cellWidth, headerHeight + rows * cellHeight);
},
saveImage() {
if (!this.previewImagePath) {
uni.showToast({ title: '图片不存在', icon: 'none' });
return;
}
// #ifdef H5
try {
if (this.previewImagePath.startsWith('data:')) {
// base64 转 blob 下载,兼容大图片
const arr = this.previewImagePath.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const blob = new Blob([u8arr], { type: mime });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.download = '周课表.png';
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
} else {
const link = document.createElement('a');
link.download = '周课表.png';
link.href = this.previewImagePath;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
uni.showToast({ title: '下载成功' });
this.closePreview();
} catch (e) {
console.error(e);
uni.showToast({ title: '下载失败', icon: 'none' });
}
// #endif
// #ifndef H5
uni.saveImageToPhotosAlbum({
filePath: this.previewImagePath,
success: () => {
uni.showToast({ title: '保存成功' });
this.closePreview();
},
fail: (err) => {
console.error(err);
if (err.errMsg && err.errMsg.indexOf('auth deny') > -1) {
uni.showModal({
title: '提示',
content: '需要您授权保存到相册,是否前往设置?',
success: (res) => {
if (res.confirm) {
uni.openSetting();
}
}
});
} else {
uni.showToast({ title: '保存失败', icon: 'none' });
}
}
});
// #endif
}
,
closePreview() {
this.$refs.previewPopup.close();
// 可清除临时图片路径以释放内存
// this.previewImagePath = '';
},
formatTime(timeStr) {
// 假设 timeStr 格式为 "HH:MM:SS"
return timeStr.slice(0, 5); // 取前5位 "HH:MM"
},
closeDialog(){
this.$refs.pfDialog.close();
},
openDialog(){
this.$refs.pfDialog.open();
},
gotoBack(){
// 返回
uni.navigateBack({
delta: 1
});
},
getPhoneNumber(e){
if(e.phone){
this.phone = e.phone
}
if(e.userid){
this.userid = e.userid
}
},
// 获取周课表
async loadweekcourse(){
uni.showLoading();
this.loading=true;
try{
const {data: res} = await uni.$http.get('/api/course/getCourseList');
console.log(res);
this.loading=false;
if (res.success) {
var data = res.data;
this.columnTitles=data.columnTitles?data.columnTitles:[];
this.claTimeContainer=data.claTimeContainer?data.claTimeContainer:[];
this.list=[];
this.columnTitles.forEach((item,index)=>{
var lcell={
id: item.courseTimeId,
order_state:null,
loadStatus:'more', // 'more', // 'noMore',
date:(item.day).slice(-5),
redate: item.day,
week:item.weekName,
slist:[]
}
this.list.push(lcell);
this.$forceUpdate();
});
this.getOrderList();
}
else{
uni.showModal({
title: '提示',
content: '当前信息不存在..',
cancelText: '取消',
confirmText: '确定',
success: ress => {
// 返回
uni.navigateBack({
delta: 1
});
}
});
}
}
catch(e){
console.log(e);
this.loading=false;
uni.showModal({
title: '提示',
content: '当前信息不存在..',
cancelText: '取消',
confirmText: '确定',
success: ress => {
// 返回
uni.navigateBack({
delta: 1
});
}
});
}
},
// 获取日课表
async getOrderList() {
if(this.list[this.tabCurrentIndex].loadStatus!=="noMore")
{
uni.showLoading({
title: '课程加载中...'
});
var para={
date:this.list[this.tabCurrentIndex].redate
}
const {data: res} = await uni.$http.post('/api/course/getCourseListByDate', para);
if(res&&res.data&&res.data.length>0){
this.list[this.tabCurrentIndex].loadStatus="noMore";
this.$forceUpdate();
this.list[this.tabCurrentIndex].slist=res.data;
this.$forceUpdate();
}
else{
this.list[this.tabCurrentIndex].loadStatus="noMore";
this.$forceUpdate();
this.list[this.tabCurrentIndex].slist=[];
this.$forceUpdate();
}
}
},
loadData(){
// 加载
if(this.list[this.tabCurrentIndex].loadStatus=="more") {
this.list[this.tabCurrentIndex].loadStatus="loading";
setTimeout(() => {
this.getOrderList();
}, 300);
}
},
getimgRemoteFile(img){
if(img){
img=img.split(',')[0];
return getRemoteFile(img)
}
else{
return require("@/static/image/theme/p1.jpg")
}
},
getYear(){
var today = new Date();
var targetday_milliseconds=today.getTime();
today.setTime(targetday_milliseconds); //注意,这行是关键代码
var tYear = today.getFullYear();
return tYear;
},
getDay(day){
var today = new Date();
var targetday_milliseconds=today.getTime() + 1000*60*60*24*day;
today.setTime(targetday_milliseconds); //注意,这行是关键代码
var tYear = today.getFullYear();
var tMonth = today.getMonth();
tMonth = this.doHandleMonth(tMonth + 1);
var tDate = today.getDate();
tDate = this.doHandleMonth(tDate);
return tYear+"/"+tMonth+"/"+tDate;
},
// 课程表切换
tabSelect0(index) {
this.tabCurrentIndex0 = index;
},
tabSelect(e) {
this.tabCurrentIndex = e.currentTarget.dataset.id;
this.scrollLeft = (e.currentTarget.dataset.id - 1) * 60;
// 重新加载 暂不需要加载
// this.list[this.tabCurrentIndex].loadStatus="more";
// this.list[this.tabCurrentIndex].slist=[];
this.loadData();
},
animationfinish({ detail: { current } }) {
this.tabCurrentIndex = current;
this.loadData();
},
gotoorder(item){
var data=encodeURIComponent(JSON.stringify(item));
// 预定详情
uni.navigateTo({
url: `/pages/book/book?data=${data}`
});
},
gotoshare(item){
console.log("分享");
},
gotoInfo(id){
// 详情
uni.navigateTo({
url: `/pages/order/orderinfo?id=${id}`
});
},
iflogin(){
this.openId = myCache('openId');
var user = myCache('user');
this.userid = user.userid? user.userid:'';
this.phone = user.userphone;
if(this.userid==""||this.userid=="0"||this.phone==""){
uni.navigateTo({
url: `/pages/login/login`
});
return false;
}
else{
return true;
}
},
reset(){
this.orderlists=[];
// 重新加载数据
this.list.forEach((cell,idx)=>{
this.list[idx].loadStatus='more';
this.list[idx].slist=[];
});
this.$forceUpdate();
this.loadData();
},
// 获取状态栏高度
geStatusBarHeight(){
return uni.getSystemInfoSync()['statusBarHeight'];
},
// 获取导航栏高度
getNavBarHeight(){
return 45+uni.getSystemInfoSync()['statusBarHeight'];
},
}
}
</script>
<style lang="scss" scoped>
.page {
padding: 0;
position: relative;
background-image: url('@/static/image/bg.jpg');
background-attachment: fixed;
background-size: cover;
background-position: center center;
height: calc(100vh);
/* #ifdef H5 */
height: calc(100vh - var(--window-top));
/* #endif */
}
.preview-container {
background: #fff;
border-radius: 20rpx;
padding: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
width: 90%;
max-width: 600rpx;
}
.preview-actions {
display: flex;
justify-content: space-around;
width: 100%;
margin-top: 30rpx;
button {
width: 40%;
}
}
.headcon{
display: flex;
flex-direction: row;
background-color: #FFFFFF;
min-height: 100rpx;
width: 100%;
border-bottom: 1px solid #ddd;
.hback{
display: flex;
flex-direction: row;
padding: 20rpx;
justify-content: flex-start;
align-items: center;
position: relative;
.htxt{
color: #000;
font-size: 30rpx;
margin-left: 10rpx;
}
.save-btn {
display: flex;
align-items: center;
margin-right: 10rpx;
padding: 8rpx 16rpx;
background-color: rgba(0,169,154,0.1);
border-radius: 30rpx;
}
}
}
.tabtip{
position: absolute;
top: 0;
left: 10rpx;
position: relative;
display: inline-block;
overflow: hidden;
}
.scrolllist0{
display: flex;
flex: 1;
height: 100rpx;
background: #FFF;
white-space: nowrap;
width: 100%;
text-align: right;
}
.tabicl{
width: 130rpx;
height: 100rpx;
line-height: 100rpx;
position: relative;
display: inline-block;
overflow: hidden;
margin-right: 20rpx;
}
.ptext{
font-size: 32rpx;
font-weight: 600;
text-align: center;
color: #333333;
height: 90rpx;
}
.cur .ptext{
color: #00A99A;
font-size: 36rpx;
}
.cur .pline{
width: 40%;
height: 9rpx;
background: #00A99A;
border-radius: 6rpx;
opacity: 1;
margin-left: 30%;
}
.scrolllist{
height: 150rpx;
background: #FFF;
white-space: nowrap;
width: 100%;
}
.tabcell{
width: 98rpx;
position: relative;
display: inline-block;
overflow: hidden;
margin-left: 12rpx;
text-align: center;
margin-top: 20rpx;
.dtxt{
font-size: 28rpx;
color:#000;
font-weight: 600;
padding: 2rpx 10rpx;
margin-bottom: 4rpx;
}
.wtxt{
font-size: 28rpx;
color:#000;
margin-bottom: 10rpx;
margin-top: 10rpx;
}
}
.tabcell.cur{
background-color: #e4f9f7;
border: 1rpx solid #00A99A;
border-radius: 10rpx;
}
.swiper-box {
flex: 1;
height: calc(100vh - var(--window-top) - 310rpx) !important;
}
.swiper-item {
height: 100%;
}
.nodata{
width: 100%;
height: calc(100vh - 400rpx) !important;
line-height: calc(100vh) !important;
background-image: url('/static/image/no.png');
background-size: 52% auto;
background-repeat: no-repeat;
background-position: center;
font-size: 28rpx;
text-align: center;
color: #00A99A;
overflow: hidden;
}
.scell{
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
margin: 20rpx;
.oitem{
display: flex;
flex-direction: column;
background-color: #FFFFFF;
border-radius: 6rpx;
padding: 20rpx;
width: 100%;
margin-bottom: 20rpx;
.otop{
display: flex;
flex-direction: row;
padding: 10rpx 0 20rpx;
width: 100%;
border-bottom: 1rpx solid #f0f0f0;
.hleft{
display: flex;
flex:1;
align-items: center;
font-size: 30rpx;
font-weight: 600;
line-height: 32rpx;
color: #000;
}
.hright{
display: flex;
justify-content: flex-end;
align-items: center;
font-size: 28rpx;
color: #000;
font-weight: 600;
}
}
.oscon{
margin:20rpx 0 10rpx;
border-radius: 16rpx;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 100%;
.osimg{
width: 200rpx;
height: 200rpx;
margin-right: 10rpx;
>image {
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
}
}
.osname{
display: flex;
flex: 1;
width: 100%;
flex-direction: column;
margin-left: 20rpx;
.oscell{
display: flex;
flex-direction: row;
margin-bottom: 10rpx;
align-items: center;
}
.txtbg{
padding: 2rpx 15rpx;
border-radius: 30rpx;
background-color: #f3fbfb;
border: 1rpx solid #7cced6;
font-size: 26rpx;
color: #333;
margin-right: 10rpx;
}
.txtbg1{
padding: 2rpx 15rpx;
border-radius: 30rpx;
background-color: #fcf2f3;
border: 1rpx solid #f09fb6;
font-size: 26rpx;
color: #333;
margin-right: 10rpx;
}
.txtbg2{
padding: 2rpx 15rpx;
border-radius: 30rpx;
background-color: #fcf3ef;
border: 1rpx solid #e99b12;
font-size: 26rpx;
color: #333;
margin-right: 10rpx;
}
.txtbg3{
padding: 2rpx 15rpx;
border-radius: 30rpx;
background-color: #f1f8f0;
border: 1rpx solid #7dbd71;
font-size: 26rpx;
color: #333;
margin-right: 10rpx;
}
.ntip{
font-size: 26rpx;
color:#1abbc3;
line-height: 32rpx;
margin-left: 10rpx;
}
.ntxt{
font-size: 26rpx;
color:#000;
line-height: 32rpx;
margin-left: 10rpx;
}
.txt{
font-size: 24rpx;
color:#666;
margin-left: 10rpx;
}
}
.rcpm{
display: flex;
flex-direction: column;
width: 116rpx;
}
.sharebtn{
background-color: #FFF;
margin-left: 20rpx;
border-radius: 40rpx;
height: 46rpx;
line-height: 44rpx;
padding: 0 20rpx;
font-size: 24rpx;
border:1rpx solid rgb(252, 105, 0);
color:rgb(252, 105, 0) !important;
}
.sharebtn::after{
border:0 !important;
}
.orderbtn{
background-color: #00A99A !important;
margin-left: 20rpx;
border-radius: 40rpx;
height: 46rpx;
line-height: 44rpx;
padding: 0 20rpx;
font-size: 24rpx;
border:1rpx solid #00A99A;
margin-bottom: 30rpx;
}
.orderbtn::after{
border:0 !important;
}
}
}
.sitem {
display: flex;
flex-direction: column;
flex: 1;
background-color: #FFFFFF;
/*设置最小宽度,才会让元素排不下,导致换行排列*/
min-width: calc((100% - 20rpx) / 2);
max-width: calc((100% - 20rpx) / 2);
margin-right:5rpx;
margin-left:5rpx;
margin-bottom: 10rpx;
border-radius: 16rpx;
.simg{
width: 100%;
height: 360rpx;
.img{
width: 100%;
height: 100%;
border-radius: 16rpx 16rpx 0 0;
}
}
.sname{
font-size: 30rpx;
font-weight: 600;
color: rgb(29, 29, 29);
padding: 16rpx;
}
.sinfo{
font-size: 26rpx;
color:rgb(116, 116, 116);
padding: 0 16rpx;
height: 60rpx;
line-height: 30rpx;
overflow: hidden;
position: relative;
text-overflow: ellipsis;
display:-webkit-box;
-webkit-box-orient:vertical;
-webkit-line-clamp:2;
}
.sprices{
padding: 0 16rpx 20rpx 16rpx;
font-size: 36rpx;
color:rgb(252, 105, 0);
border-radius: 0 0 16rpx 16rpx;
.cx{
font-size: 28rpx;
color:rgb(116, 116, 116);
text-decoration: line-through;
margin-left: 20rpx;
}
}
}
}
.table_head{
position: sticky;
top: 0;
z-index: 20;
}
.table_body{
position: sticky;
top: 0;
z-index: 19;
// height: calc(100vh - 240rpx);
// /* #ifdef H5 */
// height: calc(100vh - 240rpx - var(--window-top));
// /* #endif */
background: #f8f8f8;
}
::v-deep .uni-table-tr{
display: flex;
background: #f8f8f8;
}
::v-deep .uni-table-th{
padding:0 !important
}
::v-deep .uni-table-td{
padding:0 !important
}
::v-deep .uni-table-tr .uni-table-th:first-child {
position: sticky;
left: 0;
z-index: 10;
background-color: #F2F2F2;
}
::v-deep .uni-table-tr .uni-table-td:first-child {
position: sticky;
left: 0;
z-index: 99;
background-color: #F2F2F2;
}
.leftfix{
position: sticky;
left: 0;
z-index: 10;
background-color: #fffae0;
}
.courcon{
width: 100%;
height: calc(100vh - 200rpx);
/* #ifdef H5 */
height: calc(100vh - 100rpx - var(--window-top));
/* #endif */
overflow: auto;
margin: 0;
padding: 0;
.leftcell{
position: sticky;
left: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 120rpx;
background-color: #e4f9f7;
border-right: 1rpx solid #fff;
}
.rightcell{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 120rpx;
background-color: #e4f9f7;
border-radius: 10rpx;
border-right: 1rpx solid #fff;
position: relative;
}
.cnum{
position: absolute;
bottom:30rpx;
right: 6rpx;
min-width: 28rpx;
padding: 0 2rpx;
border-radius: 50% 50%;
background-color: #ff0000;
color: #FFF;
font-size: 20rpx;
}
.courhead{
border-radius: 8rpx;
background-color: #ffffff;
display: flex;
flex-direction: column;
width: 100rpx;
height: 120rpx;
justify-content: center;
align-items: center;
position: absolute;
left: 0;
top: 0;
}
.ctitle{
font-size: 26rpx;
color:#000;
}
.courcell{
width: 100rpx;
height: 120rpx;
border-bottom: 1rpx dotted #ccc;
background-color: #ffffff;
}
.chcell{
width: 240rpx;
}
.courfix{
position: sticky;
left: 0;
z-index: 19;
display: flex;
flex-direction: column;
width: 100%;
height: 280rpx;
background-color: #F2F2F2;
justify-content: flex-start;
align-items: center;
border-bottom: 1rpx dotted #ccc;
color:#000;
font-size: 28rpx;
padding-top: 10rpx;
}
.courheadtext{
display: flex;
flex-direction: column;
width: 100%;
height: 280rpx;
justify-content: center;
align-items: center;
border-radius: 10rpx;
border-left-width: 6rpx;
border-left: solid;
}
.ctxt{
line-height: 30rpx;
padding-bottom: 4rpx;
margin-bottom: 6rpx;
}
.orderbtn{
background-color: #ddd !important;
border-radius: 40rpx;
height: 40rpx;
line-height: 38rpx;
padding: 0 16rpx;
font-size: 24rpx;
border:1rpx solid #ddd;
margin-top: 6rpx;
color:#000;
}
.orderbtn::after{
border:0 !important;
}
.noorderbtn{
background-color: #eee !important;
border-radius: 40rpx;
height: 40rpx;
line-height: 38rpx;
padding: 0 16rpx;
font-size: 24rpx;
border:1rpx solid #eee;
margin-top: 6rpx;
color:#888;
}
.noorderbtn::after{
border:0 !important;
}
}
.bbtn{
margin: 20rpx 30rpx 54rpx;
font-size: 32rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #FCFCFD;
width: 314rpx;
height: 76rpx;
line-height: 76rpx;
background: #00A99A;
box-shadow: 0rpx 12rpx 64rpx 2rpx rgba(137,150,95,0.4);
border-radius: 10rpx;
opacity: 1;
&::after{
border:none;
}
}
.pcon{
width: 690rpx;
padding-top:30rpx;
padding-bottom:50rpx;
background: #FCFCFD;
box-shadow: 0rpx 128rpx 128rpx 2rpx rgba(31,47,70,0.12);
border-radius: 16rpx 16rpx 16rpx 16rpx;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.phtxt{
width: 600rpx;
margin: 20rpx auto 10rpx;
font-size: 36rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 600;
color: #23262F;
}
.ptxt
{
width: 620rpx;
margin: 0 auto 20rpx;
overflow-y: auto;
border-radius: 16rpx 16rpx 16rpx 16rpx;
opacity: 1;
border: 2rpx solid #F7F8FA;
line-height: 50rpx;
padding: 20rpx;
height:calc(100vh - 800rpx) !important;
}
.btn{
width: 520rpx;
height: 80rpx;
line-height: 80rpx;
background: #00A99A;
box-shadow: 0rpx 12rpx 64rpx 2rpx rgba(0,169,154,0.4);
border-radius: 254rpx 254rpx 254rpx 254rpx;
text-align: center;
font-size: 32rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #FCFCFD;
}
}
</style>