Newer
Older
HuangJiPC / src / pages / views / userJX / components / fullCalenderOption.vue
@zhangdeliang zhangdeliang on 21 Jun 24 KB update
<template>
  <div class="content_box">
    <FullCalendar :options="calendarOptions" class="calenderBox" ref="FullCalendar"> </FullCalendar>
    <!-- 新增培训计划 -->
    <n-modal
      title="新增培训计划"
      :mask-closable="false"
      preset="dialog"
      :show-icon="false"
      :style="{ width: '1800px', height: '500px' }"
      v-model:show="addTrainShow"
    >
      <addTrain :trainStartTime="trainStartTime" :trainEndTime="trainEndTime" @refreshTrainData="refreshTrainData"> </addTrain>
    </n-modal>
    <!-- 培训详情 -->
    <n-modal
      title="培训成果"
      :mask-closable="false"
      preset="dialog"
      :show-icon="false"
      :style="{ width: '1500px' }"
      v-model:show="TrainInfo"
    >
      <n-form ref="formRef" label-align="right" :label-width="100" :model="trainingData.data" label-placement="left">
        <div class="tableName">基本信息</div>
        <div class="topTable">
          <n-space>
            <n-form-item label="培训讲师:" path="trainTeacher">
              <n-input v-model:value="trainingData.data.trainTeacher" style="width: 200px" placeholder="" />
            </n-form-item>
            <n-form-item label="培训主题:" path="trainTopic">
              <n-input v-model:value="trainingData.data.trainTopic" style="width: 200px" placeholder="" />
            </n-form-item>
            <n-form-item label="培训地点:" path="trainAdds">
              <n-input v-model:value="trainingData.data.trainAdds" style="width: 200px" placeholder="" />
            </n-form-item>
            <n-form-item label="培训类型:" path="trainCategory">
              <n-select
                v-model:value="trainingData.data.trainCategory"
                style="width: 200px"
                :options="trainCategoryOptions"
                placeholder=""
                clearable
              />
            </n-form-item>
          </n-space>
          <n-space>
            <n-form-item label="开始时间:" path="trainStartTime">
              <n-date-picker
                v-model:value="trainingData.data.trainStartTime"
                style="width: 200px"
                placeholder=""
                type="datetime"
                clearable
              />
            </n-form-item>
            <n-form-item label="结束时间:" path="trainEndTime">
              <n-date-picker v-model:value="trainingData.data.trainEndTime" style="width: 200px" placeholder="" type="datetime" clearable />
            </n-form-item>
            <n-form-item label="培训方式:" path="trainType">
              <n-select
                v-model:value="trainingData.data.trainType"
                style="width: 200px"
                :options="trainTypeOptions"
                placeholder=""
                clearable
              />
            </n-form-item>
            <n-form-item label="培训时长:" path="trainTime">
              <n-input-number
                v-model:value="trainingData.data.trainTime"
                style="width: 200px"
                :show-button="false"
                min="0"
                placeholder=""
              />
            </n-form-item>
          </n-space>
          <n-form-item label="培训内容:" path="trainRemark">
            <n-input v-model:value="trainingData.data.trainRemark" type="textarea" :autosize="{ minRows: 2 }" placeholder="" />
          </n-form-item>
        </div>
        <div class="tableName">培训成果上报</div>
        <div class="bottomTable">
          <n-radio-group v-model:value="trainresult.type">
            <n-space>
              <!-- 人员签到  培训课件 培训现场照片-->
              <n-radio :value="item.value" v-for="item in radioArr" :key="item.value" @change="changeRadio">
                {{ item.label }}
              </n-radio>
            </n-space>
          </n-radio-group>
          <n-divider />
          <n-form-item v-if="trainresult.type == '0'" label="模板下载链接:">
            <n-button type="warning" size="tiny" @click="downloadqd">培训签到记录模板下载</n-button>
            <n-upload accept=".xlsx,.xls" list-type="text" @change="handleChangeupload">
              <n-button style="left: 1000px">上传文件</n-button>
            </n-upload>
          </n-form-item>
          <n-data-table
            v-if="trainresult.type == '0'"
            :bordered="true"
            :single-line="false"
            :max-height="700"
            :striped="true"
            :columns="Listcolumns"
            :data="tableListData"
            :remote="true"
          >
          </n-data-table>
          <n-upload
            v-if="trainresult.type == '1'"
            v-model:file-list="uploadFile1"
            accept=".ppt,.pptx,.doc,.docx,.pdf"
            @change="changeFileKJ"
          >
            <n-button>点击上传</n-button>
          </n-upload>
          <n-upload
            v-if="trainresult.type == '2'"
            v-model:file-list="uploadFile2"
            accept=".jpg,.png,.jpeg,.svg,.gif"
            list-type="image-card"
            @change="changeFileXC"
          >
          </n-upload>
        </div>
      </n-form>
      <template #action>
        <n-space style="margin-right: 650px">
          <n-button type="primary" @click="UpdataTrainInfo()">保存 </n-button>
          <n-button @click="cancelTrainInfo()">取消</n-button>
        </n-space>
      </template>
    </n-modal>
  </div>
