Pārlūkot izejas kodu

Merge branch 'master' of http://192.168.101.4:3000/jiaxiaoqiang/JG-CLIENT-TEMP

dy 1 gadu atpakaļ
vecāks
revīzija
249c0cc4fc

+ 3 - 2
.env.production

@@ -1,6 +1,7 @@
 ## 生产环境
 NODE_ENV='production'
 
-# 代理前缀
-VITE_APP_BASE_API = '/prod-api'
+# 代理前缀 三江环境
+VITE_APP_BASE_API = '/client-server'
+
 

+ 6 - 0
.env.test

@@ -0,0 +1,6 @@
+## 生产环境
+NODE_ENV='test'
+
+# 代理前缀 测试环境
+VITE_APP_BASE_API = '/prod-api'
+

+ 39 - 0
docker/Dockerfile

@@ -0,0 +1,39 @@
+FROM nginx
+MAINTAINER jgiot@163.com
+RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
+VOLUME /tmp
+ENV LANG en_US.UTF-8
+RUN echo "server {  \
+                      listen       80; \
+                      client_max_body_size 100m;\
+                      location   /prod-api/ { \
+                      proxy_pass              http://192.168.101.4:8079/; \
+                      proxy_redirect          off; \
+                      proxy_set_header        Host mes-server; \
+                      proxy_set_header        X-Real-IP \$remote_addr; \
+                      proxy_set_header        X-Forwarded-For \$proxy_add_x_forwarded_for; \
+                  } \
+                  location /jgfile/ { \
+                              proxy_pass          http://192.168.101.4:9000/jgfile/; \
+                              proxy_redirect      off; \
+                              proxy_set_header    Host jgfile; \
+                              proxy_set_header    X-Real-IP \$remote_addr; \
+                              proxy_set_header    X-Forwarded-For \$proxy_add_x_forwarded_for; \
+                      } \
+                  #解决Router(mode: 'history')模式下,刷新路由地址不能找到页面的问题 \
+                  location / { \
+                     root   /var/www/html/; \
+                      index  index.html index.htm; \
+                      if (!-e \$request_filename) { \
+                          rewrite ^(.*)\$ /index.html?s=\$1 last; \
+                          break; \
+                      } \
+                  } \
+                  access_log  /var/log/nginx/access.log ; \
+              } " > /etc/nginx/conf.d/default.conf \
+    &&  mkdir  -p  /var/www \
+    &&  mkdir -p /var/www/html
+
+ADD dist/ /var/www/html/
+EXPOSE 80
+EXPOSE 443

+ 3 - 1
package.json

@@ -6,7 +6,8 @@
   "scripts": {
     "preinstall": "npx only-allow pnpm",
     "dev": "vite serve --mode development",
-    "build:prod": "vite build --mode production && vue-tsc --noEmit",
+    "build:test": "vite build --mode test && vue-tsc --noEmit",
+    "build": "vite build --mode production && vue-tsc --noEmit",
     "lint:eslint": "eslint  --fix --ext .ts,.js,.vue ./src ",
     "lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"",
     "lint:stylelint": "stylelint  \"**/*.{css,scss,vue}\" --fix",
@@ -53,6 +54,7 @@
     "echarts": "^5.5.0",
     "element-plus": "^2.6.0",
     "lodash-es": "^4.17.21",
+    "mitt": "^3.0.1",
     "net": "^1.0.2",
     "nprogress": "^0.2.0",
     "path-browserify": "^1.0.1",

+ 40 - 0
src/api/process/appointOut.ts

@@ -0,0 +1,40 @@
+import request from "@/utils/request";
+
+//首页涉及相关api(获取未完成任务、扫码开工)
+/**
+ * 获取未完成任务
+ *
+ * @param data
+ */
+export function addAppointOut(data: any) {
+  return request({
+    url: "/api/v1/process/outsource/add",
+    method: "post",
+    data: data,
+  });
+}
+
+export function appointRecord(data: any) {
+  return request({
+    url: "/api/v1/process/outsource/page",
+    method: "post",
+    data: data,
+  });
+}
+
+export function reveiveRecord(data: any) {
+  return request({
+    url: "/api/v1/process/outsource/receive",
+    method: "post",
+    data: data,
+  });
+}
+
+// 	主键id
+export function deleteRecordById(id: string) {
+  return request({
+    url: "/api/v1/process/outsource/del",
+    method: "post",
+    data: { id: id },
+  });
+}

+ 9 - 0
src/api/process/reportBreak.ts

@@ -8,6 +8,15 @@ export function breakReportInfoById(processId: string) {
   });
 }
 
