Explorar el Código

1.委外 2.引入emit 3.添加dockerfile 4.package增加test环境

jiaxiaoqiang hace 1 año
padre
commit
64295fdc72

+ 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 },
+  });
+}

+ 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,
+      },
+    },
   ],
 };

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

@@ -3,7 +3,12 @@ import { defineStore } from "pinia";
 import { getUserDicts, getUserList } from "@/api/auth";
 
 export const useDictionaryStore = defineStore("dictionaryStore", () => {
-  const types = ["unqualified_type", "stage", "process_state"];
+  const types = [
+    "unqualified_type",
+    "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>

+ 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>