zouyu
4 小时以前 8fc1138d2f2115121fd15b679ca1a20fc2ba838a
绩效管理:工时汇总对接MES数据&人员考勤调整
已修改8个文件
已删除1个文件
451 ■■■■■ 文件已修改
src/api/performance/manHour.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/connect.js 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/date.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/inspection.vue 94 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/performance/attendance/components/staffClockInRecord.vue 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/performance/attendance/index.vue 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/performance/class/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/performance/competency/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/performance/manHour/workTimeStatistics.vue 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/performance/manHour.js
@@ -11,6 +11,16 @@
  });
}
// å¯¼å‡ºå·¥æ—¶æ±‡æ€»
export function exportWorkHoursTotal(query) {
  return request({
    url: "/auxiliaryOriginalHours/exportWorkHoursTotal",
    method: "get",
    params: query,
    responseType : "blob",
  });
}
// æŸ¥è¯¢è¾…助工时
export function selectAuxiliaryWorkingHoursDay(query) {
  return request({
src/utils/connect.js
ÎļþÒÑɾ³ý
src/utils/date.js
@@ -1,3 +1,4 @@
import dayjs from 'dayjs';
/**
 * èŽ·å–å¹´æœˆæ—¥
 */
@@ -58,7 +59,7 @@
 * @param date
 * @returns {string}
 */
export function getYearAndMonthAndDaysZTZB(date = new Date()) {
export function getYearAndMonthAndDaysZTNS(date = new Date()) {
  let year = date.getFullYear();
  let month = date.getMonth();
  let days = date.getDate();
@@ -71,11 +72,61 @@
      year++;
    }
  }
  month += 1;
  month = month < 10 ? '0' + month + '-' : month + '-';
  year = year + '-';
  days = days < 10 ? '0' + days : days;
  return (year + month + days);
}
}
/**
 * èŽ·å–æ—¶é—´èŒƒå›´ï¼š26号-25号
 * åˆå§‹åŒ–默认日期范围:近一个月(当前日期 - 30天 è‡³ å½“前日期)
 * @param format
 * @returns {*[]}
 */
export function getTimeRange(format = 'YYYY-MM-DD HH:mm:ss') {
  // èŽ·å–å½“å‰æ—¶é—´
  const now = dayjs();
  // èŽ·å–å½“å‰æ—¥æœŸçš„ã€Œæ—¥ã€ï¼ˆ1-31)
  const currentDate = now.date();
  let startTime, endTime;
  // æ ¸å¿ƒé€»è¾‘:判断当前日期是否大于25号
  if (currentDate > 25) {
    // âœ… æƒ…况1:当前日>25 â†’ å½“月26号 ~ æ¬¡æœˆ25号
    startTime = now.startOf('month').add(25, 'day'); // å½“月1号 +25天 = 26号
    endTime = startTime.add(1, 'month').date(25).hour(23)
      .minute(59)
      .second(59);     // æ¬¡æœˆ25号(dayjs自动处理跨年)
  } else {
    // âœ… æƒ…况2:当前日≤25 â†’ ä¸Šæœˆ26号 ~ å½“月25号
    startTime = now.subtract(1, 'month').startOf('month').add(25, 'day'); // ä¸Šæœˆ26号
    endTime = now.date(25).hour(23)
      .minute(59)
      .second(59); // å½“月25号
  }
  // è¿”回格式化后的时间数组
  return [startTime.format(format), endTime.format(format)];
}
/**
 * èŽ·å–ç­æ¬¡æœˆä»½
 * @returns {*}
 */
export function getWorkMonth(){
  // èŽ·å–å½“å‰æ—¶é—´
  const now = dayjs();
  // èŽ·å–å½“å‰æ—¥æœŸçš„ã€Œæ—¥ã€ï¼ˆ1-31)
  const currentDate = now.date();
  let workMonth;
  // æ ¸å¿ƒé€»è¾‘:判断当前日期是否大于25号
  if (currentDate > 25) {
    workMonth = now.add(1, 'month')
  }else{
    workMonth = now
  }
  return workMonth
}
src/views/business/inspectionTask/inspection.vue
@@ -188,7 +188,7 @@
          <el-button :loading="dataAcquisitionLoading" v-if="state == 1" size="small" type="primary"
            @click="getDataAcquisitionDevice">数据采集</el-button>
          <el-button :type="dataAcquisitionEidtAble ? '' : 'primary'" v-if="state == 1" size="small"
            @click="dataAcquisitionEidtAble = !dataAcquisitionEidtAble,disconnect()">{{ dataAcquisitionEidtAble ? "关闭编辑" : "编辑数采"
            @click="dataAcquisitionEidtAble = !dataAcquisitionEidtAble">{{ dataAcquisitionEidtAble ? "关闭编辑" : "编辑数采"
            }}</el-button>
        </div>
      </div>
@@ -650,7 +650,6 @@
import html2canvas from "html2canvas";
import { mapGetters } from "vuex";
import viewManHourDia from "@/views/business/inspectionTask/components/viewManHourDia.vue";
import {connect,disconnect,weightList} from "@/utils/connect";
import {getOneByIfsId,saveOrUpdateProps} from "@/api/business/ifsPartProps.js";
import {getDicts} from "@/api/system/dict/data";
export default {
@@ -704,8 +703,6 @@
        sampleName: null,
        state: null,
      },
      isSerialConnected: false, // æ–°å¢žçŠ¶æ€å˜é‡ï¼Œä¸²å£è¿žæŽ¥çŠ¶æ€
      serialPort: null, // å­˜å‚¨ä¸²å£å¯¹è±¡
      id: null,
      changeType: null,
      insOrder: {},
@@ -996,11 +993,6 @@
  beforeDestroy() {
    // åœ¨ç»„件销毁前确保停止 Worker,避免内存泄漏
    this.stopWorker();
    disconnect();
    // è°ƒç”¨å‰å…ˆåˆ¤æ–­æ–¹æ³•是否存在,避免报错
    if (this.closeSerialPort) {
      this.closeSerialPort();// ç»„件销毁前关闭串口
    }
  },
  methods: {
    validateQuality(){
@@ -1084,9 +1076,6 @@
      this.$nextTick(()=>{
        this.ifsMaterialPropsVisible = true;
      })
    },
    disconnect() {
      return disconnect
    },
    // æ–‡ä»¶ç®¡ç†--开始
    getList() {
@@ -1328,58 +1317,32 @@
    // æ•°æ®é‡‡é›†
  getDataAcquisitionDevice() {
      (async () => {
        try {
          // æ£€æŸ¥é¡µé¢ä¸­æ£€æµ‹é¡¹æ˜¯å¦åŒ…含密度
          const hasDensityItem = this.currentSample.insProduct.some(item => {
            // å‡è®¾å¯†åº¦ç›¸å…³çš„æ£€æµ‹é¡¹åç§°åŒ…含 "密度" å…³é”®å­—,可按需修改
            return item.inspectionItem && item.inspectionItem.includes('密度');
          });
          if (hasDensityItem) {
            // æ£€æŸ¥æ˜¯å¦æ”¯æŒ Web Serial API
            if ('serial' in navigator) {
              // è¯·æ±‚可用串口
              const ports = await navigator.serial.getPorts();
              if (ports.length > 0) {
                await connect();
              } else {
                console.log('没有可用的串口');
              }
            } else {
              console.log('当前浏览器不支持 Web Serial API');
            }
          }
        } catch (error) {
          console.error('检测串口时出错:', error);
        }
      })().then(() => {
        let itemIds = [];
        this.currentSample.insProduct.forEach((item) => {
          if (item.inspectionItemType === "1") {
            itemIds.push(item.id);
          }
        });
        const params = {
          entrustCode: this.insOrder.entrustCode,
          lotBatchNo: this.insOrder.lotBatchNo,
          sampleCode: this.currentSample.sampleCode,
          id: this.currentSample.id,
          itemIds: itemIds,
        };
        this.dataAcquisitionLoading = true;
        dataCollection(params).then((res) => {
          this.dataAcquisitionLoading = false;
          if (res.code != 200) {
            return;
          }
          this.dataAcquisitionInfoNew = this.HaveJson(res.data);
          // å¯¹æ•°é‡‡å›žæ¥çš„值进行处理
          this.handleDataAcquisition(res.data);
        }).catch(err => {
          this.dataAcquisitionLoading = false;
        });
      });
    },
    let itemIds = [];
    this.currentSample.insProduct.forEach((item) => {
      if (item.inspectionItemType === "1") {
        itemIds.push(item.id);
      }
    });
    const params = {
      entrustCode: this.insOrder.entrustCode,
      lotBatchNo: this.insOrder.lotBatchNo,
      sampleCode: this.currentSample.sampleCode,
      id: this.currentSample.id,
      itemIds: itemIds,
    };
    this.dataAcquisitionLoading = true;
    dataCollection(params).then((res) => {
      this.dataAcquisitionLoading = false;
      if (res.code != 200) {
        return;
      }
      this.dataAcquisitionInfoNew = this.HaveJson(res.data);
      // å¯¹æ•°é‡‡å›žæ¥çš„值进行处理
      this.handleDataAcquisition(res.data);
    }).catch(err => {
      this.dataAcquisitionLoading = false;
    });
  },
    objectOrder(obj) {
      let newkey = Object.keys(obj).sort();
      let newObj = {};
@@ -3155,9 +3118,6 @@
    goback() {
      this.$router.go(-1)
    }
  },
  destroyed() {
    disconnect()
  },
};
</script>
src/views/performance/attendance/components/staffClockInRecord.vue
@@ -20,14 +20,20 @@
      //进出门类型列表
      enterOrExitList:[
        {
          effect:'',
          type:'success',
          label:'进门',
          value:1
        },
        {
          effect:'',
          type:'',
          label:'出门',
          value:2
        },
        {
          effect:'',
          type:'info',
          label:'进/出门',
          value:3
        }
@@ -311,13 +317,18 @@
      return label;
    },
    formatterEnterOrExitType(value){
      let label = "";
      let type = {
        effect:'plain',
        type:'warning',
        label:'未知',
        value:value
      };
      this.enterOrExitList.forEach(item=>{
        if(item.value===value){
          label = item.label
          type = item
        }
      })
      return label;
      return type;
    },
    getDataSourceTypeTag(type) {
      const tagMap = {
@@ -353,12 +364,12 @@
        :data="tableData"
        border
        style="width: 100%"
        height="300"
        max-height="900"
        :header-cell-style="{textAlign:'center'}"
        :cell-style="{textAlign:'center'}"
      >
        <el-table-column type="index" label="序号" width="60"></el-table-column>
        <el-table-column label="是否纳入考勤" prop="enableReport" width="120">
        <el-table-column fixed="left" type="index" label="序号" width="60"></el-table-column>
        <el-table-column fixed="left" label="是否纳入考勤" prop="enableReport" width="120">
          <template slot-scope="scope">
            <el-switch
              @change="changeEnableReport(scope.row)"
@@ -368,15 +379,19 @@
            </el-switch>
          </template>
        </el-table-column>
        <el-table-column prop="personCode" label="工号" min-width="150" width="150"></el-table-column>
        <el-table-column prop="personName" label="姓名" min-width="150" width="150"></el-table-column>
        <el-table-column prop="deptName" label="部门名称" min-width="150" width="150"></el-table-column>
        <el-table-column prop="cardNumber" label="卡号" min-width="150" width="150"></el-table-column>
        <el-table-column fixed="left" prop="personCode" label="工号" min-width="120" width="120"></el-table-column>
        <el-table-column fixed="left" prop="personName" label="姓名" min-width="120" width="120"></el-table-column>
        <el-table-column prop="deptName" label="部门名称" min-width="100" width="100"></el-table-column>
        <el-table-column prop="cardNumber" label="卡号" min-width="120" width="120" show-overflow-tooltip></el-table-column>
        <el-table-column prop="enterOrExit" label="进出门类型" min-width="100" width="100" >
          <template slot-scope="scope">
            <el-tag :type="formatterEnterOrExitType(scope.row.enterOrExit).type" :effect="formatterEnterOrExitType(scope.row.enterOrExit).effect">{{formatterEnterOrExitType(scope.row.enterOrExit).label}}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="swingTime" label="刷卡时间" min-width="180" width="180"></el-table-column>
        <el-table-column prop="channelName" label="考勤点通道名称" min-width="180" width="180"></el-table-column>
        <el-table-column prop="deviceName" label="考勤设备名称" min-width="150" width="150"></el-table-column>
        <el-table-column prop="deviceCode" label="考勤设备编码" min-width="150" width="150"></el-table-column>
        <el-table-column prop="enterOrExit" label="进出门类型" min-width="150" width="150" :formatter="(row)=>formatterEnterOrExitType(row.enterOrExit)"></el-table-column>
        <el-table-column prop="openType" label="开门类型" min-width="150" width="150" :formatter="(row)=>formatterAttendanceType(row.openType)"></el-table-column>
        <el-table-column prop="isSync" label="数据来源" min-width="150" width="150">
          <template slot-scope="scope">
src/views/performance/attendance/index.vue
@@ -225,7 +225,7 @@
  syncAttendanceRecord
} from '@/api/performance/attendance'
import {getDicts} from "@/api/system/dict/data";
import dayjs from 'dayjs';
import {getTimeRange} from "@/utils/date";
export default {
  name: "Attendance",
  components: {
@@ -299,7 +299,7 @@
    this.selectEnumByCategory()
    this.getUserList()
    this.getTableHeight();
    this.dateRange = this.getTimeRange()
    this.dateRange = getTimeRange()
    this.resizeHandler = this.debounce(() => {
      this.getTableHeight();
    }, 200);
@@ -332,35 +332,7 @@
        this.dailyTypeList = response.data;
      });
    },
    /**
     * åˆå§‹åŒ–默认日期范围:近一个月(当前日期 - 30天 è‡³ å½“前日期)
     */
    getTimeRange(format = 'YYYY-MM-DD HH:mm:ss') {
      // èŽ·å–å½“å‰æ—¶é—´
      const now = dayjs();
      // èŽ·å–å½“å‰æ—¥æœŸçš„ã€Œæ—¥ã€ï¼ˆ1-31)
      const currentDate = now.date();
      let startTime, endTime;
      // æ ¸å¿ƒé€»è¾‘:判断当前日期是否大于25号
      if (currentDate > 25) {
        // âœ… æƒ…况1:当前日>25 â†’ å½“月26号 ~ æ¬¡æœˆ25号
        startTime = now.startOf('month').add(25, 'day'); // å½“月1号 +25天 = 26号
        endTime = startTime.add(1, 'month').date(25).hour(23)
          .minute(59)
          .second(59);     // æ¬¡æœˆ25号(dayjs自动处理跨年)
      } else {
        // âœ… æƒ…况2:当前日≤25 â†’ ä¸Šæœˆ26号 ~ å½“月25号
        startTime = now.subtract(1, 'month').startOf('month').add(25, 'day'); // ä¸Šæœˆ26号
        endTime = now.date(25).hour(23)
          .minute(59)
          .second(59); // å½“月25号
      }
      // è¿”回格式化后的时间数组
      return [startTime.format(format), endTime.format(format)];
    },
    //同步考勤记录
    confirmSyncAttendance(){
      if(!this.syncDateRange || this.syncDateRange.length<2){
@@ -384,7 +356,10 @@
    },
    //打开同步考勤记录弹框
    openSyncAttendanceDialog(){
      this.syncAttendanceVisible = true
      this.syncDateRange = getTimeRange()
      this.$nextTick(()=>{
        this.syncAttendanceVisible = true
      })
    },
    //删除考勤记录
    confirmRemoveRecord(row){
@@ -575,7 +550,7 @@
    },
    //重置按钮
    resetQuery() {
      this.dateRange = this.getTimeRange();
      this.dateRange = getTimeRange();
      this.queryParams = {
        startDate: "",
        endDate: "",
src/views/performance/class/index.vue
@@ -324,6 +324,7 @@
  editAnnotationText,
  delAnnotationText
} from "@/api/performance/class";
import {getWorkMonth} from "@/utils/date";
export default {
  name: 'Class',
  data() {
@@ -332,8 +333,7 @@
        userName: "",
        laboratory: "",
        year: new Date(),
        month: new Date().getMonth() + 1,
        // month:''
        month: getWorkMonth().month()+1,
      },
      monthOptions: [
        {
src/views/performance/competency/index.vue
@@ -58,16 +58,18 @@
      }"
      border
    >
      <el-table-column type="index" label="序号" width="80"></el-table-column>
      <el-table-column type="index" label="序号" width="80" fixed="left"></el-table-column>
      <el-table-column
        prop="userName"
        label="人员名称"
        min-width="120"
        fixed="left"
        width="120"
      ></el-table-column>
      <el-table-column
        prop="postName"
        label="岗位"
        fixed="left"
        min-width="120"
        width="120"
      ></el-table-column>
@@ -132,8 +134,10 @@
    this.getLevelDict()
    this.getTableHeader();
    this.getList();
    this.getTableHeight()
    this.getPostList()
    this.$nextTick(()=>{
      this.getTableHeight()
    })
  },
  mounted() {
    window.addEventListener("resize", this.getTableHeight);
src/views/performance/manHour/workTimeStatistics.vue
@@ -30,27 +30,59 @@
            <el-form-item>
              <el-button type="primary" size="mini" @click="refreshTable">查询</el-button>
              <el-button size="mini" @click="refresh">重置</el-button>
              <el-button :loading="downloadLoading" type="success" size="mini" @click="exportWorkHoursTotal">导出</el-button>
            </el-form-item>
          </el-form>
        </div>
      </div>
      <limsTable
        :column="tableColumn"
        :table-data="tableData"
        :table-loading="tableLoading"
        :page="page"
<!--      <limsTable-->
<!--        :column="tableColumn"-->
<!--        :table-data="tableData"-->
<!--        :table-loading="tableLoading"-->
<!--        :page="page"-->
<!--        :height="'calc(100vh - 290px)'"-->
<!--        @pagination="pagination"-->
<!--      >-->
<!--      </limsTable>-->
      <el-table
        :data="tableData"
        v-loading="tableLoading"
        :height="'calc(100vh - 290px)'"
        @pagination="pagination"
        :cell-style="{ textAlign: 'center' }"
        :header-cell-style="{ background: '#f8f8f9', color: '#515a6e', textAlign: 'center' }"
        border
      >
      </limsTable>
        <el-table-column type="index" label="序号"></el-table-column>
        <el-table-column prop="userName" label="姓名" min-width="120"></el-table-column>
        <el-table-column prop="month" label="月份" min-width="120"></el-table-column>
        <el-table-column label="LIMS">
          <el-table-column prop="yieldHour" label="产量工时" min-width="120"></el-table-column>
          <el-table-column prop="subsidiaryHour" label="辅助工时" min-width="120"></el-table-column>
          <el-table-column prop="totalHour" label="总工时" min-width="120"></el-table-column>
        </el-table-column>
        <el-table-column label="耐丝域MES">
          <el-table-column prop="operationPerformanceByNS" label="工序绩效" min-width="120"></el-table-column>
          <el-table-column prop="productPerformanceByNS" label="成品绩效" min-width="120"></el-table-column>
          <el-table-column prop="onsiteInspWageByNS" label="巡检绩效" min-width="120"></el-table-column>
          <el-table-column prop="handymanWageByNS" label="杂工工资" min-width="120"></el-table-column>
        </el-table-column>
        <el-table-column label="科技域MES">
          <el-table-column prop="operationPerformanceByKJ" label="工序绩效" min-width="120"></el-table-column>
          <el-table-column prop="productPerformanceByKJ" label="成品绩效" min-width="120"></el-table-column>
          <el-table-column prop="onsiteInspWageByKJ" label="巡检绩效" min-width="120"></el-table-column>
          <el-table-column prop="handymanWageByKJ" label="杂工工资" min-width="120"></el-table-column>
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>
<script>
import { getYearAndMonthAndDays, getYearAndMonthAndDaysZTZB } from "@/utils/date";
import {transformExcel} from '@/utils/file'
import { getYearAndMonthAndDays, getWorkMonth } from "@/utils/date";
import limsTable from "@/components/Table/lims-table.vue";
import { selectAuxiliaryAllByMonth } from "@/api/performance/manHour";
import { selectAuxiliaryAllByMonth,exportWorkHoursTotal } from "@/api/performance/manHour";
export default {
  components: {
    limsTable
@@ -58,7 +90,7 @@
  data() {
    return {
      queryParams: {
        month: getYearAndMonthAndDaysZTZB().slice(0, 7),
        month: getWorkMonth().format('YYYY-MM'),
        name: "",
        departLims: "",
      },
@@ -96,12 +128,25 @@
        size: 20,
        current: 0,
      },
      downloadLoading: false,
    };
  },
  mounted() {
    this.refreshTable();
  },
  methods: {
    exportWorkHoursTotal(){
      this.downloadLoading = true
      exportWorkHoursTotal(this.queryParams).then(res=>{
        transformExcel(res, "中天耐丝质量部工时汇总.xlsx")
        this.$message.success("导出成功")
        this.$nextTick(()=>{
          this.downloadLoading = false
        })
      }).catch((error)=>{
        console.error(error)
      })
    },
    refreshTable() {
      this.tableLoading = true;
      selectAuxiliaryAllByMonth(this.queryParams)
@@ -115,7 +160,7 @@
    },
    refresh() {
      this.queryParams = {
        month: getYearAndMonthAndDaysZTZB().slice(0, 7),
        month: getWorkMonth().format('YYYY-MM'),
        name: "",
        departLims: "",
      };