+// 根据缺陷分类,获取原因列表(缺陷大类获取二类)
+export function basebugsByType(type: string) {
+  return request({
+    url: `/api/v1/op/baseBug/list`,
+    method: "post",
+    data: { bugType: type },
+  });
+}
+
 // 不合格品分布情况及工序(通过序列号获取关联工序)
 export function operationListByIds(ids: string[]) {
   return request({

+ 45 - 1
src/components/PDFView/index.vue

@@ -1,11 +1,22 @@
 <template>
+  <el-button
+    v-if="contentType === 'button'"
+    :link="isLink"
+    :type="btnType"
+    @click="showPdf"
+  >
+    {{ btnText }}
+  </el-button>
   <VuePdfEmbed
+    v-else
+    :page="pageNumber"
     :source="pdfSource"
     annotation-layer
     text-layer
     @click="showPdf"
   />
   <el-drawer
+    v-if="needToShowPdf"
     v-model="visible"
     :footer="false"
     :header="false"
@@ -14,7 +25,12 @@
     direction="rtl"
     size="972px"
   >
-    <VuePdfEmbed :source="pdfSource" annotation-layer text-layer />
+    <VuePdfEmbed
+      :page="showPdfNumber"
+      :source="pdfSource"
+      annotation-layer
+      text-layer
+    />
   </el-drawer>
 </template>
 
@@ -33,6 +49,34 @@ const props = defineProps({
     type: String,
     required: true,
   },
+  pageNumber: {
+    type: Number,
+    default: 0,
+  },
+  needToShowPdf: {
+    type: Boolean,
+    default: false,
+  },
+  contentType: {
+    type: String as PropType<"button" | "pdf">,
+    default: "pdf",
+  },
+  btnText: {
+    type: String,
+    default: "预览",
+  },
+  btnType: {
+    type: String,
+    default: "primary",
+  },
+  isLink: {
+    type: Boolean,
+    default: false,
+  },
+  showPdfNumber: {
+    type: Number,
+    default: 0,
+  },
 });
 
 const visible = ref(false);

+ 67 - 79
src/components/Upload/MultiUpload.vue

@@ -1,36 +1,35 @@
-<!--
-  多图上传组件
-  @author: youlaitech
-  @date 2022/11/20
--->
-
 <template>
-  <el-upload
-    v-model:file-list="fileList"
-    list-type="picture-card"
-    :before-upload="handleBeforeUpload"
-    :http-request="handleUpload"
-    :on-remove="handleRemove"
-    :on-preview="previewImg"
-    :limit="props.limit"
-  >
-    <i-ep-plus />
-  </el-upload>
+  <div class="upload-container">
+    <el-upload
+      v-model:file-list="fileList"
+      :before-upload="handleBeforeUpload"
+      :http-request="handleUpload"
+      :limit="limit"
+      :show-file-list="false"
+      class="img-box"
+      list-type="picture-card"
+    >
+      <i-ep-plus />
+    </el-upload>
+    <div v-for="(item, index) in fileList" :key="index" class="img-container">
+      <img :src="item.url" class="img-box" object-fit="cover" />
+      <Delete class="delete-icon" @click="deleteImg(index)" />
+    </div>
+  </div>
 
   <el-dialog v-model="dialogVisible">
-    <img w-full :src="previewImgUrl" alt="Preview Image" />
+    <img :src="previewImgUrl" alt="Preview Image" w-full />
   </el-dialog>
 </template>
 
-<script setup lang="ts">
+<script lang="ts" setup>
 import {
   UploadRawFile,
   UploadRequestOptions,
   UploadUserFile,
-  UploadFile,
-  UploadProps,
 } from "element-plus";
-import { uploadFileApi, deleteFileApi } from "@/api/file";
+import { Delete } from "@element-plus/icons-vue";
+import { uploadFileApi } from "@/api/file";
 
 const emit = defineEmits(["update:modelValue"]);
 
@@ -55,26 +54,7 @@ const previewImgUrl = ref("");
 const dialogVisible = ref(false);
 
 const fileList = ref([] as UploadUserFile[]);
-watch(
-  () => props.modelValue,
-  (newVal: string[]) => {
-    const filePaths = fileList.value.map((file) => file.url);
-    // 监听modelValue文件集合值未变化时,跳过赋值
-    if (
-      filePaths.length > 0 &&
-      filePaths.length === newVal.length &&
-      filePaths.every((x) => newVal.some((y) => y === x)) &&
-      newVal.every((y) => filePaths.some((x) => x === y))
-    ) {
-      return;
-    }
-
-    fileList.value = newVal.map((filePath) => {
-      return { url: filePath } as UploadUserFile;
-    });
-  },
-  { immediate: true }
-);
+const urlList = ref([] as string[]); // 上传成功后的图片路径集合,要传给父组件
 
 /**
  * 自定义图片上传
@@ -83,39 +63,9 @@ watch(
  */
 async function handleUpload(options: UploadRequestOptions): Promise<any> {
   // 上传API调用
-  const { data: fileInfo } = await uploadFileApi(options.file);
-
-  // 上传成功需手动替换文件路径为远程URL,否则图片地址为预览地址 blob:http://
-  const fileIndex = fileList.value.findIndex(
-    (file) => file.uid == (options.file as any).uid
-  );
-
-  fileList.value.splice(fileIndex, 1, {
-    name: fileInfo.name,
-    url: fileInfo.url,
-  } as UploadUserFile);
-
-  emit(
-    "update:modelValue",
-    fileList.value.map((file) => file.url)
-  );
-}
-
-/**
- * 删除图片
- */
-function handleRemove(removeFile: UploadFile) {
-  const filePath = removeFile.url;
-
-  if (filePath) {
-    deleteFileApi(filePath).then(() => {
-      // 删除成功回调
-      emit(
-        "update:modelValue",
-        fileList.value.map((file) => file.url)
-      );
-    });
-  }
+  const res: any = await uploadFileApi(options.file);
+  urlList.value.push(res.data.fileUrl);
+  emit("update:modelValue", urlList.value);
 }
 
 /**
@@ -129,11 +79,49 @@ function handleBeforeUpload(file: UploadRawFile) {
   return true;
 }
 
+const deleteImg = (index: number) => {
+  fileList.value.splice(index, 1);
+  urlList.value.splice(index, 1);
+  emit("update:modelValue", urlList.value);
+};
+
 /**
  * 预览图片
  */
-const previewImg: UploadProps["onPreview"] = (uploadFile) => {
-  previewImgUrl.value = uploadFile.url!;
-  dialogVisible.value = true;
-};
+// const previewImg: UploadProps["onPreview"] = (uploadFile) => {
+//   previewImgUrl.value = uploadFile.url!;
+//   dialogVisible.value = true;
+// };
 </script>
+
+<style lang="scss" scoped>
+.upload-container {
+  display: flex;
+  justify-content: start;
+  flex-wrap: wrap;
+}
+
+.img-container {
+  position: relative;
+}
+
+.img-box {
+  width: 148px;
+  height: 148px;
+  display: block;
+  margin-right: 10px;
+  margin-top: 10px;
+  border-radius: 4px;
+}
+
+.delete-icon {
+  position: absolute;
+  width: 20px;
+  height: 20px;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+  color: red;
+  cursor: pointer;
+}
+</style>

+ 10 - 0
src/router/modules/process.ts

@@ -127,5 +127,15 @@ export default {
         back: true,
       },
     },
+    {
+      path: "appoint",
+      component: () => import("@/views/pro-operation/appoint-out/index.vue"),
+      name: "appoint-out",
+      meta: {
+        title: "委外",
+        icon: "homepage",
+        back: true,
+      },
+    },
   ],
 };

