Browse Source

添加资源甘特图

qinhb 9 months ago
parent
commit
16e678f00f

+ 1 - 1
.env.development

@@ -11,6 +11,6 @@ VITE_APP_BASE_API = '/dev-api'
 VITE_APP_UPLOAD_URL = 'http://192.168.101.4:9000'
 # 开发接口地址
 #VITE_APP_API_URL = 'http://192.168.101.4:8078'
-VITE_APP_API_URL = 'http://10.88.11.200:8078'
+VITE_APP_API_URL = 'http://192.168.101.178:8078'
 # 是否启用 Mock 服务
 VITE_MOCK_DEV_SERVER = false

+ 138 - 0
src/views/plan/schedule/components/plan_device_task.vue

@@ -0,0 +1,138 @@
+<template>
+  <div class="mainContentBox">
+    <avue-crud
+        ref="crudRef"
+        v-model:search="search"
+        v-model="form"
+        :data="data"
+        :option="option"
+        v-model:page="page"
+        @row-save="createRow"
+        @row-update="updateRow"
+        @row-del="deleteRow"
+        @search-change="searchChange"
+        @search-reset="resetChange"
+        @size-change="dataList"
+        @current-change="dataList"
+        @selection-change="selectionChange1"
+    >
+
+    </avue-crud>
+  </div>
+</template>
+<script setup>
+import {defineProps, ref} from "vue";
+import { useCrud } from "@/hooks/userCrud";
+import { useCommonStoreHook } from "@/store";
+import dictDataUtil from "@/common/configs/dictDataUtil";
+import { html2CanvasPrint } from "@/utils/common";
+const { isShowTable, tableType } = toRefs(useCommonStoreHook());
+const toPrintRef = ref(null);
+const test = () => {
+  isShowTable.value = true;
+  tableType.value = tableType.value == 1 ? 2 : 1;
+};
+const info = ref({})
+const props = defineProps({
+  workOrderInfo: {
+    type: Object,
+    default: () => {
+      return null;
+    }
+  }
+})
+const clickObjs = ref([])
+const selectionChange1 =(row)=>{
+  toDeleteIds.value = [];
+  row?.forEach((element) => {
+    toDeleteIds.value.push(element.id);
+  });
+  clickObjs.value = row
+}
+const crudRef = ref(null); //crudRef.value 获取avue-crud对象
+
+watch(
+    () => props.workOrderInfo,
+    () => {
+      form.value.workOrderCode = props.workOrderInfo.workOrderCode
+      info.value = props.workOrderInfo
+      search.value.workOrderCode = props.workOrderInfo.workOrderCode
+      dataList();
+    }
+);
+// 传入一个url,后面不带/
+const { form, data, option, search, page, toDeleteIds, Methords, Utils } =
+  useCrud({
+    src: "/api/v1/deviceTask",
+  });
+const { dataList, createRow, updateRow, deleteRow, searchChange, resetChange } = Methords; //增删改查
+const { selectionChange, multipleDelete } = Methords; //选中和批量删除事件
+const { checkBtnPerm, downloadTemplate, exportData } = Utils; //按钮权限等工具
+
+const printCode = () =>{
+  dialog.visible = true
+}
+
+
+// 设置表格列或者其他自定义的option
+option.value = Object.assign(option.value, {
+  delBtn: false,
+  selection: false,
+  search: false,
+  filterBtn: false,
+  columnBtn: false,
+  editBtn: true,
+  addBtn: false,
+  viewBtn: false,
+  menu: true,
+  column: [
+    {
+      label: "流转卡号",
+      prop: "seqNo",
+      width: 120,
+      editDisabled: true,
+      overHidden: true,
+    },
+    {
+      label: "工序名称",
+      prop: "operationName",
+      editDisabled: true,
+    },
+    {
+      label: "分配设备",
+      prop: "allocationDeviceName",
+      width: 100,
+      overHidden: true,
+      editDisabled: true,
+      type: "select",
+      //dicData: userList,
+      props: { label: "userName", value: "userName" },
+    },
+    {
+      label: "开始时间",
+      prop: "planStartWhen",
+      type: "datetime",
+      valueFormat: "YYYY-MM-DD HH:mm:ss",
+      rules: [
+        {
+          required: true,
+          message: "开始时间不能大于结束时间",
+          trigger: "blur",
+        },
+      ],
+    },
+    {
+      label: "结束时间",
+      prop: "planStartEnd",
+      type: "datetime",
+      valueFormat: "YYYY-MM-DD HH:mm:ss",
+    },
+  ],
+});
+onMounted(() => {
+  info.value = props.workOrderInfo
+  form.value.workOrderCode = props.workOrderInfo.workOrderCode
+  search.value.workOrderCode = props.workOrderInfo.workOrderCode
+  dataList();
+});
+</script>

