|
@@ -19,11 +19,38 @@
|
|
|
<el-button type="primary" size="mini" @click="onsearch()"> 查询</el-button>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
- <data-table :fields="fields" :data="list"> </data-table>
|
|
|
+ <!-- <data-table :fields="fields" :data="list"> </data-table> -->
|
|
|
+ <el-table :data="list" border stripe height="600px" v-if="headList.length > 0">
|
|
|
+ <el-table-column align="center" v-for="(h, hindex) in headList" :key="`col-${hindex}`" :label="h.label" :prop="h.prop">
|
|
|
+ <template v-if="h.prop !== 'name'">
|
|
|
+ <el-table-column align="center" label="上午">
|
|
|
+ <template v-slot="{ row }">
|
|
|
+ {{ getTableContent(row, h.label, 'am') }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column align="center" label="下午">
|
|
|
+ <template v-slot="{ row }">
|
|
|
+ {{ getTableContent(row, h.label, 'pm') }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column align="center" label="寝室">
|
|
|
+ <template v-slot="{ row }">
|
|
|
+ {{ getTableContent(row, h.label, 'bd') }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-card v-else>
|
|
|
+ <el-row type="flex" align="middle" style="height:600px; font-size:18px; text-align:center">
|
|
|
+ <el-col :span="24">请选择班级后进行查询</el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
</list-frame>
|
|
|
</div>
|
|
|
</template>
|
|
|
<script>
|
|
|
+const moment = require('moment');
|
|
|
import _ from 'lodash';
|
|
|
import listFrame from '@frame/layout/admin/list-frame';
|
|
|
import dataTable from '@frame/components/data-table';
|
|
@@ -32,14 +59,14 @@ const { mapActions: attendance } = createNamespacedHelpers('attendance');
|
|
|
const { mapActions: student } = createNamespacedHelpers('student');
|
|
|
const { mapActions: trainplan } = createNamespacedHelpers('trainplan');
|
|
|
const { mapActions: classes } = createNamespacedHelpers('classes');
|
|
|
-
|
|
|
+const { mapActions: leave } = createNamespacedHelpers('leave');
|
|
|
export default {
|
|
|
metaInfo: { title: '考勤管理' },
|
|
|
name: 'attendance',
|
|
|
props: {},
|
|
|
components: {
|
|
|
listFrame,
|
|
|
- dataTable,
|
|
|
+ // dataTable,
|
|
|
},
|
|
|
data: () => ({
|
|
|
classList: [],
|
|
@@ -54,7 +81,7 @@ export default {
|
|
|
label: '类型',
|
|
|
prop: 'type',
|
|
|
format: item => {
|
|
|
- return item === '0' ? '上课考勤' : item === '1' ? '上课考勤' : '';
|
|
|
+ return item === '0' ? '上课考勤' : item === '1' ? '寝室考勤' : '';
|
|
|
},
|
|
|
},
|
|
|
{
|
|
@@ -66,6 +93,9 @@ export default {
|
|
|
},
|
|
|
],
|
|
|
form: {},
|
|
|
+ headList: [],
|
|
|
+ attendList: [],
|
|
|
+ leaveList: [],
|
|
|
list: [],
|
|
|
total: 0,
|
|
|
}),
|
|
@@ -78,6 +108,8 @@ export default {
|
|
|
...student({ stuquery: 'query' }),
|
|
|
...trainplan({ planfetch: 'fetch' }),
|
|
|
...classes({ classesquery: 'query' }),
|
|
|
+ ...leave({ studentLeave: 'query' }),
|
|
|
+ // 找到指定范围的学生,找到这些学生的签单信息,找到这些学生的班级信息
|
|
|
async searchinfo() {
|
|
|
const res = await this.planfetch(this.defaultOption.planid);
|
|
|
let terms = res.data.termnum;
|
|
@@ -87,49 +119,238 @@ export default {
|
|
|
this.getClasses(this.form.termid);
|
|
|
}
|
|
|
},
|
|
|
- getBatch(termid) {
|
|
|
- let batchs = this.termList.filter(f => f._id === termid);
|
|
|
- if (batchs.length > 0) {
|
|
|
- let { batchnum } = batchs[0];
|
|
|
- this.$set(this, `batchList`, batchnum);
|
|
|
- }
|
|
|
- },
|
|
|
async getClasses(termid) {
|
|
|
const res = await this.classesquery({ termid });
|
|
|
var arr = res.data.filter(item => item.type == '0');
|
|
|
this.$set(this, `classList`, arr);
|
|
|
},
|
|
|
async onsearch({ skip = 0, limit = 10, ...info } = {}) {
|
|
|
- const res = await this.query({ termid: this.form.termid, classid: this.form.classid, ...info });
|
|
|
- const newdatas = [];
|
|
|
- if (this.form.name) {
|
|
|
- var arr = res.data.filter(item => item.stuname == this.form.name);
|
|
|
- for (const data of arr) {
|
|
|
- for (const attend of data.attend) {
|
|
|
- let newdata = { stuname: data.stuname, ...attend };
|
|
|
- newdatas.push(newdata);
|
|
|
- }
|
|
|
+ // 首先整理表头,如果没有选择班级,就是说,要看这期的考勤,需要将这期的日期都列出来坐表头
|
|
|
+ const headColumn = this.getColumn();
|
|
|
+ if (!headColumn) return;
|
|
|
+ this.$set(this, `headList`, headColumn);
|
|
|
+ // 找到相关信息
|
|
|
+ this.getStudent();
|
|
|
+ this.getAttendance();
|
|
|
+ this.getLeave();
|
|
|
+ },
|
|
|
+ // 获取表格内容,
|
|
|
+ getTableContent(...data) {
|
|
|
+ const res = this.getPersonAttend(...data);
|
|
|
+ if (!res) return;
|
|
|
+ const { type } = res;
|
|
|
+ const { word } = this.chageWord(type);
|
|
|
+ return word;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 获取表格内容的通用方法
|
|
|
+ * @param stu 学生信息
|
|
|
+ * @param date 日期
|
|
|
+ * @param type 类型: am=>上午;pm=>下午;bd=>寝室(晚上)
|
|
|
+ * @description 因为 文字 与 颜色 都可以根据这个方法取对应的值,所以把这个方法单抽出来
|
|
|
+ * @property toReturn.type 返回结果: ok:正常签到;late:迟到;nosign:没签到;leave:请假;exit:退出
|
|
|
+ */
|
|
|
+ getPersonAttend(stu, date, type) {
|
|
|
+ let toReturn = {};
|
|
|
+ // 先找有没有请假/退出,退出直接返回GG
|
|
|
+ const { id: studentid } = stu;
|
|
|
+ const les = this.leaveList.filter(f => f.studentid === studentid && f.status === '1');
|
|
|
+ // 查看是否有退出且审核的记录
|
|
|
+ const isExit = les.find(f => f.type == '1');
|
|
|
+ if (isExit) {
|
|
|
+ toReturn = { type: 'exit' };
|
|
|
+ return toReturn;
|
|
|
+ }
|
|
|
+ // 查看请假在不在这个时间里
|
|
|
+ // 先获得考勤要求时间
|
|
|
+ const { start, end } = this.getThisDayAttendTime(date, type);
|
|
|
+ if (!start || !end) return;
|
|
|
+ // 在请假里找,如果有符合条件的请假信息,说明本单元格内容是请假范围内的
|
|
|
+ const lr = les.find(l => {
|
|
|
+ const { starttime, endtime } = l;
|
|
|
+ if (starttime && endtime) {
|
|
|
+ const r1 = moment(starttime).isSameOrBefore(end);
|
|
|
+ const r2 = moment(endtime).isAfter(start);
|
|
|
+ return r1 && r2;
|
|
|
}
|
|
|
+ });
|
|
|
+ if (lr) {
|
|
|
+ toReturn = { type: 'leave' };
|
|
|
+ return toReturn;
|
|
|
+ }
|
|
|
+ // 下面就该找考勤了,分为:签没签到=>签到了=>迟没迟到:1,没签到,2正常到位,3迟到
|
|
|
+ const stuAtt = this.attendList.find(f => f.studentid === studentid);
|
|
|
+ if (!stuAtt) {
|
|
|
+ toReturn = { type: 'nosign' };
|
|
|
+ return toReturn;
|
|
|
+ }
|
|
|
+ const { attend } = stuAtt;
|
|
|
+ if (!_.isArray(attend)) {
|
|
|
+ toReturn = { type: 'nosign' };
|
|
|
+ return toReturn;
|
|
|
+ }
|
|
|
+ // 过滤出该天的签到记录
|
|
|
+ let thisday = attend.filter(f => f.date === date);
|
|
|
+ if (thisday.length <= 0) {
|
|
|
+ toReturn = { type: 'nosign' };
|
|
|
+ return toReturn;
|
|
|
+ }
|
|
|
+ // 找到该天,该签到时间
|
|
|
+ // TODO这里做的不是很好,数据关系不强,没法定下哪个记录就是上午,哪个记录就是下午,只能靠时间决定
|
|
|
+ // 应该以下个检查开始时间为节点去过滤,am使用pm的开始时间为节点,pm使用bd的开始时间为节点,bd使用自己的开始时间往后查
|
|
|
+ thisday = thisday.map(i => ({ ...i, toCompare: `${i.date} ${i.time}` }));
|
|
|
+ thisday = _.orderBy(thisday, ['toCompare'], ['asc']);
|
|
|
+ let attTime;
|
|
|
+ const { pm_start, bd_start } = this.defaultOption;
|
|
|
+ if (type === 'am') attTime = `${date} ${pm_start}`;
|
|
|
+ else attTime = `${date} ${bd_start}`;
|
|
|
+ // 找到在上述3个时间点前的记录,然后看记录是迟到还是正常到位
|
|
|
+ const r = thisday.find(f => {
|
|
|
+ if (type === 'am' || type === 'pm') return moment(f.toCompare).isSameOrBefore(attTime);
|
|
|
+ else return moment(f.toCompare).isSameOrAfter(attTime);
|
|
|
+ });
|
|
|
+ if (!r) {
|
|
|
+ toReturn = { type: 'nosign' };
|
|
|
+ return toReturn;
|
|
|
+ }
|
|
|
+ const { status } = r;
|
|
|
+ if (status == '1') {
|
|
|
+ toReturn = { type: 'ok' };
|
|
|
} else {
|
|
|
- for (const data of res.data) {
|
|
|
- for (const attend of data.attend) {
|
|
|
- let newdata = { stuname: data.stuname, ...attend };
|
|
|
- newdatas.push(newdata);
|
|
|
- }
|
|
|
+ toReturn = { type: 'late' };
|
|
|
+ }
|
|
|
+ return toReturn;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取对应签到时间
|
|
|
+ getThisDayAttendTime(date, type) {
|
|
|
+ let start = _.get(this.defaultOption, `${type}_start`);
|
|
|
+ let end = _.get(this.defaultOption, `${type}_end`);
|
|
|
+ if (!start || !end) {
|
|
|
+ console.warn('未找到系统设置的考勤时间');
|
|
|
+ return {};
|
|
|
+ }
|
|
|
+ start = `${date} ${start}`;
|
|
|
+ end = `${date} ${end}`;
|
|
|
+ return { start, end };
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 获取表头,使用form中的termid和classid制作表头
|
|
|
+ */
|
|
|
+ getColumn() {
|
|
|
+ let head = [{ label: '学生', prop: 'name' }];
|
|
|
+ const { termid, classid } = this.form;
|
|
|
+ if (classid) {
|
|
|
+ // 有classid,就一定有classList否则数据也不能出现
|
|
|
+ const cla = this.classList.find(f => f.id === classid);
|
|
|
+ if (!cla) {
|
|
|
+ this.tip('数据错误: 未找到指定班级!', 'error');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const { startdate, enddate } = cla;
|
|
|
+ if (!(startdate && enddate)) {
|
|
|
+ this.tip('数据错误: 未找到开始,结束日期', 'error');
|
|
|
+ return;
|
|
|
}
|
|
|
+ let dayList = this.getDayList(startdate, enddate);
|
|
|
+ return [...head, ...dayList];
|
|
|
+ } else {
|
|
|
+ this.tip('请选择班级', 'warning');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 获取天列表
|
|
|
+ * @param start 开始日期
|
|
|
+ * @param end 结束日期
|
|
|
+ */
|
|
|
+ getDayList(start, end) {
|
|
|
+ const day = moment(end).diff(moment(start), 'days');
|
|
|
+ let arr = [];
|
|
|
+ for (let d = 0; d <= day; d++) {
|
|
|
+ const ad = moment(start)
|
|
|
+ .add(d, 'days')
|
|
|
+ .format('YYYY-MM-DD');
|
|
|
+ let obj = { label: ad, prop: `day${d}` };
|
|
|
+ arr.push(obj);
|
|
|
+ }
|
|
|
+ return arr;
|
|
|
+ },
|
|
|
+ // 获取学生
|
|
|
+ async getStudent() {
|
|
|
+ const { classid } = this.form;
|
|
|
+ if (!classid) {
|
|
|
+ this.tip('请选择班级', 'warning');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const res = await this.stuquery({ classid });
|
|
|
+ if (this.$checkRes(res)) {
|
|
|
+ this.$set(this, 'list', res.data);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 获取考勤
|
|
|
+ async getAttendance() {
|
|
|
+ const { classid } = this.form;
|
|
|
+ if (!classid) {
|
|
|
+ this.tip('请选择班级', 'warning');
|
|
|
+ return;
|
|
|
}
|
|
|
+ const res = await this.query({ classid });
|
|
|
if (this.$checkRes(res)) {
|
|
|
- this.$set(this, `total`, newdatas.length);
|
|
|
- let _newdatas = newdatas.splice(skip, skip + limit);
|
|
|
- this.$set(this, `list`, _newdatas);
|
|
|
+ this.$set(this, 'attendList', res.data);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 获取请假
|
|
|
+ async getLeave() {
|
|
|
+ const { classid } = this.form;
|
|
|
+ if (!classid) {
|
|
|
+ this.tip('请选择班级', 'warning');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const res = await this.studentLeave({ classid });
|
|
|
+ if (this.$checkRes(res)) {
|
|
|
+ this.$set(this, 'leaveList', res.data);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 提示通用,本页面测试使用
|
|
|
+ * @param message 提示信息
|
|
|
+ * @param type 提示类型 success/error/warning/info
|
|
|
+ * @param duration 持续时间,默认3000;0为不消失
|
|
|
+ */
|
|
|
+ tip(message, type, duration = 3000) {
|
|
|
+ this.$message({ type, message, duration });
|
|
|
+ },
|
|
|
+ chageWord(type) {
|
|
|
+ let word;
|
|
|
+ switch (type) {
|
|
|
+ case 'ok':
|
|
|
+ word = '签到';
|
|
|
+ break;
|
|
|
+ case 'late':
|
|
|
+ word = '迟到';
|
|
|
+ break;
|
|
|
+ case 'nosign':
|
|
|
+ word = '';
|
|
|
+ break;
|
|
|
+ case 'leave':
|
|
|
+ word = '请假';
|
|
|
+ break;
|
|
|
+ case 'exit':
|
|
|
+ word = '退出';
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ word = '';
|
|
|
+ break;
|
|
|
}
|
|
|
+ return { word };
|
|
|
},
|
|
|
},
|
|
|
watch: {
|
|
|
defaultOption: {
|
|
|
handler(val) {
|
|
|
this.form.termid = this.defaultOption.termid;
|
|
|
- this.getBatch(this.form.termid);
|
|
|
},
|
|
|
deep: true,
|
|
|
},
|