+ 1 - 1
src/store/modules/dictionary.ts

@@ -3,7 +3,7 @@ import { defineStore } from "pinia";
 import { getUserDicts, getUserList } from "@/api/auth";
 
 export const useDictionaryStore = defineStore("dictionaryStore", () => {
-  const types = ["unqualified_type", "stage", "process_state"];
+  const types = ["defect_mana", "stage", "process_state", "outsource_state"];
   const dicts = ref<{ [key: string]: any[] }>({});
 
   // 所有的用户列表

+ 9 - 0
src/utils/common.ts

@@ -0,0 +1,9 @@
+import mitt from "mitt";
+
+const emitter = mitt();
+
+enum EventsNames {
+  APPOINT_OUT = "APPOINT_OUT",
+}
+
+export { emitter, EventsNames };

+ 221 - 0
src/views/pro-operation/appoint-out/applyFor.vue

@@ -0,0 +1,221 @@
+<template>
+  <div class="commonTitle">申请</div>
+  <el-scrollbar class="barHeight">
+    <div id="drawContent">
+      <el-form
+        ref="formRef"
+        :model="formLabelAlign"
+        :rules="rules"
+        label-position="top"
+        label-width="auto"
+        size="large"
+      >
+        <el-form-item label="基本信息">
+          <div class="base-info">
+            <div class="info-item">
+              <div class="item-label">产品名称</div>
+              <div class="item-value">
+                {{ processStore.scanInfo?.materialName }}
+              </div>
+            </div>
+            <div class="info-item">
+              <div class="item-label">产品型号</div>
+              <div class="item-value">
+                {{ processStore.scanInfo?.materialModel }}
+              </div>
+            </div>
+            <div class="info-item">
+              <div class="item-label">当前工序</div>
+              <div class="item-value">
+                {{ processStore.scanInfo?.operationName }}
+              </div>
+            </div>
+            <div class="info-item">
+              <div class="item-label">开工时间</div>
+              <div class="item-value">
+                {{ processStore.scanInfo?.realStartWhen }}
+              </div>
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item
+          :label="`委外产品[${formLabelAlign.details.length}]`"
+          prop="details"
+        >
+          <el-select
+            v-model="formLabelAlign.details"
+            multiple
+            placeholder="请选择"
+            value-key="value"
+          >
+            <el-option
+              v-for="item in processWorkSeq"
+              :key="item"
+              :label="item"
+              :value="item"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="供应商" prop="manufacturers">
+          <el-input v-model="formLabelAlign.manufacturers" />
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="formLabelAlign.remark" :rows="3" type="textarea" />
+        </el-form-item>
+      </el-form>
+      <div class="bottom-btns">
+        <el-button class="cancelBtn" @click="cancelClick">重置</el-button>
+        <el-button class="sureBtn" type="primary" @click="confirmClick"
+          >发起申请
+        </el-button>
+      </div>
+    </div>
+  </el-scrollbar>
+</template>
+
+<script lang="ts" setup>
+import { breakReportInfoById } from "@/api/process/reportBreak";
+import { useProcessStore } from "@/store/modules/processView";
+import { addAppointOut } from "@/api/process/appointOut";
+import { emitter, EventsNames } from "@/utils/common";
+
+const processStore = useProcessStore();
+
+const formRef = ref<InstanceType<typeof ElForm>>();
+
+const formLabelAlign = reactive({
+  details: [],
+  manufacturers: "",
+  remark: "",
+});
+const rules = reactive({
+  manufacturers: [
+    { required: true, message: "请输入 委外厂商", trigger: "blur" },
+  ],
+  details: [{ required: true, message: "请选择 ", trigger: "blur" }],
+});
+
+const processWorkSeq = ref<any>([]);
+
+const cancelClick = () => {
+  formRef.value && formRef.value.resetFields();
+};
+
+onMounted(() => {
+  breakReportInfoById(processStore.scanInfo.id).then((res) => {
+    processWorkSeq.value = res.data.processWorkSeq || [];
+  });
+});
+
+const confirmClick = () => {
+  // drawerVisible.value = false;
+  formRef.value &&
+    formRef.value.validate((valid: boolean) => {
+      if (valid) {
+        const seqs: any[] = [];
+        formLabelAlign.details.forEach((item) => {
+          seqs.push({ seqNo: item });
+        });
+        let params = {
+          processId: processStore.scanInfo.id,
+          outNum: formLabelAlign.details.length,
+          details: seqs,
+          manufacturers: formLabelAlign.manufacturers,
+          remark: formLabelAlign.remark,
+        };
+
+        addAppointOut(params).then(() => {
+          ElMessage.success("申请成功");
+          emitter.emit(EventsNames.APPOINT_OUT);
+        });
+      } else {
+        return false;
+      }
+    });
+};
+</script>
+
+<style lang="scss" scoped>
+#drawContent {
+  width: 100%;
+  //:deep(.el-form--large.el-form--label-top .el-form-item .el-form-item__label) {
+  //  font-weight: 500;
+  //  font-size: 22px;
+  //  color: rgba(0, 0, 0, 0.9);
+  //  text-align: left;
+  //}
+}
+
+.base-info {
+  width: 100%;
+  background: #e3e5e7;
+  border-radius: 16px 16px 16px 16px;
+  padding: 0 30px;
+
+  .info-item {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 64px;
+
+    .item-label {
+      font-size: 20px;
+      color: rgba(0, 0, 0, 0.6);
+    }
+
+    .item-value {
+      font-weight: 500;
+      font-size: 24px;
+      color: rgba(0, 0, 0, 0.9);
+    }
+  }
+
+  .info-item:not(:last-child) {
+    border-bottom: 1px solid rgba(0, 0, 0, 0.2);
+  }
+}
+
+.bottom-btns {
+  display: flex;
+  justify-content: space-between;
+  margin-top: 20px;
+  //margin-bottom: 20px;
+
+  .button {
+    margin-right: 20px;
+  }
+
+  .cancelBtn {
+    width: 292px;
+    height: 80px;
+    background: rgba(0, 0, 0, 0.06);
+    border-radius: 76px 76px 76px 76px;
+  }
+
+  .sureBtn {
+    width: 292px;
+    height: 80px;
+    background: #0a59f7;
+    border-radius: 76px 76px 76px 76px;
+  }
+}
+
+.barHeight {
+  height: calc(100vh - 170px);
+}
+
+.scrollbar-demo-item {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 50px;
+  margin: 10px;
+  text-align: center;
+  border-radius: 4px;
+  background: var(--el-color-primary-light-9);
+  color: var(--el-color-primary);
+}
+</style>
+
+<style lang="scss" scoped></style>

+ 19 - 0
src/views/pro-operation/appoint-out/index.vue

@@ -0,0 +1,19 @@
+<template>
+  <div class="mainContentBox">
+    <el-row :gutter="20">
+      <el-col :span="10" class="elColClasss">
+        <ApplyFor />
+      </el-col>
+      <el-col :span="14" class="elColClasss">
+        <Record />
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import ApplyFor from "@/views/pro-operation/appoint-out/applyFor.vue";
+import Record from "@/views/pro-operation/appoint-out/record.vue";
+</script>
+
+<style lang="scss" scoped></style>

+ 119 - 0
src/views/pro-operation/appoint-out/record.vue

@@ -0,0 +1,119 @@
+<template>
+  <div class="commonTitle">记录</div>
+  <el-scrollbar class="barHeight">
+    <el-table :data="tableData" style="width: 100%">
+      <el-table-column label="委外厂商" prop="manufacturers" />
+      <el-table-column label="物料名称" prop="materialName" />
+      <el-table-column label="委外工序" prop="operationName" />
+      <el-table-column label="订单名称" prop="orderName" />
+      <el-table-column label="状态" prop="state">
+        <template #default="{ row }">
+          {{ dictS.getLableByValue("outsource_state", row.state) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" prop="operation">
+        <template #default="{ row }">
+          <el-button
+            v-if="row.state === '1'"
+            round
+            type="primary"
+            @click="handleClick(row)"
+            >接收
+          </el-button>
+          <el-button
+            v-if="row.state === '0'"
+            round
+            type="danger"
+            @click="deleteRecord(row)"
+            >删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-scrollbar>
+  <Pagination
+    v-model:limit="page.pageSize"
+    v-model:page="page.pageNo"
+    :total="page.total"
+    @pagination="paginationChange"
+  />
+</template>
+
+<script lang="ts" setup>
+import { emitter, EventsNames } from "@/utils/common";
+import {
+  appointRecord,
+  deleteRecordById,
+  reveiveRecord,
+} from "@/api/process/appointOut";
+import { useDictionaryStore } from "@/store";
+
+const dictS = useDictionaryStore();
+
+const page = reactive({
+  pageSize: 1,
+  pageNo: 1,
+  total: 0,
+});
+
+const tableData = ref<any>([]);
+
+const handleClick = (row: any) => {
+  reveiveRecord([{ seqNo: row.details.seqNo, outsourceId: row.id }]).then(
+    () => {
+      ElMessage.success("接收成功");
+      getData();
+    }
+  );
+};
+
+const deleteRecord = (row: any) => {
+  deleteRecordById(row.id).then(() => {
+    ElMessage.success("删除成功");
+    getData();
+  });
+};
+
+const paginationChange = () => {
+  getData();
+};
+
+const getData = () => {
+  appointRecord({
+    pageNo: page.pageNo,
+    pageSize: page.pageSize,
+  }).then((res) => {
+    console.log(res);
+    tableData.value = res.data.records;
+  });
+};
+
+onMounted(() => {
+  getData();
+  emitter.on(EventsNames.APPOINT_OUT, () => {
+    getData();
+  });
+});
+
+onBeforeUnmount(() => {
+  emitter.off(EventsNames.APPOINT_OUT);
+});
+</script>
+
+<style lang="scss" scoped>
+.barHeight {
+  height: calc(100vh - 220px);
+}
+
+.scrollbar-demo-item {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 50px;
+  margin: 10px;
+  text-align: center;
+  border-radius: 4px;
+  background: var(--el-color-primary-light-9);
+  color: var(--el-color-primary);
+}
+</style>

+ 69 - 64
src/views/pro-operation/report-break/index.vue

@@ -5,7 +5,7 @@
     :show-close="false"
     destroy-on-close
     direction="rtl"
-    size="972px"
+    size="990px"
   >
     <template #header>
       <div class="drawerTitle">报故</div>
@@ -67,90 +67,87 @@
                 />
               </el-select>
             </el-form-item>
-            <el-form-item label="不合格原因分类" prop="reasonList">
-              <el-select
-                v-model="formLabelAlign.reasonList"
-                multiple
-                placeholder="请选择"
-                value-key="dictValue"
-              >
-                <el-option
-                  v-for="item in dictStroe.dicts.unqualified_type"
-                  :key="item.dictValue"
-                  :label="item.dictLabel"
-                  :value="item.dictValue"
-                />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="是否首检" prop="firstInspection">
-              <el-radio-group v-model="formLabelAlign.firstInspection">
-                <el-radio :value="0" border size="large">否</el-radio>
-                <el-radio :value="1" border size="large">是</el-radio>
-              </el-radio-group>
-            </el-form-item>
-            <el-form-item label="不合格品分布情况及工序" prop="processesList">
-              <el-select
-                v-model="formLabelAlign.processesList"
-                multiple
-                placeholder="请选择"
-                value-key="operationId"
-              >
-                <el-option
-                  v-for="item in operationList"
-                  :key="item.operationId"
-                  :label="item.operationName"
-                  :value="item.operationId"
-                />
-              </el-select>
-            </el-form-item>
 
-            <el-row :gutter="20" style="width: 100%">
-              <el-col :span="12">
-                <el-form-item label="责任/经办者" prop="personResponsible">
+            <el-row>
+              <el-col :span="11">
+                <el-form-item label="缺陷分类" prop="reasonType">
                   <el-select
-                    v-model="formLabelAlign.personResponsible"
-                    filterable
+                    v-model="formLabelAlign.reasonType"
                     placeholder="请选择"
-                    value-key="id"
+                    value-key="dictValue"
+                    @change="selectQuexianType"
                   >
                     <el-option
-                      v-for="item in dictStroe.allUsers"
-                      :key="item.id"
-                      :label="item.userName"
-                      :value="item.userName"
+                      v-for="item in dictStroe.dicts.defect_mana"
+                      :key="item.dictValue"
+                      :label="item.dictLabel"
+                      :value="item.dictValue"
                     />
                   </el-select>
                 </el-form-item>
               </el-col>
-              <el-col :span="12">
-                <el-form-item label="检验员" prop="userName">
+              <el-col :offset="1" :span="12">
+                <el-form-item label="缺陷名称" prop="reasonList">
                   <el-select
-                    v-model="formLabelAlign.userName"
+                    v-model="formLabelAlign.reasonList"
                     filterable
+                    multiple
                     placeholder="请选择"
                     value-key="id"
                   >
                     <el-option
-                      v-for="item in dictStroe.allUsers"
+                      v-for="item in quexianList"
                       :key="item.id"
-                      :label="item.userName"
-                      :value="item.userName"
+                      :label="item.bugName"
+                      :value="item.bugCode"
                     />
                   </el-select>
                 </el-form-item>
               </el-col>
             </el-row>
 
-            <el-form-item label="归档编号" prop="archiveNumber">
-              <el-input v-model="formLabelAlign.archiveNumber" />
+            <el-form-item label="是否首检" prop="firstInspection">
+              <el-radio-group v-model="formLabelAlign.firstInspection">
+                <el-radio :value="0" border size="large">否</el-radio>
+                <el-radio :value="1" border size="large">是</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="不合格品分布情况及工序" prop="processesList">
+              <el-select
+                v-model="formLabelAlign.processesList"
+                multiple
+                placeholder="请选择"
+                value-key="operationId"
+              >
+                <el-option
+                  v-for="item in operationList"
+                  :key="item.operationId"
+                  :label="item.operationName"
+                  :value="item.operationId"
+                />
+              </el-select>
             </el-form-item>
-            <el-form-item label="工作令号" prop="workOrderNumber">
-              <el-input v-model="formLabelAlign.workOrderNumber" />
+
+            <el-form-item label="责任/经办者" prop="personResponsible">
+              <el-select
+                v-model="formLabelAlign.personResponsible"
+                filterable
+                placeholder="请选择"
+                value-key="id"
+              >
+                <el-option
+                  v-for="item in dictStroe.allUsers"
+                  :key="item.id"
+                  :label="item.userName"
+                  :value="item.userName"
+                />
+              </el-select>
             </el-form-item>
-            <el-form-item label="追踪卡号" prop="trackingNumber">
-              <el-input v-model="formLabelAlign.trackingNumber" />
+            <el-form-item>
+              <MultiUpload v-model="formLabelAlign.fileList" />
             </el-form-item>
-            <el-form-item label="特征和程度" prop="remark">
+
+            <el-form-item label="备注" prop="remark">
               <el-input
                 v-model="formLabelAlign.remark"
                 :rows="3"
@@ -175,12 +172,14 @@
 <script lang="ts" setup>
 import {
   addBreakReport,
+  basebugsByType,
   breakReportInfoById,
   operationListByIds,
 } from "@/api/process/reportBreak";
 import { useProcessStore } from "@/store/modules/processView";
 import { useDictionaryStore } from "@/store/modules/dictionary";
 import { getProcessInfo } from "@/api/prosteps";
+import MultiUpload from "@/components/Upload/MultiUpload.vue";
 
 const processStore = useProcessStore();
 const dictStroe = useDictionaryStore();
@@ -192,14 +191,13 @@ const infoData = ref<any>({});
 const formRef = ref<InstanceType<typeof ElForm>>();
 const formLabelAlign = reactive({
   seqNoList: [],
+  reasonType: "",
   reasonList: [],
   processesList: [],
+  fileList: [],
   firstInspection: 0,
   personResponsible: "",
   userName: "",
-  archiveNumber: "",
-  workOrderNumber: "",
-  trackingNumber: "",
   remark: "",
 });
 const rules = reactive({
@@ -238,12 +236,19 @@ const selectProcessWorkSeqChange = useDebounceFn(() => {
   });
 }, 2000);
 
+const quexianList = ref<any>([]);
+const selectQuexianType = (value: string) => {
+  basebugsByType(value).then((res) => {
+    quexianList.value = res.data || [];
+  });
+};
+
 const cancelClick = () => {
   drawerVisible.value = false;
 };
 
 const confirmClick = () => {
-  // drawerVisible.value = false;
+  drawerVisible.value = false;
   formRef.value &&
     formRef.value.validate((valid: boolean) => {
       if (valid) {

+ 0 - 26
src/views/pro-steps/Jiluxiang.vue

@@ -1,26 +0,0 @@
-<template>
-  <!-- <MaterialCollectionDG ref="MaterialCollectionDGRef" />
-  <el-button @click="handleClick">物料采集</el-button>
-  <el-input v-model="input" style="width: 240px" placeholder="Please input" /> -->
-
-  <el-scrollbar style="padding-bottom: 24px">
-    <LeftBarInfo />
-    <Operates />
-  </el-scrollbar>
-</template>
-
-<script lang="ts" setup>
-// import MaterialCollectionDG from "@/components/CommonDialogs/MaterialCollectionDG.vue";
-import LeftBarInfo from "./components/leftBarInfo.vue";
-import Operates from "./components/operates.vue";
-const input = ref("aa");
-// const MaterialCollectionDGRef = ref<any>(null);
-// const handleClick = () => {
-//   MaterialCollectionDGRef.value &&
-//     MaterialCollectionDGRef.value.showMCDG("", () => {
-//       console.log("采集成功");
-//     });
-// };
-</script>
-
-<style lang="scss" scoped></style>

+ 7 - 4
src/views/pro-steps/components/operates.vue

@@ -1,7 +1,11 @@
 <template>
   <div class="containerBox">
-    <div v-for="(item, index) in stepComponents" :key="index"
-      :class="selectIndex == index ? 'operator active' : 'operator'" @click="setIndex(index)">
+    <div
+      v-for="(item, index) in stepComponents"
+      :key="index"
+      :class="selectIndex == index ? 'operator active' : 'operator'"
+      @click="setIndex(index)"
+    >
       <div class="operatorText">{{ item.compentName }}</div>
       <div class="operatorIcon">
         <svg-icon :icon-class="item.compentType" size="45" />
@@ -22,8 +26,6 @@ const reportBreakRef = ref<InstanceType<typeof ReportBreak>>();
 // ================ 报工
 const reportWorkRef = ref<InstanceType<typeof ReportWork>>();
 
-// ================ 委外
-const reportOutsourcing = ref<InstanceType<typeof ReportOutsourcing>>();
 const router = useRouter();
 
 const selectIndex = ref(0);
@@ -44,6 +46,7 @@ const setIndex = (index: number) => {
       router.push({ name: "drawing-list" });
       break;
     case "weiwai":
+      router.push({ name: "appoint-out" });
       break;
     case "baogong":
       reportWorkRef.value?.openReportWorkDrawer();

+ 16 - 6
src/views/pro-steps/index.vue

@@ -3,15 +3,23 @@
     <el-row :gutter="20">
       <el-col :span="4" class="boxStyle">
         <!-- 侧边栏盒子 -->
-        <Jiluxiang />
+        <OperationBar />
       </el-col>
       <el-col :span="20">
         <div class="typeContainer">
           <el-scrollbar>
             <div style="display: flex">
-              <div v-for="(item, index) in stepComponents" :key="index" class="scrollbar-demo-item">
-                <router-link replace :to="{ name: item.name }">
-                  <div :class="getNameClass(index)" class="typeBox" @click="setSelectIndex(index)">
+              <div
+                v-for="(item, index) in stepComponents"
+                :key="index"
+                class="scrollbar-demo-item"
+              >
+                <router-link :to="{ name: item.name }" replace>
+                  <div
+                    :class="getNameClass(index)"
+                    class="typeBox"
+                    @click="setSelectIndex(index)"
+                  >
                     <div class="svgIcon">
                       <svg-icon :icon-class="item.iconName" size="30" />
                     </div>
@@ -27,7 +35,8 @@
           <el-scrollbar>
             <router-view v-slot="{ Component, route }">
               <keep-alive
-                include="Dianjian,Jiluxiang,Duomeiticaiji,Esop,Jingu,Mingpaibangding,Shebeijilu,Tiaoshipipei,Wuliaocaiji">
+                include="Dianjian,Jiluxiang,Duomeiticaiji,Esop,Jingu,Mingpaibangding,Shebeijilu,Tiaoshipipei,Wuliaocaiji"
+              >
                 <component :is="Component" :key="route.fullPath" />
               </keep-alive>
             </router-view>
@@ -40,9 +49,10 @@
 
 <script setup>
 import { onMounted } from "vue";
-import Jiluxiang from "@/views/pro-steps/Jiluxiang.vue";
+import OperationBar from "@/views/pro-steps/operationBar.vue";
 import { useProcessStore } from "@/store";
 import { getOpCompent } from "@/api/prosteps";
+
 const store = useProcessStore();
 defineOptions({ name: "ProSteps" });
 const route = useRoute();

+ 13 - 0
src/views/pro-steps/operationBar.vue

@@ -0,0 +1,13 @@
+<template>
+  <el-scrollbar style="padding-bottom: 24px">
+    <LeftBarInfo />
+    <Operates />
+  </el-scrollbar>
+</template>
+
+<script lang="ts" setup>
+import LeftBarInfo from "./components/leftBarInfo.vue";
+import Operates from "./components/operates.vue";
+</script>
+
+<style lang="scss" scoped></style>