+ 42 - 8
src/views/plan/schedule/index.vue

@@ -24,17 +24,35 @@
           @click="handleEdit(row, index)"
           type="primary"
           :size="size"
-          >编辑</el-button
+          >派工调整</el-button
+        >
+        <el-button
+            icon="el-icon-edit"
+            text
+            v-if="row.productLineCode === '1'"
+            @click="handleDevice(row, index)"
+            type="primary"
+            :size="size"
+        >资源调整</el-button
         >
       </template>
       <template #menu-left>
         <div id="charts"></div>
       </template>
     </avue-crud>
+    <el-dialog
+        v-model="dialog4.visible"
+        :title="dialog4.title"
+        width="950px"
+        @close="dialog4.visible = false"
+    >
+      <plan_device_task :workOrderInfo="form" />
+    </el-dialog>
   </div>
 </template>
 <script setup>
 import { ref } from "vue";
+import { getUserList } from "@/api/system/user";
 import { useCrud } from "@/hooks/userCrud";
 import dictDataUtil from "@/common/configs/dictDataUtil";
 import { queryStationByLineId } from "@/api/station";
@@ -57,6 +75,7 @@ const { checkBtnPerm, downloadTemplate, exportData } = Utils; //按钮权限等
 
 const crudRef = ref(null); //crudRef.value 获取avue-crud对象
 const stationList = ref([]);
