index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. <template>
  2. <view class="page">
  3. <cu-custom :isBack="false"></cu-custom>
  4. <view class="header" :style="[{top:topHeader + 'px'}]">
  5. <view class="header-title header-switch">
  6. <view class="header-title-main" @tap="changeTab('schedule')" :class="{'cur':type==='schedule'}">
  7. <view class="margin-bottom-xs">日程表</view>
  8. <view class="point"></view>
  9. </view>
  10. <view class="header-title-main margin-left-lg" @tap="changeTab('mine')" :class="{'cur':type==='mine'}">
  11. <view class="margin-bottom-xs">我的</view>
  12. <view class="point"></view>
  13. </view>
  14. </view>
  15. <view style="width:120px;" v-if="type==='mine'">
  16. <swiper-tab :menuList="menuList" @changeTab="swipe" :activeTab="currentIndex"></swiper-tab>
  17. </view>
  18. </view>
  19. <!-- 日程表 -->
  20. <view v-if="type==='schedule'">
  21. <scroll-view scroll-y="true"
  22. :style="[{marginTop:'56px',height:'calc(100vh - 114px - '+ topHeader+'px)'}]">
  23. <uni-calendar class="uni-calendar--hook" :selected="info.selected"
  24. style="padding-top:100px;"
  25. :showMonth="false" @change="changeDay" @monthSwitch="monthSwitch" />
  26. <view class="cu-card margin-top-xs padding-xs">
  27. <view class="cu-item day-info">
  28. <view class="day-title flex justify-between solid-bottom padding text-lg">
  29. <view>应到上课节数:{{present.be_section_num}}</view>
  30. <view>实际上课节数: {{present.actual_section_num}}</view>
  31. </view>
  32. <view class="day-chart flex justify-between padding">
  33. <view v-for="(chart,key) in attendance" :key="key" class="flex-sub">
  34. <canvas :canvas-id="key" style="margin:0 auto;width:100%;height:80px;"></canvas>
  35. <view style="margin-left:18px;">{{chart.title}}</view>
  36. </view>
  37. </view>
  38. </view>
  39. </view>
  40. <view class="list margin-top-xs">
  41. <view class="cu-card margin-top margin-bottom shadow" v-for="(item,index) in classList" :key="index">
  42. <view class="cu-item" v-for="(mark,i) in item.mark" :key="i">
  43. <view class="card-header flex justify-end solid-bottom">
  44. <view class="text-student course-status">
  45. {{mark.status_desc}}
  46. </view>
  47. </view>
  48. <view class="card flex">
  49. <view class="card-left">
  50. <img mode="scaleToFill" :src="mark.avatar" alt="" class="card-image">
  51. </view>
  52. <view class="card-right margin-left-sm">
  53. <view class="card-title">{{mark.course_name}}</view>
  54. <view class="card-item margin-top-xs" >
  55. <text class="card-text">{{mark.start_at}}</text>
  56. </view>
  57. <view class="card-item margin-top-xs" >
  58. <text class="card-text">{{mark.end_at}}</text>
  59. </view>
  60. <view class="card-item margin-top-xs">
  61. <button class="cu-btn round line-cyan button-hover" @tap="classOperation(mark.class_plan_id)">
  62. {{mark.status==-1?'查看':'去上课'}}
  63. </button>
  64. <button class="cu-btn round line-red button-hover margin-left" @tap="askLeave(mark.class_plan_id)">去请假</button>
  65. </view>
  66. </view>
  67. </view>
  68. </view>
  69. </view>
  70. </view>
  71. </scroll-view>
  72. </view>
  73. <!-- 我的 -->
  74. <view v-if="type==='mine'">
  75. <scroll-view scroll-y="true" @scrolltolower="loadMore" class="scroll-main"
  76. :style="[{height:'calc(100vh - 114px - '+ topHeader+'px)'}]">
  77. <view class="list">
  78. <view class="cu-card margin-top margin-bottom shadow" v-for="(item,index) in list" :key="index">
  79. <view class="cu-item">
  80. <view class="card-header flex justify-between solid-bottom">
  81. <view v-if="item.day">{{item.day}}</view>
  82. <view class="text-student course-status" v-if="item.status">
  83. {{item.status}}
  84. </view>
  85. <view class="text-student course-status" v-if="currentIndex===1">
  86. 已完结
  87. </view>
  88. </view>
  89. <view class="card flex">
  90. <view class="card-left">
  91. <img mode="scaleToFill" :src="item.image" alt="" class="card-image">
  92. </view>
  93. <view class="card-right margin-left-sm">
  94. <view class="card-title">{{item.attend_name}}</view>
  95. <view class="card-item margin-top-xs" >
  96. <text class="card-label">机构:</text>
  97. <text class="card-text">{{item.agency_name||'-'}}</text>
  98. </view>
  99. <view class="card-item margin-top-xs" >
  100. <text class="card-label">老师:</text>
  101. <text class="card-text">{{item.teacher||'-'}}</text>
  102. </view>
  103. </view>
  104. </view>
  105. </view>
  106. </view>
  107. </view>
  108. </scroll-view>
  109. </view>
  110. <mp-tabbar :outerSelected="1" />
  111. </view>
  112. </template>
  113. <script>
  114. import NP from 'number-precision'
  115. import { uniCalendar } from '@dcloudio/uni-ui'
  116. import { _nowCourse, _courseHistory, _attendance, _dateCourse } from '@/api/course'
  117. import { getDate } from '@/common/utils'
  118. import swiperTab from '@/components/swiper-tab.vue'
  119. export default {
  120. components: {
  121. uniCalendar, swiperTab
  122. },
  123. data () {
  124. return {
  125. topHeader: this.globalCustomBarHeight,
  126. type: 'schedule',
  127. menuList: ['我的', '历史'],
  128. currentIndex: 0,
  129. page_num: 1,
  130. noMore: false, // 加载判断
  131. list: [],
  132. classList: [], // 日程表
  133. info: { // 日历数据
  134. year: '',
  135. month: '',
  136. day: '',
  137. selected: []
  138. },
  139. present: {
  140. absenteeism_num: 0,
  141. actual_section_num: 5,
  142. be_section_num: 5,
  143. late_num: 0,
  144. leave_num: 0,
  145. normal_num: 0
  146. }, // 实际出席情况
  147. attendance: { // 出席统计数据
  148. leave_num: {
  149. title: '请假', times: 2, color: '#9bde78'
  150. },
  151. late_num: {
  152. title: '正常', times: 3, color: '#e0be60'
  153. },
  154. normal_num: {
  155. title: '迟到', times: 1, color: '#61d4e2'
  156. },
  157. absenteeism_num: {
  158. title: '旷课', times: 0, color: '#d697eb'
  159. }
  160. }
  161. }
  162. },
  163. onReady() {
  164. const today = getDate(new Date(), 0)
  165. this.info.year = today.year
  166. this.info.month = today.month
  167. this.info.day = today.date
  168. this.init()
  169. },
  170. methods: {
  171. init() {
  172. if (this.type === 'mine') {
  173. this.get_list()
  174. } else {
  175. this.getSehedule()
  176. }
  177. },
  178. getSehedule() {
  179. this.get_attend().then(res => {
  180. this.present = res.data
  181. this.fix_attend(res.data)
  182. this.drawChart()
  183. this.get_dateCourse()
  184. })
  185. },
  186. classOperation(id) { // 上课
  187. this.globalNavigateTo('studentOperation', { id })
  188. },
  189. askLeave(id) { // 请假
  190. this.globalNavigateTo('studentAbsent', { id })
  191. },
  192. loadMore() {
  193. if (this.noMore) {
  194. uni.showToast({ title: '没有更多了', icon: 'none' })
  195. return false
  196. }
  197. this.page_num++
  198. this.get_list()
  199. },
  200. get_list() {
  201. if (this.currentIndex === 0) {
  202. this.get_course()
  203. } else {
  204. this.get_history()
  205. }
  206. },
  207. get_course() {
  208. _nowCourse({ page_num: this.page_num }).then(res => {
  209. if (this.page_num > 1) {
  210. if (res.data.length < this.page_size) this.noMore = true
  211. this.list = this.list.concat(res.data)
  212. } else {
  213. this.noMore = false
  214. this.list = res.data
  215. }
  216. })
  217. },
  218. get_history() {
  219. _courseHistory({ page_num: this.page_num }).then(res => {
  220. if (this.page_num > 1) {
  221. if (res.data.length < this.page_size) this.noMore = true
  222. this.list = Object.assign({}, this.list, res.data)
  223. } else {
  224. this.noMore = false
  225. this.list = res.data
  226. }
  227. })
  228. },
  229. drawChart() {
  230. for (var key in this.attendance) {
  231. this.draw(this.attendance[key], key)
  232. }
  233. },
  234. changeTab(type) {
  235. this.type = type
  236. this.$nextTick(() => {
  237. this.init()
  238. })
  239. },
  240. swipe(index) {
  241. this.currentIndex = index
  242. this.page_num = 1
  243. this.get_list()
  244. },
  245. changeDay(e) {
  246. this.info.day = e.date
  247. // 模拟动态打卡
  248. // if (this.info.selected.length > 5) return
  249. // this.info.selected.push({
  250. // date: e.fulldate,
  251. // info: '打卡'
  252. // })
  253. },
  254. monthSwitch(e) {
  255. this.info.year = e.year
  256. this.info.month = e.month < 10 ? '0' + e.month : e.month
  257. this.getSehedule()
  258. },
  259. get_attend() { // 获取出席情况
  260. const date = this.info.year + '-' + this.info.month
  261. return new Promise((resolve, reject) => {
  262. const info = _attendance({ date })
  263. resolve(info)
  264. })
  265. },
  266. fix_attend(info) { // 补充出席实际数据
  267. Object.keys(this.attendance).forEach(key => {
  268. this.attendance[key].times = info[key]
  269. })
  270. },
  271. get_dateCourse() { // 获取对应日期数据
  272. _dateCourse({ year: this.info.year, month: this.info.month }).then(res => {
  273. this.classList = res.data
  274. })
  275. },
  276. draw (ele, key) {
  277. const ctx = uni.createCanvasContext(key)
  278. const value = NP.divide(ele.times, this.present.be_section_num) * 100// 次数转化对应百分比值
  279. const startAngle = 1.5
  280. const endAngle = 1.5 + value * 2 / 100
  281. // 画圆环
  282. ctx.beginPath()
  283. ctx.arc(34, 40, 28, 0, 2 * Math.PI)
  284. ctx.setStrokeStyle('#EDEDED') // 弧线的颜色
  285. ctx.setLineWidth('8') // 弧的宽度
  286. ctx.setLineCap('round') // 线条结束端点样式 butt 平直 round 圆形 square 正方形
  287. ctx.stroke()
  288. // 画进度条
  289. ctx.beginPath()
  290. ctx.arc(34, 40, 28, startAngle * Math.PI, endAngle * Math.PI)
  291. ctx.setStrokeStyle(ele.color)
  292. ctx.setLineWidth('8')
  293. ctx.setLineCap('round')
  294. ctx.stroke()
  295. ctx.setFontSize(14)
  296. ctx.setFillStyle('#000') // 文字的颜色
  297. ctx.fillText(ele.times + '次', 20, 44)
  298. ctx.draw()
  299. }
  300. }
  301. }
  302. </script>
  303. <style lang="scss">
  304. @import '~@/common/css/mixin.scss';
  305. .page{
  306. height:100vh;
  307. }
  308. .header-switch{
  309. justify-content:start;
  310. .header-title-main{
  311. font-size:18px;
  312. color:#999;
  313. .point{
  314. opacity: 0;
  315. }
  316. &.cur{
  317. transition: font-size .3s;
  318. font-size:24px;
  319. color:#000;
  320. .point{
  321. opacity: 1;
  322. }
  323. }
  324. }
  325. }
  326. .day-info{
  327. }
  328. .scroll-main{
  329. margin-top:78px;
  330. padding:20rpx;
  331. }
  332. .cu-card{
  333. .cu-item{
  334. margin-top:10rpx;
  335. padding:20rpx;
  336. margin:0;
  337. }
  338. }
  339. .card-header{
  340. padding-bottom:10px;
  341. @include title(10px,18px);
  342. }
  343. .card{
  344. &-title{
  345. font-size:16px;
  346. }
  347. &-image{
  348. width:35vw;
  349. height:25vw;
  350. border-radius:2vw;
  351. }
  352. &-label{
  353. color:#999;
  354. }
  355. &-text{
  356. color:#333;
  357. }
  358. }
  359. .course-status{
  360. font-size:14px;
  361. font-weight:normal;
  362. }
  363. </style>