</template>
<script>
import { reactive, toRefs, onMounted, ref } from 'vue';
import addTrain from './addTraining.vue';
import { getrainingInfo, updatatrainingInfo, chievementUpload, fileUpload, fileDelete } from '@/services';
import { resetForm, formatDate } from '@/utils/util';
import '@fullcalendar/core/vdom'; // solves problem with Vite
import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import resourceTimelinePlugin from '@fullcalendar/resource-timeline';
import interactionPlugin from '@fullcalendar/interaction';
import { downloadBlob } from '@/utils/util';
import axios from 'axios';
import moment from 'moment';
export default {
  name: 'calendarOptions',
  components: {
    FullCalendar,
    addTrain,
  },
  setup() {
    const allData = reactive({
      calendarOptions: {
        // 引入的插件,比如fullcalendar/daygrid,fullcalendar/timegrid引入后才可显示月,周,
        plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin, resourceTimelinePlugin],
        droppable: true, //是否可拖拽
        eventColor: '', // 全部日历日程背景色
        eventTextColor: '#ffffff', //日历中事件的默认文本颜色,优先级低于添加事件时设置的文本色
        initialDate: moment().format('YYYY-MM-DD'), // 自定义设置背景颜色时一定要初始化日期时间
        initialView: 'dayGridMonth', // 默认为那个视图(月:dayGridMonth,周:timeGridWeek,日:timeGridDay)
        buttonText: {
          today: '今天',
          month: '月',
          week: '周',
          day: '日',
        },
        locale: 'zh-cn',
        editable: true, //是否允许修改事件
        eventStartEditable: true, //事件开始时间是否可拖动修改,优先级低于添加事件时设置的值
        eventDurationEditable: true, //事件结束时间是否可拖动修改,优先级低于添加事件时设置的值
        selectable: true, //是否允许用户通过单击或拖动选择日历中的对象,包括天和时间。
        allDaySlot: true, //是否显示allDay

        headerToolbar: {
          // 日历头部按钮位置
          left: 'title',
          center: 'prevYear,prev,next,nextYear',
          right: 'today,dayGridMonth,timeGridWeek,timeGridDay',
        },
        slotLabelFormat: {
          hour: '2-digit',
          minute: '2-digit',
          meridiem: false,
          hour12: false, // 设置时间为24小时
        },
        contentHeight: 850,
        editable: true, // 是否可以进行(拖动、缩放)修改
        eventStartEditable: true, // Event日程开始时间可以改变,默认true,如果是false其实就是指日程块不能随意拖动,只能上下拉伸改变他的endTime
        eventDurationEditable: true, // Event日程的开始结束时间距离是否可以改变,默认true,如果是false则表示开始结束时间范围不能拉伸,只能拖拽
        selectable: true, // 是否可以选中日历格
        selectMirror: true,
        selectMinDistance: 8, // 选中日历格的最小距离
        dayMaxEvents: true,
        weekends: true,
        navLinks: true, // 天链接
        slotEventOverlap: false, // 相同时间段的多个日程视觉上是否允许重叠,默认true允许
        // 事件
        eventDrop: EventsDrop, // 日程事件拖拽完成并且时间改变之后触发
        // eventResize: this.eventResize, // 日程事件的时间范围大小被改变成功之后触发
        select: addTraining, // 选中日历格事件
        eventClick: TrainingInfo, //点击日历日程事件
        // datesRender: this.handleDatesRender,
        // dayClick: this.dayClick, //日历点击事件
        events: [
          {
            id: '',
            title: '新员工入职培训 14:30',
            start: '2022-12-09',
            end: '2022-12-10',
            backgroundColor: 'blue',
            borderColor: 'blue',
            editable: true,
          },
        ],
        customButtons: {
          prev: {
            // this overrides the prev button
            text: 'PREV',
            click: () => {
              prev();
            },
          },
          next: {
            // this overrides the next button
            text: 'NEXT',
            click: () => {
              next();
            },
          },
          prevYear: {
            // this overrides the prevYear button
            text: 'PREVYEAR',
            click: () => {
              prevYear();
            },
          },
          nextYear: {
            // this overrides the prevYear button
            text: 'NEXTYEAR',
            click: () => {
              nextYear();
            },
          },
        },
      },
      addTrainShow: false,
      TrainInfo: false,
      time1: null,
      time2: null,
      trainStartTime: null, //培训开始时间
      trainEndTime: null, //培训结束时间
      trainingData: {
        data: {
          color: '',
          id: null,
          trainTeacher: null,
          trainTopic: null,
          trainRemark: null,
          trainAdds: null,
          trainStartTime: null,
          trainEndTime: null,
          trainFiles: [],
          trainPics: [],
          trainScoreLogs: [],
          trainType: '',
          trainTime: null,
          trainCategory: '',
        },
      },
      trainresult: {
        type: '0',
      },
      radioArr: [
        { value: '0', label: '人员签到记录' },
        { value: '1', label: '培训课件' },
        { value: '2', label: '现场培训照片' },
      ],
      trainCategoryOptions: [
        { value: '0', label: '入职培训' },
        { value: '1', label: '过程培训' },
      ],
      trainTypeOptions: [
        { value: '0', label: 'PPT' },
        { value: '1', label: 'Word' },
        { value: '2', label: '实操' },
      ],
      uploadFile1: [],
      imageXC: [],
      fileKJ: [],
      uploadFile2: [],
      tableListData: [],
      Listcolumns: [
        { title: '参会人员姓名', align: 'center', key: 'userName' },
        { title: '岗位', align: 'center', key: 'userJobDesc' },
        { title: '职级', align: 'center', key: 'jobLevel' },
        { title: '联系方式', align: 'center', key: 'userMobile' },
        { title: '评分', align: 'center', key: 'trainScore' },
        { title: '评语', align: 'center', key: 'trainComment' },
      ],
      dataAll: [],
      currentData: {
        color: '',
        id: null,
        trainTeacher: null,
        trainTopic: null,
        trainRemark: null,
        trainAdds: null,
        trainStartTime: null,
        trainEndTime: null,
        trainFiles: [],
        trainPics: [],
        trainScoreLogs: [],
        trainType: '',
        trainTime: null,
        trainCategory: '',
      },
    });
    const prev = () => {
      FullCalendar.value.getApi().prev();
      const time0 = FullCalendar.value.getApi().getDate();
      allData.time1 = moment(time0).startOf('month').format('YYYY-MM-DD 00:00:00');
      allData.time2 = moment(time0).endOf('month').format('YYYY-MM-DD 23:59:59');
      getPlan(allData.time1, allData.time2);
    };
    const next = () => {
      FullCalendar.value.getApi().next();
      const time0 = FullCalendar.value.getApi().getDate();
      allData.time1 = moment(time0).startOf('month').format('YYYY-MM-DD 00:00:00');
      allData.time2 = moment(time0).endOf('month').format('YYYY-MM-DD 23:59:59');
      getPlan(allData.time1, allData.time2);
    };
    const prevYear = () => {
      FullCalendar.value.getApi().prevYear();
      const time0 = FullCalendar.value.getApi().getDate();
      allData.time1 = moment(time0).startOf('month').format('YYYY-MM-DD 00:00:00');
      allData.time2 = moment(time0).endOf('month').format('YYYY-MM-DD 23:59:59');
      getPlan(allData.time1, allData.time2);
    };
    const nextYear = () => {
      FullCalendar.value.getApi().nextYear();
      const time0 = FullCalendar.value.getApi().getDate();
      allData.time1 = moment(time0).startOf('month').format('YYYY-MM-DD 00:00:00');
      allData.time2 = moment(time0).endOf('month').format('YYYY-MM-DD 23:59:59');
      getPlan(allData.time1, allData.time2);
    };
    const FullCalendar = ref(null);
    //获取培训信息
    const getPlan = async (strtime, endtime) => {
      const params = {
        current: 1,
        data: {
          startTime: strtime,
          endTime: endtime,
        },
        size: 999,
      };
      let res = await getrainingInfo(params);
      if (res.code == 200) {
        let datas = res.data.records;
        allData.calendarOptions.events = [];
        allData.dataAll = datas;
        datas.forEach((item) => {
          const flag = item.trainFiles.length > 0 || item.trainPics.length > 0 || item.trainScoreLogs.length > 0;
          const obj = {
            id: item.id,
            title: flag ? item.trainTopic + '(成果已上传)' : item.trainTopic,
            start: item.trainStartTime !== null ? item.trainStartTime.split(' ')[0] : null,
            end: item.trainEndTime !== null ? item.trainEndTime.split(' ')[0] : null,
            backgroundColor: item.color || 'blue',
            borderColor: item.color || 'blue',
            editable: !flag,
          };
          allData.calendarOptions.events.push(obj);
        });
      }
    };
    //新增培训弹窗
    function addTraining(info) {
      allData.addTrainShow = true;
      allData.trainStartTime = formatDate(info.startStr, 'YYYY-MM-DD hh:mm:ss');
      allData.trainEndTime = formatDate(info.endStr, 'YYYY-MM-DD hh:mm:ss');
    }
    //保存培训新增信息
    function refreshTrainData() {
      allData.addTrainShow = false;
      getPlan(allData.time1, allData.time2);
    }
    //培训详情弹窗
    function TrainingInfo(e) {
      const id = e.event.id;
      allData.TrainInfo = true;
      allData.tableListData = [];
      allData.uploadFile1 = [];
      allData.uploadFile2 = [];
      allData.trainingData.data.trainPics = [];
      allData.trainingData.data.trainFiles = [];
      allData.trainingData.data.trainScoreLogs = [];
      allData.currentData = allData.dataAll.find((item) => item.id * 1 === id * 1);
      allData.trainingData.data.trainTeacher = allData.currentData.trainTeacher;
      allData.trainingData.data.trainRemark = allData.currentData.trainRemark;
      allData.trainingData.data.trainStartTime = Date.parse(allData.currentData.trainStartTime);
      allData.trainingData.data.trainEndTime = Date.parse(allData.currentData.trainEndTime);
      allData.trainingData.data.trainAdds = allData.currentData.trainAdds;
      allData.trainingData.data.trainTopic = allData.currentData.trainTopic;
      allData.trainingData.data.id = allData.currentData.id;
      allData.trainingData.data.color = allData.currentData.color;
      allData.trainingData.data.trainType = allData.currentData.trainType;
      allData.trainingData.data.trainTime = allData.currentData.trainTime;
      allData.trainingData.data.trainCategory = allData.currentData.trainCategory;
      allData.trainingData.data.trainScoreLogs = allData.currentData.trainScoreLogs;
      allData.tableListData = allData.currentData.trainScoreLogs;
      if (allData.currentData.trainFiles != null && allData.currentData.trainFiles.length > 0) {
        allData.currentData.trainFiles.forEach((item) => {
          let param = {
            fileNo: item.fileNo,
            name: item.fileOriginalName,
            status: 'finished',
            url: item.fileCloudStorageKey,
          };
          allData.uploadFile1.push(param);
          allData.fileKJ.push(param);
          allData.trainingData.data.trainFiles.push(item.fileNo);
        });
      }
      if (allData.currentData.trainPics != null && allData.currentData.trainPics.length > 0) {
        allData.currentData.trainPics.forEach((item) => {
          let param = {
            fileNo: item.fileNo,
            name: item.fileOriginalName,
            status: 'finished',
            url: item.fileCloudStorageKey,
          };
          allData.uploadFile2.push(param);
          allData.imageXC.push(param);
          allData.trainingData.data.trainPics.push(item.fileNo);
        });
      }
    }
    // 上报类型类型点击切换
    function changeRadio(e) {
      allData.trainresult.type = e.target.value;
    }
    //保存培训成果信息
    const UpdataTrainInfo = async () => {
      let params = { ...allData.trainingData.data };
      params.trainStartTime = formatDate(allData.trainingData.data.trainStartTime, 'YYYY-MM-DD hh:mm:ss');
      params.trainEndTime = formatDate(allData.trainingData.data.trainEndTime, 'YYYY-MM-DD hh:mm:ss');
      let res = await updatatrainingInfo(params);
      if (res && res.code == 200) {
        $message.success('操作成功');
        getPlan(allData.time1, allData.time2);
        allData.TrainInfo = false;
        allData.trainingData.data.trainPics = [];
        allData.trainingData.data.trainFiles = [];
        allData.trainingData.data.trainScoreLogs = [];
      }
    };
    //取消保存培训成果
    const cancelTrainInfo = () => {
      allData.TrainInfo = false;
      allData.trainingData.data.trainPics = [];
      allData.trainingData.data.trainFiles = [];
      allData.trainingData.data.trainScoreLogs = [];
    };
    //拖拽事件获取到的开始事件和结束事件
    function EventsDrop(info) {
      const id = info.event.id;
      allData.currentData = allData.dataAll.find((item) => item.id * 1 === id * 1);
      allData.trainingData.data.trainStartTime = Date.parse(info.event.startStr);
      allData.trainingData.data.trainEndTime = Date.parse(info.event.endStr);
      allData.trainingData.data.trainAdds = allData.currentData.trainAdds;
      allData.trainingData.data.trainTopic = allData.currentData.trainTopic;
      allData.trainingData.data.id = allData.currentData.id;
      allData.trainingData.data.color = allData.currentData.color;
      allData.trainingData.data.trainTeacher = allData.currentData.trainTeacher;
      allData.trainingData.data.trainRemark = allData.currentData.trainRemark;
      allData.trainingData.data.trainType = allData.currentData.trainType;
      allData.trainingData.data.trainTime = allData.currentData.trainTime;
      allData.trainingData.data.trainCategory = allData.currentData.trainCategory;
      UpdataTrainInfo();
    }
    //上传签到记录
    const handleChangeupload = async (file) => {
      if (file.event) {
        // 文件上传
        let formdata = new FormData();
        formdata.append('file', file.file.file);
        formdata.append('trainId', allData.trainingData.data.id);
        let config = {
          headers: { 'Content-Type': 'multipart/form-data' },
        };
        let res = await chievementUpload(formdata, config);
        if (res && res.code === 200) {
          $message.success('上传成功');
          if (res.data.length > 0) {
            let datas = res.data;
            datas.forEach((item) => {
              allData.trainingData.data.trainScoreLogs.push(item);
            });
            allData.tableListData = allData.trainingData.data.trainScoreLogs;
          }
        }
      }
    };
    // 课件文件上传和删除
    const changeFileKJ = async (file) => {
      $loadingBar.start();
      if (file.event) {
        // 文件上传
        let formdata = new FormData();
        formdata.append('files', file.file.file);
        formdata.append('trainId', allData.trainingData.data.id);
        let config = {
          headers: { 'Content-Type': 'multipart/form-data' },
        };
        let res = await fileUpload(formdata, config);
        if (res && res.code === 200) {
          if (res.data.length > 0) {
            let datas = res.data[0];
            allData.trainingData.data.trainFiles.push(datas.fileNo);
            let param = {
              fileNo: datas.fileNo,
              name: datas.fileOriginalName,
              url: datas.fileCloudStorageKey,
              status: 'success',
            };
            allData.fileKJ.push(param);
          }
        } else {
          allData.uploadFile1 = [];
          let param = {
            fileNo: file.file.id,
            name: file.file.name,
            status: 'error',
          };
          allData.uploadFile1.push(param);
        }
        $loadingBar.finish();
      } else {
        // 文件删除,根据文件名进行匹配
        let fileIndex = null;
        allData.fileKJ.map((item, index) => {
          if (file.file.name == item.name) {
            fileIndex = index;
          }
        });
        let fileNos = [];
        fileNos.push(allData.fileKJ[fileIndex].fileNo);
        let res = await fileDelete(fileNos);
        if (res && res.code === 200) {
          allData.trainingData.data.trainFiles.splice(fileIndex, 1);
        }
      }
    };
    // 现场图片文件上传和删除
    const changeFileXC = async (file) => {
      $loadingBar.start();
      if (file.event) {
        // 文件上传
        let formdata = new FormData();
        formdata.append('files', file.file.file);
        formdata.append('trainId', allData.trainingData.data.id);
        let config = {
          headers: { 'Content-Type': 'multipart/form-data' },
        };
        let res = await fileUpload(formdata, config);
        if (res && res.code === 200) {
          if (res.data.length > 0) {
            let datas = res.data[0];
            allData.trainingData.data.trainPics.push(datas.fileNo);
            let param = {
              fileNo: datas.fileNo,
              name: datas.fileOriginalName,
              url: datas.fileCloudStorageKey,
              status: 'success',
            };
            allData.imageXC.push(param);
          }
        } else {
          allData.uploadFile2 = [];
          let param = {
            fileNo: file.file.id,
            name: file.file.name,
            status: 'error',
          };
          allData.uploadFile2.push(param);
        }
        $loadingBar.finish();
      } else {
        // 文件删除,根据文件名进行匹配
        let fileIndex = null;
        allData.imageXC.map((item, index) => {
          if (file.file.name == item.name) {
            fileIndex = index;
          }
        });
        let fileNos = [];
        fileNos.push(allData.imageXC[fileIndex].fileNo);
        let res = await fileDelete(fileNos);
        if (res && res.code === 200) {
          allData.trainingData.data.trainPics.splice(fileIndex, 1);
        }
      }
    };
    // 获取人员签到模板
    function downloadqd() {
      const token = localStorage.getItem('tokenXF');
      $loadingBar.start();
      axios
        .get('/api/personAchievements/trainplanachievement/downloadPersonnelSignRecordTemplate', {
          headers: {
            token: token,
          },
          responseType: 'blob',
          params: {},
        })
        .then(function (res) {
          downloadBlob(res.data, decodeURIComponent(res.headers['content-disposition'].split('filename=')[1]));
        });
      $loadingBar.finish();
    }
    onMounted(() => {
      const time0 = FullCalendar.value.getApi().currentData.currentDate;
      allData.time1 = moment(time0).startOf('month').format('YYYY-MM-DD 00:00:00');
      allData.time2 = moment(time0).endOf('month').format('YYYY-MM-DD 23:59:59');
      getPlan(allData.time1, allData.time2);
    });
    return {
      ...toRefs(allData),
      changeRadio,
      FullCalendar,
      getPlan,
      UpdataTrainInfo,
      EventsDrop,
      refreshTrainData,
      handleChangeupload,
      prev,
      next,
      prevYear,
      downloadqd,
      changeFileXC,
      changeFileKJ,
      cancelTrainInfo,
    };
  },
};
</script>
<style lang="less" scoped>
.tableName {
  border-bottom: 2px solid rgb(20, 217, 215);
  font-size: 16px;
  font-weight: bold;
  margin-bottom: 10px;
}
</style>