+const userList = ref([])
 const charts = shallowRef(null);
 const handleEdit = (row, index) => {
   queryStationByLineId(row.productLineId).then((data) => {
@@ -64,9 +83,22 @@ const handleEdit = (row, index) => {
   });
   crudRef.value && crudRef.value.rowEdit(row, index);
 };
+const handleDevice =(row,index) =>{
+  form.value = row
+  dialog4.visible = true
+}
+const dialog4 = reactive({
+  title: "资源调整",
+  visible: false,
+});
+const queryUserList = () => {
+  getUserList({}).then((data) => {
+    userList.value = data.data;
+  });
+};
 onMounted(() => {
   charts.value = echarts.init(document.getElementById("charts"));
-
+  queryUserList();
   dataList();
 
   getStatistics(search.value).then((res) => {
@@ -161,11 +193,13 @@ option.value = Object.assign(option.value, {
       ],
     },
     {
-      label: "工单编码",
-      prop: "workOrderCode",
-      search: true,
-      width: 125,
-      editDisabled: true,
+      label: "分配人员",
+      prop: "allocationUser",
+      width: 100,
+      overHidden: true,
+      type: "select",
+      dicData: userList,
+      props: { label: "userName", value: "userName" },
     },
     {
       label: "产线名称",
@@ -202,7 +236,7 @@ option.value = Object.assign(option.value, {
         return val.operationSort + 1;
       },
       editDisabled: true,
-      width: 60,
+      width:55,
     },
     {
       label: "状态",

+ 475 - 0
src/views/report/productionScheduling/device/index.vue

@@ -0,0 +1,475 @@
+<template>
+  <div>
+    <avue-crud
+      v-model:search="search"
+      :option="option"
+      @search-change="searchChange"
+      @search-reset="resetChange"
+    />
+    <div class="btns">
+      <div>
+        <el-button type="primary" @click="exportToPNG">导出PNG图片</el-button>
+        <el-button type="primary" @click="exportToPDF">导出PDF文件</el-button>
+      </div>
+      <!-- <el-button type="params" v-print="printObj" @click="toPrint"
+        >打印</el-button
+      > -->
+      <div class="illustration">
+        <div style="display: flex; align-items: center; margin-right: 2px">
+          <div class="round1"></div>
+          :完成
+        </div>
+        <div style="display: flex; align-items: center">
+          <div class="round2"></div>
+          :未完成
+        </div>
+      </div>
+    </div>
+    <div class="main-content">
+      <div ref="ganttRef" id="gantt_here" class="gantt-container"></div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { gantt } from "dhtmlx-gantt";
+import "dhtmlx-gantt/codebase/dhtmlxgantt.css";
+import html2canvas from "html2canvas";
+import { htmlPdf } from "@/utils/htmlPDF.js";
+import { useCrud } from "@/hooks/userCrud";
+import { v4 as uuidv4 } from "uuid";
+const ganttRef = ref(null);
+const {
+  form,
+  data,
+  option,
+  search,
+  page,
+  toDeleteIds,
+  Methords,
+  Utils,
+  lines,
+} = useCrud({
+  src: "/api/v1/process/census/deviceApsResultGant",
+  pageStr: "no",
+});
+const exportToPDF = () => {
+  var fileName = "甘特图pdf";
+  const fileList = document.getElementsByClassName("gantt_here");
+  htmlPdf(fileName, document.querySelector("#gantt_here"), fileList, 1111);
+};
+const exportToPNG = async () => {
+  const chartContainer = ganttRef.value;
+  const canvas = await html2canvas(chartContainer);
+  const img = canvas.toDataURL("image/png");
+  const a = document.createElement("a");
+  a.href = img;
+  a.download = "甘特图.png";
+  a.click();
+};
+// const printUrl = ref("");
+// const toPrint = async () => {
+//   const chartContainer = ganttRef.value;
+//   const canvas = await html2canvas(chartContainer);
+//   const img = canvas.toDataURL("image/png");
+//   printUrl.value = img;
+// };
+// const printObj = ref({
+//   id: "gantt_here",
+//   preview: true,
+//   popTitle: "甘特图",
+//   url: printUrl.value,
+// });
+const { dataList, createRow, updateRow, deleteRow, searchChange, resetChange } =
+  Methords;
+option.value = Object.assign(option.value, {
+  selection: false,
+  menu: true,
+  menuWidth: 100,
+  addBtn: false,
+  filterBtn: false,
+  searchShowBtn: true,
+  columnBtn: false,
+  gridBtn: false,
+  editBtn: false,
+  viewBtn: false,
+  delBtn: false,
+  column: [
+    {
+      label: "日期范围",
+      prop: "searchTime",
+      search: true,
+      hide: true,
+      type: "date",
+      format: "YYYY-MM-DD",
+      valueFormat: "YYYY-MM-DD",
+      searchRange: true,
+      startPlaceholder: "开始日期",
+      endPlaceholder: "结束日期",
+    },
+    {
+      label: "设备编码",
+      prop: "name",
+      search: true,
+    },
+    {
+      label: "时间维度",
+      prop: "timeType",
+      search: true,
+      type: "select",
+      searchValue: "1",
+      dicData: [
+        {
+          dictLabel: "小时",
+          dictValue: "0",
+        },
+        {
+          dictLabel: "日",
+          dictValue: "1",
+        },
+        {
+          dictLabel: "月",
+          dictValue: "2",
+        },
+      ],
+      props: {
+        label: "dictLabel",
+        value: "dictValue",
+      },
+    },
+  ],
+});
+const getCurrentMonthStartAndEndDates = () => {
+  // 获取当前日期
+  let now = new Date();
+
+  // 获取当前月份的第一天
+  let startDate = new Date(now.getFullYear(), now.getMonth(), 1);
+
+  // 获取当前月份的最后一天
+  let endDate = new Date(now.getFullYear(), now.getMonth() + 1, 0);
+
+  // 格式化日期为'YYYY-MM-DD'格式
+  function formatDate(date) {
+    let year = date.getFullYear();
+    let month = String(date.getMonth() + 1).padStart(2, "0");
+    let day = String(date.getDate()).padStart(2, "0");
+    return `${year}-${month}-${day}`;
+  }
+
+  // 返回包含开始和结束日期的数组
+  return [formatDate(startDate), formatDate(endDate)];
+};
+const demoData = {
+  data: [
+    {
+      id: 11,
+      text: "Project #1",
+      start_date: "2024-07-01 08:00:00",
+      endTime: "2024-09-30 09:00:00",
+      startTime: "2024-07-30 08:00:00",
+      duration: "11",
+      progress: 0.8,
+      open: true,
+    },
+  ],
+};
+//初始化甘特图
+const initGantt = (type) => {
+  gantt.plugins({
+    tooltip: true,
+    marker: true,
+    critical_path: true,
+    export_api: true,
+  });
+  gantt.templates.task_text = function (start, end, task) {
+    return (
+      "<span style='padding:0 5px;font-size:15px;color: #000; display: inline-block;width: 100%; white-space: nowrap;overflow: hidden; text-overflow: ellipsis; '>" +
+      task.text +
+      "</span>"
+    );
+  };
+  gantt.templates.progress_text = function (start, end, task) {
+    return (
+      "<span style='text-align:left;'>" +
+      Math.round(task.progress * 100) +
+      "% </span>"
+    );
+  };
+  gantt.templates.task_class = function (start, end, task) {
+    return "taskClass";
+  };
+  if (type) {
+    gantt.config.duration_unit = type;
+  }
+  gantt.config.show_today = true;
+  gantt.config.grid_width = 350;
+  gantt.config.add_column = false;
+  gantt.config.duration_step = 1;
+  gantt.config.drag_resize = true;
+  gantt.config.columns = [
+    {
+      tree: true,
+      name: "text",
+      label: "设备名称",
+      width: "240",
+    },
+  ];
+  gantt.config.autofit = true;
+  gantt.config.resize_rows = true;
+  gantt.config.row_height = 60;
+  gantt.config.bar_height = 40;
+  gantt.config.min_column_width = 60;
+  gantt.config.xml_date = "%Y-%m-%d %H:%i:%s"; //甘特图时间格式
+  gantt.config.readonly = true; //是否只读
+  gantt.i18n.setLocale("cn"); //设置语言
+  gantt.templates.tooltip_text = function (start, end, task) {
+    const text = `设备名称: ${task.name}`;
+    return task.parent
+      ? `名称: ${task.text}<br/>计划开始时间: ${task.startTime}<br/>计划结束时间: ${task.endTime}`
+      : `${text}<br/>`;
+  };
+  const dateToStr = gantt.date.date_to_str(gantt.config.task_date);
+  const today = new Date(new Date().setHours(0, 0, 0, 0));
+  gantt.addMarker({
+    start_date: today,
+    css: "today",
+    text: "今日",
+    title: `Today: ${dateToStr(today)}`,
+  });
+  gantt.init("gantt_here"); //初始化
+};
+const setTime = () => {
+  search.value = {
+    searchTime: getCurrentMonthStartAndEndDates(),
+    timeType: "1",
+  };
+};
+
+function convertDateString1(str) {
+  // 将输入的日期字符串转换为 Date 对象
+  let date = new Date(str);
+  // 获取年、月、日
+  let year = date.getFullYear();
+  let month = date.toLocaleString("en-us", { month: "short" });
+  let day = date.getDate();
+  // 构造新的日期字符串格式
+  let formattedDate = `${date.toDateString().slice(0, 3)} ${month} ${day} ${year} 00:00:00 GMT+0800 (GMT+08:00)`;
+  return formattedDate;
+}
+function convertDateString2(str) {
+  // 将输入的日期字符串转换为 Date 对象
+  let date = new Date(str);
+
+  // 获取年、月、日
+  let year = date.getFullYear();
+  let month = date.toLocaleString("en-us", { month: "short" });
+  let day = date.getDate();
+
+  // 构造新的日期字符串格式
+  let formattedDate = `${date.toDateString().slice(0, 3)} ${month} ${day} ${year} 24:00:00 GMT+0800 (GMT+08:00)`;
+
+  return formattedDate;
+}
+// watchEffect(() => {
+//   //此api后续弃用
+//   // gantt.parse({ data: data.value });
+//   gantt.parse(demoData);
+// });
+const setGantt = () => {
+  gantt.clearAll();
+  if (search?.value.searchTime) {
+    gantt.config.start_date = convertDateString1(search?.value.searchTime[0]);
+    gantt.config.end_date = convertDateString2(search?.value.searchTime[1]);
+    gantt.config.show_tasks_outside_timescale = true;
+  }
+  switch (search.value.timeType) {
+    case "0":
+      gantt.config.duration_unit = "hour";
+      gantt.config.scale_height = 28 * 4;
+      gantt.config.scales = [
+        {
+          unit: "year",
+          step: 1,
+          format: "%Y年",
+          css: function (date) {
+            return "year";
+          },
+        },
+        {
+          unit: "month",
+          step: 1,
+          format: "%m月",
+          css: function (date) {
+            return "month";
+          },
+        },
+        {
+          unit: "day",
+          step: 1,
+          format: "%d日",
+          css: function (date) {
+            return "day";
+          },
+        },
+        {
+          unit: "hour",
+          step: 1,
+          format: "%H时",
+          css: function (date) {
+            return "hour";
+          },
+        },
+      ];
+      gantt.init("gantt_here");
+      gantt.render();
+      gantt.parse({ data: useData.value });
+      break;
+    case "1":
+      gantt.config.scale_height = 28 * 4;
+      gantt.config.scales = [
+        {
+          unit: "year",
+          step: 1,
+          format: "%Y年",
+          css: function (date) {
+            return "year";
+          },
+        },
+        {
+          unit: "month",
+          step: 1,
+          format: "%m月",
+          css: function (date) {
+            return "month";
+          },
+        },
+        {
+          unit: "day",
+          step: 1,
+          format: "%d日",
+          css: function (date) {
+            return "day";
+          },
+        },
+      ];
+      gantt.config.duration_unit = "day";
+
+      gantt.init("gantt_here");
+      gantt.render();
+      gantt.parse({ data: useData.value });
+      break;
+    case "2":
+      // initGantt("month");
+      gantt.config.scale_height = 28 * 4;
+      gantt.config.scales = [
+        {
+          unit: "year",
+          step: 1,
+          format: "%Y年",
+          css: function (date) {
+            return "year";
+          },
+        },
+        {
+          unit: "month",
+          step: 1,
+          format: "%m月",
+          css: function (date) {
+            return "month";
+          },
+        },
+      ];
+      gantt.config.duration_unit = "month";
+      gantt.init("gantt_here");
+      gantt.parse({ data: useData.value });
+      gantt.render();
+      break;
+  }
+};
+const useData = ref([]);
+const setUseData = (data, lines) => {
+  useData.value = [];
+  const reslinedata = ref([]);
+  const data1 = JSON.parse(JSON.stringify(data));
+  const line1 = JSON.parse(JSON.stringify(lines));
+  data1.forEach((element) => {
+    element.start_date = element.startTime;
+    if (element.progress != 1) {
+      element.color = "orange";
+    } else {
+      element.color = "green";
+    }
+    element.id = uuidv4();
+    element.parent = element.productLineName;
+  });
+  const data2 = JSON.parse(JSON.stringify(data1));
+  line1.forEach((element) => {
+    let obj = {};
+    obj.id = element.productLineName;
+    obj.name = element.productLineName;
+    obj.text = element.productLineName;
+    obj.render = "split";
+    reslinedata.value.push(obj);
+  });
+  const data3 = JSON.parse(JSON.stringify(reslinedata.value));
+  const data4 = [...data3, ...data2];
+  useData.value = data4;
+};
+onMounted(() => {
+  setTime();
+  dataList();
+  initGantt();
+});
+watch(
+  () => data.value,
+  () => {
+    if (data.value) {
+      setUseData(data.value, lines.value);
+      setGantt();
+    }
+  },
+  { immediate: true, deep: true }
+);
+</script>
+<style scoped lang="scss">
+.btns {
+  margin: 7px 0;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  .illustration {
+    display: flex;
+    padding-right: 5%;
+    align-items: center;
+    width: 240px;
+    justify-content: space-between;
+    .round2 {
+      width: 20px;
+      height: 20px;
+      background-color: orange;
+      border-radius: 10px;
+    }
+    .round1 {
+      width: 20px;
+      height: 20px;
+      background-color: green;
+      border-radius: 10px;
+    }
+  }
+}
+:deep(.avue-crud__body) {
+  display: none;
+}
+.main-content {
+  width: 100%;
+  height: 600px;
+  .gantt-container {
+    width: 100%;
+    height: 100%;
+  }
+}
+.ganttStyle {
+  border-radius: 50%;
+}
+</style>

+ 1 - 1
src/views/report/productionScheduling/line/index.vue

@@ -15,7 +15,7 @@
         >打印</el-button
       > -->
       <div class="illustration">
-        <div style="display: flex; align-items: center; margin-right: 5px">
+        <div style="display: flex; align-items: center; margin-right: 2px">
           <div class="round1"></div>
           :完成
         </div>

+ 1 - 1
src/views/report/productionScheduling/order/index.vue

@@ -15,7 +15,7 @@
         >打印</el-button
       > -->
       <div class="illustration">
-        <div style="display: flex; align-items: center; margin-right: 5px">
+        <div style="display: flex; align-items: center; margin-right: 2px">
           <div class="round1"></div>
           :完成
         </div>

+ 1 - 1
src/views/storage/stock/index.vue

@@ -31,7 +31,7 @@
             type="primary"
             :size="size"
             @click="syncPosition"
-        >wcs数据同步
+        >立体仓同步
           <template #icon>
             <svg-icon icon-class="paicheng" />
           </template>