Browse Source

新模块开发

cz 2 years ago
parent
commit
da37eb7272

+ 0 - 342
src/components/iot/SelectProduct.vue

@@ -1,342 +0,0 @@
-<template>
-  <div class="tenant">
-    <!-- <Banner /> -->
-    <div class="content">
-      <byTable
-        :source="sourceList.data"
-        :pagination="sourceList.pagination"
-        :config="config"
-        :loading="loading"
-        highlight-current-row
-        :selectConfig="selectConfig"
-        :table-events="{
-          //element talbe事件都能传
-          select: select,
-        }"
-        @get-list="getList"
-      >
-        <template #slotName="{ item }">
-          {{ item.createTime }}
-        </template>
-      </byTable>
-    </div>
-    <el-dialog
-      :title="modalType == 'add' ? '添加产品' : '编辑'"
-      v-model="dialogVisible"
-      width="800"
-      v-loading="loading"
-    >
-      <byForm
-        :formConfig="formConfig"
-        :formOption="formOption"
-        v-model="formData.data"
-        :rules="rules"
-        ref="byform"
-      >
-      </byForm>
-      <template #footer>
-        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
-        <el-button
-          type="primary"
-          @click="submitForm('byform')"
-          size="large"
-          :loading="submitLoading"
-        >
-          确 定
-        </el-button>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-  
-<script setup>
-/* eslint-disable vue/no-unused-components */
-import { ElMessage, ElMessageBox } from "element-plus";
-import byTable from "@/components/byTable/index";
-import byForm from "@/components/byForm/index";
-import { computed, defineComponent, ref, toRaw } from "vue";
-const loading = ref(false);
-const submitLoading = ref(false);
-const sourceList = ref({
-  data: [],
-  pagination: {
-    total: 3,
-    pageNum: 1,
-    pageSize: 10,
-  },
-});
-let dialogVisible = ref(false);
-let roomDialogVisible = ref(false);
-let modalType = ref("add");
-let rules = ref({
-  tdaApplicationId: [
-    { required: true, message: "请选择行业名称", trigger: ["blur", "change"] },
-  ],
-  name: [
-    { required: true, message: "请输入产品名称", trigger: ["blur", "change"] },
-  ],
-  manufacturerName: [
-    { required: true, message: "请选择厂商名称", trigger: ["blur", "change"] },
-  ],
-  deviceType: [
-    { required: true, message: "请选择设备类型", trigger: ["blur", "change"] },
-  ],
-});
-const { proxy } = getCurrentInstance();
-const selectConfig = reactive([
-  {
-    label: "行业名称",
-    prop: "id",
-    data: [],
-  },
-  {
-    label: "厂商名称",
-    prop: "id",
-    data: [],
-  },
-]);
-const config = computed(() => {
-  return [
-    {
-      attrs: {
-        label: "行业名称",
-        prop: "appName",
-      },
-    },
-    {
-      attrs: {
-        label: "产品名称",
-        prop: "name",
-        align: "center",
-      },
-    },
-    {
-      attrs: {
-        label: "协议类型",
-        prop: "protocolType",
-        align: "center",
-      },
-    },
-    {
-      attrs: {
-        label: "数据格式",
-        prop: "dataFormat",
-        align: "center",
-      },
-    },
-    {
-      attrs: {
-        label: "厂商名称",
-        prop: "manufacturerName",
-        align: "center",
-      },
-    },
-    {
-      attrs: {
-        label: "设备类型",
-        prop: "deviceType",
-      },
-    },
-
-    {
-      attrs: {
-        label: "操作",
-        width: "100",
-        align: "right",
-      },
-      // 渲染 el-button,一般用在最后一列。
-      renderHTML(row) {
-        return [
-          {
-            attrs: {
-              label: "选择",
-              type: "primary",
-              text: true,
-            },
-            el: "button",
-            click() {
-              handleSelect(row);
-            },
-          },
-        ];
-      },
-    },
-  ];
-});
-
-let formData = reactive({
-  data: {},
-  treeData: [],
-});
-const formOption = reactive({
-  inline: true,
-  labelWidth: 100,
-  itemWidth: 100,
-  rules: [],
-});
-const byform = ref(null);
-const treeData = ref([]);
-const formConfig = reactive([
-  {
-    type: "select",
-    prop: "tdaApplicationId",
-    label: "行业名称",
-    required: true,
-    data: [
-      {
-        name: "aaa", //title || name 是显示的字段名称
-        value: "11", //value || id 是选择的字段值
-      },
-    ],
-  },
-  {
-    type: "input",
-    prop: "name",
-    label: "产品名称",
-    required: true,
-    itemWidth: 100,
-    itemType: "text",
-  },
-  {
-    type: "select",
-    prop: "protocolType",
-    label: "协议类型",
-    disabled: true,
-    required: true,
-  },
-  {
-    type: "select",
-    prop: "dataFormat",
-    label: "数据格式",
-    disabled: true,
-    required: true,
-  },
-  {
-    type: "input",
-    prop: "manufacturerName",
-    label: "厂商名称",
-    required: true,
-  },
-  {
-    type: "input",
-    prop: "deviceType",
-    label: "设备类型",
-    required: true,
-  },
-]);
-const getList = async (req) => {
-  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
-  loading.value = true;
-  proxy
-    .post("/tdaProduct/page", sourceList.value.pagination)
-    .then((message) => {
-      console.log(message);
-      sourceList.value.data = message.rows;
-      sourceList.value.pagination.total = message.total;
-      setTimeout(() => {
-        loading.value = false;
-      }, 200);
-    });
-};
-const openModal = () => {
-  dialogVisible.value = true;
-  modalType.value = "add";
-  formData.data = {
-    protocolType: "HTTP",
-    dataFormat: "JSON",
-  };
-};
-const selection = ref({
-  data: [],
-});
-const select = (_selection, row) => {
-  selection.value.data = _selection;
-  console.log(_selection.length);
-};
-const openRoomModal = () => {
-  roomDialogVisible.value = true;
-  proxy
-    .get("/tenantInfo/roleMenuTreeSelect/" + selection.value.data[0].tenantId)
-    .then((res) => {
-      if (res.code == 200) {
-        treeData.value = res.menus;
-        formData.treeData = res.checkedKeys;
-        tree.value.setCheckedKeys(res.checkedKeys);
-      }
-    });
-};
-const tree = ref(null);
-const submitTree = () => {
-  proxy
-    .post("/tenantInfo/bindingMenu", {
-      tenantId: selection.value.data[0].tenantId,
-      menuIdList: tree.value.getCheckedKeys(),
-    })
-    .then((res) => {
-      ElMessage({
-        message: "保存成功",
-        type: "success",
-      });
-      roomDialogVisible.value = false;
-    });
-};
-
-const submitForm = () => {
-  console.log(byform.value);
-  byform.value.handleSubmit((valid) => {
-    submitLoading.value = true;
-
-    proxy.post("/tdaProduct/" + modalType.value, formData.data).then(
-      (res) => {
-        ElMessage({
-          message: modalType.value == "add" ? "添加成功" : "编辑成功",
-          type: "success",
-        });
-        dialogVisible.value = false;
-        submitLoading.value = false;
-        getList();
-      },
-      (err) => (submitLoading.value = false)
-    );
-  });
-};
-
-const getDtl = (row) => {
-  modalType.value = "edit";
-  proxy.post("/tdaProduct/detail", { id: row.id }).then((res) => {
-    formData.data = res;
-    console.log(formData);
-    dialogVisible.value = true;
-  });
-};
-const selectData = reactive({
-  tradeList: [],
-});
-const getSelect = () => {
-  proxy
-    .post("/tdaApplication/page", { pageNum: 1, pageSize: 9999 })
-    .then((message) => {
-      selectData.tradeList = message.rows;
-      selectConfig[0].data = selectData.tradeList.map((x) => ({
-        label: x.appName,
-        value: x.id,
-      }));
-      formConfig[0].data = selectData.tradeList.map((x) => ({
-        title: x.appName,
-        value: x.id,
-      }));
-    });
-};
-
-getList();
-getSelect();
-const handleSelect = (row) => {
-  proxy.$emit("handleSelect", toRaw(row));
-};
-</script>
-  
-<style lang="scss" scoped>
-.tenant {
-  padding: 20px;
-}
-</style>

+ 513 - 0
src/components/product/SelectMaterial.vue

@@ -0,0 +1,513 @@
+<template>
+  <div class="user">
+    <div class="tree">
+      <treeList
+        title="物料分类"
+        submitType="2"
+        :data="treeListData"
+        v-model="sourceList.pagination.productClassifyId"
+        @change="treeChange"
+        @changeTreeList="getTreeList"
+      >
+      </treeList>
+    </div>
+    <div class="content">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        highlight-current-row
+        :selectConfig="selectConfig"
+        :table-events="{
+          //element talbe事件都能传
+          select: select,
+        }"
+        :action-list="[]"
+        @get-list="getList"
+      >
+        <template #pic="{ item }">
+          <div v-if="item.fileList.length > 0">
+            <img
+              :src="item.fileList[0].fileUrl"
+              class="pic"
+              @click="handleClickFile(item.fileList[0])"
+            />
+          </div>
+          <div v-else></div>
+        </template>
+      </byTable>
+    </div>
+    <el-dialog
+      :title="modalType == 'add' ? '添加' : '编辑'"
+      v-model="dialogVisible"
+      width="500"
+      v-loading="loading"
+    >
+      <byForm
+        :formConfig="formConfig"
+        :formOption="formOption"
+        v-model="formData.data"
+        :rules="rules"
+        ref="byform"
+      >
+        <template #productPic>
+          <div>
+            <el-upload
+              v-model:fileList="fileList"
+              action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+              :data="uploadData"
+              list-type="picture-card"
+              :on-remove="handleRemove"
+              :on-success="handleSuccess"
+              :before-upload="handleBeforeUpload"
+            >
+              <el-icon><Plus /></el-icon>
+            </el-upload>
+          </div>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button
+          type="primary"
+          @click="submitForm('byform')"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+    <el-dialog
+      title="Excel导入"
+      v-model="openExcelDialog"
+      width="400"
+      v-loading="loading"
+    >
+      <template #footer>
+        <el-button @click="openExcelDialog = false" size="large"
+          >取 消</el-button
+        >
+        <el-button
+          type="primary"
+          @click="submitExcel()"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+  
+<script setup>
+/* eslint-disable vue/no-unused-components */
+import { ElMessage, ElMessageBox } from "element-plus";
+import byTable from "@/components/byTable/index";
+import byForm from "@/components/byForm/index";
+import treeList from "@/components/product/treeList";
+
+import { computed, defineComponent, ref } from "vue";
+const loading = ref(false);
+const submitLoading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 3,
+    pageNum: 1,
+    pageSize: 10,
+    type: "",
+    productClassifyId: "",
+    keyword: "",
+    definition: "2",
+  },
+});
+let dialogVisible = ref(false);
+let openExcelDialog = ref(false);
+
+let modalType = ref("add");
+let rules = ref({
+  productClassifyId: [
+    { required: true, message: "请选择物料分类", trigger: "change" },
+  ],
+  type: [{ required: true, message: "请选择物料类型", trigger: "change" }],
+  name: [{ required: true, message: "请输入物料名称", trigger: "blur" }],
+  unit: [{ required: true, message: "请选择单位", trigger: "change" }],
+});
+const { proxy } = getCurrentInstance();
+const selectConfig = computed(() => {
+  return [
+    {
+      label: "物料类型",
+      prop: "type",
+      data: [
+        {
+          label: "原料",
+          value: "1",
+        },
+        {
+          label: "辅料",
+          value: "2",
+        },
+        {
+          label: "配件",
+          value: "3",
+        },
+        {
+          label: "包材",
+          value: "4",
+        },
+        {
+          label: "其他",
+          value: "5",
+        },
+      ],
+    },
+  ];
+});
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "物料类型",
+        prop: "type",
+      },
+      render(type) {
+        return formConfig.value[1].data.find((x) => x.id == type).label;
+      },
+    },
+    {
+      attrs: {
+        label: "物料编码",
+        prop: "code",
+      },
+    },
+    {
+      attrs: {
+        label: "物料名称",
+        prop: "name",
+      },
+    },
+    {
+      attrs: {
+        label: "图片",
+        prop: "unit",
+        slot: "pic",
+      },
+    },
+    {
+      attrs: {
+        label: "单位",
+        prop: "unit",
+      },
+    },
+    {
+      attrs: {
+        label: "规格型号",
+        prop: "spec",
+      },
+    },
+    {
+      attrs: {
+        label: "物料备注",
+        prop: "remark",
+      },
+    },
+
+    {
+      attrs: {
+        label: "操作",
+        width: "200",
+        align: "right",
+      },
+      // 渲染 el-button,一般用在最后一列。
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "选择",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              handleSelect(row);
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+
+let formData = reactive({
+  data: {},
+});
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+const byform = ref(null);
+const treeListData = ref([]);
+const formConfig = computed(() => {
+  return [
+    {
+      type: "treeSelect",
+      prop: "productClassifyId",
+      label: "物料分类",
+      data: [],
+    },
+    {
+      type: "select",
+      prop: "type",
+      label: "物料类型",
+      required: true,
+      data: [
+        {
+          label: "原料",
+          id: "1",
+        },
+        {
+          label: "辅料",
+          id: "2",
+        },
+        {
+          label: "配件",
+          id: "3",
+        },
+        {
+          label: "包材",
+          id: "4",
+        },
+        {
+          label: "其他",
+          id: "5",
+        },
+      ],
+    },
+    {
+      type: "input",
+      prop: "name",
+      label: "物料名称",
+    },
+    {
+      type: "input",
+      prop: "spec",
+      label: "规格型号",
+    },
+    {
+      type: "select",
+      prop: "unit",
+      label: "单位",
+      required: true,
+      data: [
+        {
+          label: "个",
+          id: "个",
+        },
+        {
+          label: "双",
+          id: "双",
+        },
+      ],
+    },
+    {
+      type: "slot",
+      slotName: "productPic",
+      prop: "fileList",
+      label: "产品图片",
+    },
+
+    {
+      type: "input",
+      prop: "remark",
+      label: "备注",
+      itemType: "textarea",
+    },
+  ];
+});
+const newPassword = () => {
+  formData.data.password = generatePassword();
+};
+const generatePassword = () => {
+  var length = 12,
+    charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+    password = "";
+  for (var i = 0, n = charset.length; i < length; ++i) {
+    password += charset.charAt(Math.floor(Math.random() * n));
+  }
+  return password;
+};
+
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  loading.value = true;
+  proxy
+    .post("/productInfo/page", sourceList.value.pagination)
+    .then((message) => {
+      console.log(message);
+      sourceList.value.data = message.rows.map((x) => ({ ...x, fileList: [] }));
+      sourceList.value.pagination.total = message.total;
+      setTimeout(() => {
+        loading.value = false;
+      }, 200);
+
+      const productIdList = message.rows.map((x) => x.id);
+      // 请求文件数据并回显
+      proxy
+        .post("/fileInfo/getList", { businessIdList: productIdList })
+        .then((fileObj) => {
+          for (let i = 0; i < sourceList.value.data.length; i++) {
+            const e = sourceList.value.data[i];
+            for (const key in fileObj) {
+              if (e.id === key) {
+                e.fileList = fileObj[key];
+              }
+            }
+          }
+        });
+    });
+};
+const uploadData = ref({});
+const fileList = ref([]);
+const fileListCopy = ref([]);
+
+const treeChange = (e) => {
+  console.log(e);
+  sourceList.value.pagination.productClassifyId = e.id;
+  getList({ productClassifyId: e.id });
+};
+
+const openModal = () => {
+  dialogVisible.value = true;
+  modalType.value = "add";
+  formData.data = {
+    definition: "2",
+    fileList: [],
+  };
+  fileList.value = [];
+  fileListCopy.value = [];
+};
+
+const openExcel = () => {
+  openExcelDialog.value = true;
+};
+const submitExcel = () => {
+  openExcelDialog.value = false;
+};
+const TreetenantId = ref("");
+const selection = ref({
+  data: [],
+});
+const select = (_selection, row) => {
+  selection.value.data = _selection;
+  console.log(_selection.length);
+};
+
+const tree = ref(null);
+const submitForm = () => {
+  console.log(byform.value);
+  byform.value.handleSubmit((valid) => {
+    formData.data.fileList = fileListCopy.value.map((x) => ({
+      id: x.id,
+      fileName: x.fileName,
+    }));
+    submitLoading.value = true;
+    proxy.post("/productInfo/" + modalType.value, formData.data).then(
+      (res) => {
+        ElMessage({
+          message: modalType.value == "add" ? "添加成功" : "编辑成功",
+          type: "success",
+        });
+        dialogVisible.value = false;
+        submitLoading.value = false;
+        getList();
+      },
+      (err) => {
+        submitLoading.value = false;
+      }
+    );
+  });
+};
+
+const getTreeList = () => {
+  proxy
+    .post("/productClassify/tree", { parentId: "", name: "", definition: "2" })
+    .then((message) => {
+      treeListData.value = message;
+      formConfig.value[0].data = message;
+    });
+};
+
+const getDtl = (row) => {
+  modalType.value = "edit";
+  proxy.post("/productInfo/detail", { id: row.id }).then((res) => {
+    fileList.value = row.fileList.map((x) => ({ ...x, url: x.fileUrl }));
+    fileListCopy.value = [...fileList.value];
+    res.type = res.type + "";
+    res.definition = "2";
+    formData.data = res;
+    dialogVisible.value = true;
+  });
+};
+getTreeList();
+getList();
+const handleBeforeUpload = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadData.value = res.uploadBody;
+  fileListCopy.value.push({
+    id: res.id,
+    fileName: res.fileName,
+    path: res.fileUrl,
+    url: res.fileUrl,
+    uid: file.uid,
+  });
+};
+
+const handleSuccess = (res, file, files) => {
+  // 查当前file的index值去赋值对应的copy变量的值
+  // let uid = file.uid;
+  // const index = fileList.value.findIndex((x) => x.uid === uid);
+  // fileListCopy.value[index].uid = uid;
+};
+
+const handleRemove = (file) => {
+  const index = fileListCopy.value.findIndex(
+    (x) => x.uid === file.uid || x.id === file.id
+  );
+  fileListCopy.value.splice(index, 1);
+};
+
+const handleClickFile = (file) => {
+  window.open(file.fileUrl, "_blank");
+};
+
+const handleSelect = (row) => {
+  proxy.$emit("handleSelect", toRaw(row));
+};
+</script>
+  
+<style lang="scss" scoped>
+.user {
+  padding: 20px;
+  display: flex;
+  justify-content: space-between;
+  .tree {
+    width: 300px;
+  }
+  .content {
+    width: calc(100% - 320px);
+  }
+}
+.pic {
+  object-fit: contain;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  vertical-align: middle;
+}
+</style>

+ 491 - 0
src/components/product/SelectProduct.vue

@@ -0,0 +1,491 @@
+<template>
+  <div class="user">
+    <div class="tree">
+      <treeList
+        title="产品分类"
+        submitType="1"
+        :data="treeListData"
+        v-model="sourceList.pagination.productClassifyId"
+        @change="treeChange"
+        @changeTreeList="getTreeList"
+      >
+      </treeList>
+    </div>
+    <div class="content">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        highlight-current-row
+        :selectConfig="selectConfig"
+        :table-events="{
+          //element talbe事件都能传
+          select: select,
+        }"
+        :action-list="[]"
+        @get-list="getList"
+      >
+        <template #pic="{ item }">
+          <div v-if="item.fileList.length > 0">
+            <img
+              :src="item.fileList[0].fileUrl"
+              class="pic"
+              @click="handleClickFile(item.fileList[0])"
+            />
+          </div>
+          <div v-else></div>
+        </template>
+      </byTable>
+    </div>
+    <el-dialog
+      :title="modalType == 'add' ? '添加' : '编辑'"
+      v-model="dialogVisible"
+      width="500"
+      v-loading="loading"
+    >
+      <byForm
+        :formConfig="formConfig"
+        :formOption="formOption"
+        v-model="formData.data"
+        :rules="rules"
+        ref="byform"
+      >
+        <template #productPic>
+          <div>
+            <el-upload
+              v-model:fileList="fileList"
+              action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+              :data="uploadData"
+              list-type="picture-card"
+              :on-remove="handleRemove"
+              :on-success="handleSuccess"
+              :before-upload="handleBeforeUpload"
+            >
+              <el-icon><Plus /></el-icon>
+            </el-upload>
+          </div>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button
+          type="primary"
+          @click="submitForm('byform')"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+    <el-dialog
+      title="Excel导入"
+      v-model="openExcelDialog"
+      width="400"
+      v-loading="loading"
+    >
+      <template #footer>
+        <el-button @click="openExcelDialog = false" size="large"
+          >取 消</el-button
+        >
+        <el-button
+          type="primary"
+          @click="submitExcel()"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+  
+<script setup>
+/* eslint-disable vue/no-unused-components */
+import { ElMessage, ElMessageBox } from "element-plus";
+import byTable from "@/components/byTable/index";
+import byForm from "@/components/byForm/index";
+import treeList from "@/components/product/treeList";
+
+import { computed, defineComponent, ref } from "vue";
+const loading = ref(false);
+const submitLoading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 3,
+    pageNum: 1,
+    pageSize: 10,
+    type: "",
+    productClassifyId: "",
+    keyword: "",
+    definition: "1",
+  },
+});
+let dialogVisible = ref(false);
+let openExcelDialog = ref(false);
+
+let modalType = ref("add");
+let rules = ref({
+  productClassifyId: [
+    { required: true, message: "请选择产品分类", trigger: "change" },
+  ],
+  type: [{ required: true, message: "请选择产品类型", trigger: "change" }],
+  name: [{ required: true, message: "请输入产品名称", trigger: "blur" }],
+  unit: [{ required: true, message: "请选择单位", trigger: "change" }],
+});
+const { proxy } = getCurrentInstance();
+const selectConfig = computed(() => {
+  return [
+    {
+      label: "产品类型",
+      prop: "type",
+      data: [
+        {
+          label: "成品",
+          value: "1",
+        },
+        {
+          label: "半成品",
+          value: "2",
+        },
+      ],
+    },
+  ];
+});
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "产品类型",
+        prop: "type",
+      },
+      render(type) {
+        return type == 1 ? "成品" : type == 2 ? "半成品" : "";
+      },
+    },
+    {
+      attrs: {
+        label: "产品编码",
+        prop: "code",
+      },
+    },
+    {
+      attrs: {
+        label: "产品名称",
+        prop: "name",
+      },
+    },
+    {
+      attrs: {
+        label: "图片",
+        prop: "unit",
+        slot: "pic",
+      },
+    },
+    {
+      attrs: {
+        label: "单位",
+        prop: "unit",
+      },
+    },
+    {
+      attrs: {
+        label: "规格型号",
+        prop: "spec",
+      },
+    },
+    {
+      attrs: {
+        label: "产品备注",
+        prop: "remark",
+      },
+    },
+
+    {
+      attrs: {
+        label: "操作",
+        width: "200",
+        align: "right",
+      },
+      // 渲染 el-button,一般用在最后一列。
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "选择",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              handleSelect(row);
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+
+const uploadData = ref({});
+const fileList = ref([]);
+const fileListCopy = ref([]);
+
+let formData = reactive({
+  data: {},
+});
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+const byform = ref(null);
+const treeListData = ref([]);
+const formConfig = computed(() => {
+  return [
+    {
+      type: "treeSelect",
+      prop: "productClassifyId",
+      label: "产品分类",
+      data: [],
+    },
+    {
+      type: "select",
+      prop: "type",
+      label: "产品类型",
+      required: true,
+      data: [
+        {
+          label: "成品",
+          id: "1",
+        },
+        {
+          label: "半成品",
+          id: "2",
+        },
+      ],
+    },
+    {
+      type: "input",
+      prop: "name",
+      label: "产品名称",
+    },
+    {
+      type: "input",
+      prop: "spec",
+      label: "规格型号",
+    },
+    {
+      type: "select",
+      prop: "unit",
+      label: "单位",
+      required: true,
+      data: [
+        {
+          label: "个",
+          id: "个",
+        },
+        {
+          label: "双",
+          id: "双",
+        },
+      ],
+    },
+    {
+      type: "slot",
+      slotName: "productPic",
+      prop: "fileList",
+      label: "产品图片",
+    },
+    {
+      type: "input",
+      prop: "remark",
+      label: "备注",
+      itemType: "textarea",
+    },
+  ];
+});
+const newPassword = () => {
+  formData.data.password = generatePassword();
+};
+const generatePassword = () => {
+  var length = 12,
+    charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
+    password = "";
+  for (var i = 0, n = charset.length; i < length; ++i) {
+    password += charset.charAt(Math.floor(Math.random() * n));
+  }
+  return password;
+};
+
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  loading.value = true;
+  proxy
+    .post("/productInfo/page", sourceList.value.pagination)
+    .then((message) => {
+      console.log(message);
+      sourceList.value.data = message.rows.map((x) => ({ ...x, fileList: [] }));
+      sourceList.value.pagination.total = message.total;
+      setTimeout(() => {
+        loading.value = false;
+      }, 200);
+
+      const productIdList = message.rows.map((x) => x.id);
+      // 请求文件数据并回显
+      proxy
+        .post("/fileInfo/getList", { businessIdList: productIdList })
+        .then((fileObj) => {
+          for (let i = 0; i < sourceList.value.data.length; i++) {
+            const e = sourceList.value.data[i];
+            for (const key in fileObj) {
+              if (e.id === key) {
+                e.fileList = fileObj[key];
+              }
+            }
+          }
+        });
+    });
+};
+
+const treeChange = (e) => {
+  console.log(e);
+  sourceList.value.pagination.productClassifyId = e.id;
+  getList({ productClassifyId: e.id });
+};
+
+const openModal = () => {
+  dialogVisible.value = true;
+  modalType.value = "add";
+  formData.data = {
+    definition: "1",
+    type: "1",
+    fileList: [],
+  };
+
+  fileList.value = [];
+  fileListCopy.value = [];
+};
+
+const openExcel = () => {
+  openExcelDialog.value = true;
+};
+const submitExcel = () => {
+  openExcelDialog.value = false;
+};
+const TreetenantId = ref("");
+const selection = ref({
+  data: [],
+});
+const select = (_selection, row) => {
+  selection.value.data = _selection;
+  console.log(_selection.length);
+};
+
+const tree = ref(null);
+const submitForm = () => {
+  console.log(byform.value);
+  byform.value.handleSubmit((valid) => {
+    formData.data.fileList = fileListCopy.value.map((x) => ({
+      id: x.id,
+      fileName: x.fileName,
+    }));
+    submitLoading.value = true;
+    proxy.post("/productInfo/" + modalType.value, formData.data).then(
+      (res) => {
+        ElMessage({
+          message: modalType.value == "add" ? "添加成功" : "编辑成功",
+          type: "success",
+        });
+        dialogVisible.value = false;
+        submitLoading.value = false;
+        getList();
+      },
+      (err) => {
+        submitLoading.value = false;
+      }
+    );
+  });
+};
+
+const getTreeList = () => {
+  proxy
+    .post("/productClassify/tree", { parentId: "", name: "", definition: "1" })
+    .then((message) => {
+      treeListData.value = message;
+      formConfig.value[0].data = message;
+    });
+};
+
+const getDtl = (row) => {
+  modalType.value = "edit";
+  proxy.post("/productInfo/detail", { id: row.id }).then((res) => {
+    fileList.value = row.fileList.map((x) => ({ ...x, url: x.fileUrl }));
+    fileListCopy.value = [...fileList.value];
+    res.type = res.type + ""; //type回显
+    res.definition = "1"; //产品
+    formData.data = res;
+    dialogVisible.value = true;
+  });
+};
+getTreeList();
+getList();
+const handleBeforeUpload = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadData.value = res.uploadBody;
+  fileListCopy.value.push({
+    id: res.id,
+    fileName: res.fileName,
+    path: res.fileUrl,
+    url: res.fileUrl,
+    uid: file.uid,
+  });
+};
+
+const handleSuccess = (res, file, files) => {
+  // 查当前file的index值去赋值对应的copy变量的值
+  // let uid = file.uid;
+  // const index = fileList.value.findIndex((x) => x.uid === uid);
+  // fileListCopy.value[index].uid = uid;
+};
+
+const handleRemove = (file) => {
+  const index = fileListCopy.value.findIndex(
+    (x) => x.uid === file.uid || x.id === file.id
+  );
+  fileListCopy.value.splice(index, 1);
+};
+
+const handleClickFile = (file) => {
+  window.open(file.fileUrl, "_blank");
+};
+
+const handleSelect = (row) => {
+  proxy.$emit("handleSelect", toRaw(row));
+};
+</script>
+  
+<style lang="scss" scoped>
+.user {
+  padding: 20px;
+  display: flex;
+  justify-content: space-between;
+  .tree {
+    width: 300px;
+  }
+  .content {
+    width: calc(100% - 320px);
+  }
+}
+.pic {
+  object-fit: contain;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  vertical-align: middle;
+}
+</style>

+ 0 - 0
src/views/product/treeList.vue → src/components/product/treeList.vue


+ 96 - 5
src/views/product/material/index.vue

@@ -37,8 +37,15 @@
         ]"
         @get-list="getList"
       >
-        <template #slotName="{ item }">
-          {{ item.createTime }}
+        <template #pic="{ item }">
+          <div v-if="item.fileList.length > 0">
+            <img
+              :src="item.fileList[0].fileUrl"
+              class="pic"
+              @click="handleClickFile(item.fileList[0])"
+            />
+          </div>
+          <div v-else></div>
         </template>
       </byTable>
     </div>
@@ -55,6 +62,21 @@
         :rules="rules"
         ref="byform"
       >
+        <template #productPic>
+          <div>
+            <el-upload
+              v-model:fileList="fileList"
+              action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+              :data="uploadData"
+              list-type="picture-card"
+              :on-remove="handleRemove"
+              :on-success="handleSuccess"
+              :before-upload="handleBeforeUpload"
+            >
+              <el-icon><Plus /></el-icon>
+            </el-upload>
+          </div>
+        </template>
       </byForm>
       <template #footer>
         <el-button @click="dialogVisible = false" size="large">取 消</el-button>
@@ -96,7 +118,8 @@
 import { ElMessage, ElMessageBox } from "element-plus";
 import byTable from "@/components/byTable/index";
 import byForm from "@/components/byForm/index";
-import treeList from "../treeList";
+import treeList from "@/components/product/treeList";
+
 import { computed, defineComponent, ref } from "vue";
 const loading = ref(false);
 const submitLoading = ref(false);
@@ -182,6 +205,7 @@ const config = computed(() => {
       attrs: {
         label: "图片",
         prop: "unit",
+        slot: "pic",
       },
     },
     {
@@ -335,6 +359,12 @@ const formConfig = computed(() => {
         },
       ],
     },
+    {
+      type: "slot",
+      slotName: "productPic",
+      prop: "fileList",
+      label: "产品图片",
+    },
 
     {
       type: "input",
@@ -364,13 +394,31 @@ const getList = async (req) => {
     .post("/productInfo/page", sourceList.value.pagination)
     .then((message) => {
       console.log(message);
-      sourceList.value.data = message.rows;
+      sourceList.value.data = message.rows.map((x) => ({ ...x, fileList: [] }));
       sourceList.value.pagination.total = message.total;
       setTimeout(() => {
         loading.value = false;
       }, 200);
+
+      const productIdList = message.rows.map((x) => x.id);
+      // 请求文件数据并回显
+      proxy
+        .post("/fileInfo/getList", { businessIdList: productIdList })
+        .then((fileObj) => {
+          for (let i = 0; i < sourceList.value.data.length; i++) {
+            const e = sourceList.value.data[i];
+            for (const key in fileObj) {
+              if (e.id === key) {
+                e.fileList = fileObj[key];
+              }
+            }
+          }
+        });
     });
 };
+const uploadData = ref({});
+const fileList = ref([]);
+const fileListCopy = ref([]);
 
 const treeChange = (e) => {
   console.log(e);
@@ -385,6 +433,8 @@ const openModal = () => {
     definition: "2",
     fileList: [],
   };
+  fileList.value = [];
+  fileListCopy.value = [];
 };
 
 const openExcel = () => {
@@ -406,6 +456,10 @@ const tree = ref(null);
 const submitForm = () => {
   console.log(byform.value);
   byform.value.handleSubmit((valid) => {
+    formData.data.fileList = fileListCopy.value.map((x) => ({
+      id: x.id,
+      fileName: x.fileName,
+    }));
     submitLoading.value = true;
     proxy.post("/productInfo/" + modalType.value, formData.data).then(
       (res) => {
@@ -436,15 +490,45 @@ const getTreeList = () => {
 const getDtl = (row) => {
   modalType.value = "edit";
   proxy.post("/productInfo/detail", { id: row.id }).then((res) => {
+    fileList.value = row.fileList.map((x) => ({ ...x, url: x.fileUrl }));
+    fileListCopy.value = [...fileList.value];
     res.type = res.type + "";
     res.definition = "2";
-    res.fileList = [];
     formData.data = res;
     dialogVisible.value = true;
   });
 };
 getTreeList();
 getList();
+const handleBeforeUpload = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadData.value = res.uploadBody;
+  fileListCopy.value.push({
+    id: res.id,
+    fileName: res.fileName,
+    path: res.fileUrl,
+    url: res.fileUrl,
+    uid: file.uid,
+  });
+};
+
+const handleSuccess = (res, file, files) => {
+  // 查当前file的index值去赋值对应的copy变量的值
+  // let uid = file.uid;
+  // const index = fileList.value.findIndex((x) => x.uid === uid);
+  // fileListCopy.value[index].uid = uid;
+};
+
+const handleRemove = (file) => {
+  const index = fileListCopy.value.findIndex(
+    (x) => x.uid === file.uid || x.id === file.id
+  );
+  fileListCopy.value.splice(index, 1);
+};
+
+const handleClickFile = (file) => {
+  window.open(file.fileUrl, "_blank");
+};
 </script>
   
 <style lang="scss" scoped>
@@ -459,4 +543,11 @@ getList();
     width: calc(100% - 320px);
   }
 }
+.pic {
+  object-fit: contain;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  vertical-align: middle;
+}
 </style>

+ 100 - 8
src/views/product/product/index.vue

@@ -37,8 +37,15 @@
         ]"
         @get-list="getList"
       >
-        <template #slotName="{ item }">
-          {{ item.createTime }}
+        <template #pic="{ item }">
+          <div v-if="item.fileList.length > 0">
+            <img
+              :src="item.fileList[0].fileUrl"
+              class="pic"
+              @click="handleClickFile(item.fileList[0])"
+            />
+          </div>
+          <div v-else></div>
         </template>
       </byTable>
     </div>
@@ -55,6 +62,21 @@
         :rules="rules"
         ref="byform"
       >
+        <template #productPic>
+          <div>
+            <el-upload
+              v-model:fileList="fileList"
+              action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+              :data="uploadData"
+              list-type="picture-card"
+              :on-remove="handleRemove"
+              :on-success="handleSuccess"
+              :before-upload="handleBeforeUpload"
+            >
+              <el-icon><Plus /></el-icon>
+            </el-upload>
+          </div>
+        </template>
       </byForm>
       <template #footer>
         <el-button @click="dialogVisible = false" size="large">取 消</el-button>
@@ -96,7 +118,8 @@
 import { ElMessage, ElMessageBox } from "element-plus";
 import byTable from "@/components/byTable/index";
 import byForm from "@/components/byForm/index";
-import treeList from "../treeList";
+import treeList from "@/components/product/treeList";
+
 import { computed, defineComponent, ref } from "vue";
 const loading = ref(false);
 const submitLoading = ref(false);
@@ -170,6 +193,7 @@ const config = computed(() => {
       attrs: {
         label: "图片",
         prop: "unit",
+        slot: "pic",
       },
     },
     {
@@ -250,6 +274,10 @@ const config = computed(() => {
   ];
 });
 
+const uploadData = ref({});
+const fileList = ref([]);
+const fileListCopy = ref([]);
+
 let formData = reactive({
   data: {},
 });
@@ -311,7 +339,12 @@ const formConfig = computed(() => {
         },
       ],
     },
-
+    {
+      type: "slot",
+      slotName: "productPic",
+      prop: "fileList",
+      label: "产品图片",
+    },
     {
       type: "input",
       prop: "remark",
@@ -340,11 +373,26 @@ const getList = async (req) => {
     .post("/productInfo/page", sourceList.value.pagination)
     .then((message) => {
       console.log(message);
-      sourceList.value.data = message.rows;
+      sourceList.value.data = message.rows.map((x) => ({ ...x, fileList: [] }));
       sourceList.value.pagination.total = message.total;
       setTimeout(() => {
         loading.value = false;
       }, 200);
+
+      const productIdList = message.rows.map((x) => x.id);
+      // 请求文件数据并回显
+      proxy
+        .post("/fileInfo/getList", { businessIdList: productIdList })
+        .then((fileObj) => {
+          for (let i = 0; i < sourceList.value.data.length; i++) {
+            const e = sourceList.value.data[i];
+            for (const key in fileObj) {
+              if (e.id === key) {
+                e.fileList = fileObj[key];
+              }
+            }
+          }
+        });
     });
 };
 
@@ -362,6 +410,9 @@ const openModal = () => {
     type: "1",
     fileList: [],
   };
+
+  fileList.value = [];
+  fileListCopy.value = [];
 };
 
 const openExcel = () => {
@@ -383,6 +434,10 @@ const tree = ref(null);
 const submitForm = () => {
   console.log(byform.value);
   byform.value.handleSubmit((valid) => {
+    formData.data.fileList = fileListCopy.value.map((x) => ({
+      id: x.id,
+      fileName: x.fileName,
+    }));
     submitLoading.value = true;
     proxy.post("/productInfo/" + modalType.value, formData.data).then(
       (res) => {
@@ -413,15 +468,45 @@ const getTreeList = () => {
 const getDtl = (row) => {
   modalType.value = "edit";
   proxy.post("/productInfo/detail", { id: row.id }).then((res) => {
-    res.type = res.type + "";
-    res.definition = "1";
-    res.fileList = [];
+    fileList.value = row.fileList.map((x) => ({ ...x, url: x.fileUrl }));
+    fileListCopy.value = [...fileList.value];
+    res.type = res.type + ""; //type回显
+    res.definition = "1"; //产品
     formData.data = res;
     dialogVisible.value = true;
   });
 };
 getTreeList();
 getList();
+const handleBeforeUpload = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadData.value = res.uploadBody;
+  fileListCopy.value.push({
+    id: res.id,
+    fileName: res.fileName,
+    path: res.fileUrl,
+    url: res.fileUrl,
+    uid: file.uid,
+  });
+};
+
+const handleSuccess = (res, file, files) => {
+  // 查当前file的index值去赋值对应的copy变量的值
+  // let uid = file.uid;
+  // const index = fileList.value.findIndex((x) => x.uid === uid);
+  // fileListCopy.value[index].uid = uid;
+};
+
+const handleRemove = (file) => {
+  const index = fileListCopy.value.findIndex(
+    (x) => x.uid === file.uid || x.id === file.id
+  );
+  fileListCopy.value.splice(index, 1);
+};
+
+const handleClickFile = (file) => {
+  window.open(file.fileUrl, "_blank");
+};
 </script>
   
 <style lang="scss" scoped>
@@ -436,4 +521,11 @@ getList();
     width: calc(100% - 320px);
   }
 }
+.pic {
+  object-fit: contain;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  vertical-align: middle;
+}
 </style>

+ 336 - 71
src/views/production/project/bom/index.vue

@@ -21,13 +21,18 @@
         ]"
         @get-list="getList"
       >
-        <template #slotName="{ item }">
-          {{ item.createTime }}
+        <template #versionSlot="{ item }">
+          <div
+            style="cursor: pointer; color: #409eff"
+            @click="hanldeOpenVer(item)"
+          >
+            v{{ item.versionNumber }}
+          </div>
         </template>
       </byTable>
     </div>
     <el-dialog
-      :title="modalType == 'add' ? '添加BOM' : '编辑BOM'"
+      :title="titleText"
       v-model="dialogVisible"
       width="800"
       v-loading="loading"
@@ -41,7 +46,9 @@
       >
         <template #slot>
           <div>
-            <el-button type="primary" plain>添加物料/半成品</el-button>
+            <el-button type="primary" plain @click="openMaterial = true"
+              >添加物料/半成品</el-button
+            >
             <el-button type="primary" plain> Excel导入</el-button>
             <el-form
               ref="tableForm"
@@ -50,19 +57,19 @@
               label-width="0px"
               style="margin-top: 15px"
             >
-              <el-table :data="formData.data.productList">
-                <el-table-column prop="name" label="物料编码" />
-                <el-table-column prop="state" label="物料名称" />
-                <el-table-column prop="city" label="单位" />
-                <el-table-column prop="num" label="数量" width="150">
+              <el-table :data="formData.data.bomDetailList">
+                <el-table-column prop="productCode" label="物料编码" />
+                <el-table-column prop="productName" label="物料名称" />
+                <el-table-column prop="productUnit" label="单位" />
+                <el-table-column prop="quantity" label="数量" width="150">
                   <template #default="{ row, $index }">
                     <el-form-item
-                      :prop="'productList.' + $index + '.num'"
-                      :rules="rules.num"
+                      :prop="'bomDetailList.' + $index + '.quantity'"
+                      :rules="rules.quantity"
                       :inline-message="true"
                     >
                       <el-input-number
-                        v-model="row.num"
+                        v-model="row.quantity"
                         :precision="2"
                         :controls="false"
                         :min="1"
@@ -73,12 +80,12 @@
                 <el-table-column prop="zip" label="成本" width="150">
                   <template #default="{ row, $index }">
                     <el-form-item
-                      :prop="'productList.' + $index + '.num'"
-                      :rules="rules.num"
+                      :prop="'bomDetailList.' + $index + '.cost'"
+                      :rules="rules.cost"
                       :inline-message="true"
                     >
                       <el-input-number
-                        v-model="row.aa"
+                        v-model="row.cost"
                         :precision="2"
                         :controls="false"
                         :min="1"
@@ -87,8 +94,10 @@
                   </template>
                 </el-table-column>
                 <el-table-column prop="zip" label="操作" width="100">
-                  <template #default>
-                    <el-button type="primary" link>删除</el-button>
+                  <template #default="{ $index }">
+                    <el-button type="primary" link @click="handleRemove($index)"
+                      >删除</el-button
+                    >
                   </template>
                 </el-table-column>
               </el-table>
@@ -97,12 +106,74 @@
         </template>
       </byForm>
       <template #footer>
-        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <div v-if="isShowBtns">
+          <el-button @click="dialogVisible = false" size="large"
+            >取 消</el-button
+          >
+          <el-button
+            type="primary"
+            @click="submitForm('byform')"
+            size="large"
+            :loading="submitLoading"
+          >
+            确 定
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <el-dialog
+      v-model="openMaterial"
+      title="选择产品"
+      width="70%"
+      append-to-body
+    >
+      <SelectMaterial @handleSelect="handleSelect"></SelectMaterial>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="openMaterial = false">取消</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <el-dialog v-model="openVersion" title="切换版本" width="30%">
+      <byForm
+        :formConfig="formConfig1"
+        :formOption="formOption"
+        v-model="formData.data1"
+        :rules="rules1"
+        ref="byform1"
+      >
+        <template #versionSlot>
+          <div>
+            <el-select
+              v-model="formData.data1.versionNumber"
+              placeholder="请选择版本号"
+              @change="changeRowData"
+            >
+              <el-option
+                v-for="item in versionData"
+                :label="'v' + item.versionNumber"
+                :value="item.versionNumber"
+              />
+            </el-select>
+            <el-button
+              type="primary"
+              link
+              style="margin-left: 10px"
+              @click="handleGetDetails"
+              v-if="formData.data1.versionNumber"
+              >查看</el-button
+            >
+          </div>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="openVersion = false" size="large">取 消</el-button>
         <el-button
           type="primary"
-          @click="submitForm('byform')"
+          @click="handleChangeVer('byform1')"
           size="large"
-          :loading="submitLoading"
         >
           确 定
         </el-button>
@@ -112,11 +183,13 @@
 </template>
   
 <script setup>
-/* eslint-disable vue/no-unused-components */
+/* eslint-disabled vue/no-unused-components */
 import { ElMessage, ElMessageBox } from "element-plus";
 import byTable from "@/components/byTable/index";
 import byForm from "@/components/byForm/index";
-import { computed, defineComponent, ref } from "vue";
+import { computed, defineComponent, ref, watch, watchEffect } from "vue";
+import SelectMaterial from "@/components/product/SelectMaterial";
+
 const loading = ref(false);
 const submitLoading = ref(false);
 const sourceList = ref({
@@ -128,15 +201,24 @@ const sourceList = ref({
   },
 });
 let dialogVisible = ref(false);
+let openMaterial = ref(false);
+let openVersion = ref(false);
+let titleText = ref("");
 let modalType = ref("add");
 let rules = ref({
-  type: [{ required: true, message: "请选择车间类型", trigger: "change" }],
-  name: [{ required: true, message: "请输入车间名称", trigger: "blur" }],
-  num: [{ required: true, message: "请输入数量", trigger: "blur" }],
+  productId: [{ required: true, message: "请选择产品", trigger: "change" }],
+  quantity: [{ required: true, message: "请输入数量", trigger: "blur" }],
+  cost: [{ required: true, message: "请输入成本", trigger: "blur" }],
   personLiableId: [
     { required: true, message: "请选择负责人", trigger: "change" },
   ],
 });
+let rules1 = ref({
+  versionNumber: [
+    { required: true, message: "请选择版本号", trigger: "change" },
+  ],
+});
+
 const { proxy } = getCurrentInstance();
 const selectConfig = reactive([
   // {
@@ -163,45 +245,52 @@ const config = computed(() => {
     {
       attrs: {
         label: "产品类型",
-        prop: "name",
-        width: 150,
+        prop: "productType",
+        width: 100,
+      },
+      render(productType) {
+        return productType ? "成品" : "半成品";
       },
     },
     {
       attrs: {
         label: "产品编码",
-        prop: "name",
+        prop: "productCode",
         width: 150,
       },
     },
     {
       attrs: {
         label: "产品名称",
-        prop: "remarks",
+        prop: "productName",
       },
     },
     {
       attrs: {
         label: "状态",
-        prop: "remarks",
+        prop: "status",
+      },
+      render(status) {
+        return status === 1 ? "启用" : "禁用";
       },
     },
     {
       attrs: {
         label: "当前版本",
-        prop: "remarks",
+        prop: "versionNumber",
+        slot: "versionSlot",
       },
     },
     {
       attrs: {
         label: "最近维护人",
-        prop: "remarks",
+        prop: "updateUserName",
       },
     },
     {
       attrs: {
         label: "最近维护时间",
-        prop: "remarks",
+        prop: "updateTime",
       },
     },
     // {
@@ -238,7 +327,7 @@ const config = computed(() => {
             },
             el: "button",
             click() {
-              getDtl(row);
+              getDtl(row, "add", true);
             },
           },
           {
@@ -249,18 +338,39 @@ const config = computed(() => {
             },
             el: "button",
             click() {
-              getDtl(row);
+              getDtl(row, "edit");
             },
           },
           {
             attrs: {
-              label: "禁用",
+              label: row.status === 1 ? "禁用" : "启用",
               type: "primary",
               text: true,
             },
             el: "button",
             click() {
-              getDtl(row);
+              ElMessageBox.confirm(
+                `是否${row.status === 1 ? "禁用" : "启用"} ?`,
+                "提示",
+                {
+                  confirmButtonText: "确定",
+                  cancelButtonText: "取消",
+                  type: "warning",
+                }
+              ).then(() => {
+                proxy.post("/bomInfo/detail", { id: row.id }).then((res) => {
+                  res.bomDetailList = res.bomDetailVoList;
+                  res.status = row.status ? 0 : 1;
+                  formData.data = res;
+                  proxy.post("/bomInfo/edit", formData.data).then((res) => {
+                    ElMessage({
+                      message: "操作成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+                });
+              });
             },
           },
           // {
@@ -304,7 +414,13 @@ const config = computed(() => {
 
 let formData = reactive({
   data: {
-    type: "1",
+    productId: "",
+    addType: "1",
+    bomDetailList: [],
+  },
+  data1: {
+    productId: "",
+    versionNumber: "",
   },
 });
 const formOption = reactive({
@@ -314,25 +430,64 @@ const formOption = reactive({
   rules: [],
 });
 const byform = ref(null);
-const formConfig = computed(() => {
-  return [
-    {
-      type: "select",
-      prop: "name",
-      label: "产品名称",
-      required: true,
+const formConfig = reactive([
+  {
+    type: "select",
+    prop: "productId",
+    label: "产品名称",
+    required: true,
+    disabled: false,
+    isLoad: {
+      url: "/productInfo/page",
+      req: {
+        pageNum: 1,
+        pageSize: 9999,
+        definition: "1",
+      },
+      labelKey: "name",
+      labelVal: "id",
+      method: "post",
+      resUrl: "rows",
     },
-    {
-      type: "slot",
-      slotName: "slot",
-      label: "物料信息",
+  },
+  {
+    type: "slot",
+    slotName: "slot",
+    label: "物料信息",
+  },
+]);
+
+const formConfig1 = reactive([
+  {
+    type: "select",
+    prop: "productId",
+    label: "产品名称",
+    required: true,
+    disabled: true,
+    isLoad: {
+      url: "/productInfo/page",
+      req: {
+        pageNum: 1,
+        pageSize: 9999,
+        definition: "1",
+      },
+      labelKey: "name",
+      labelVal: "id",
+      method: "post",
+      resUrl: "rows",
     },
-  ];
-});
+  },
+  {
+    type: "slot",
+    slotName: "versionSlot",
+    label: "切换版本",
+    required: true,
+  },
+]);
 const getList = async (req) => {
   sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
   loading.value = true;
-  proxy.post("/workshop/page", sourceList.value.pagination).then((message) => {
+  proxy.post("/bomInfo/page", sourceList.value.pagination).then((message) => {
     console.log(message);
     sourceList.value.data = message.rows;
     sourceList.value.pagination.total = message.total;
@@ -343,20 +498,31 @@ const getList = async (req) => {
 };
 const openModal = () => {
   dialogVisible.value = true;
+  titleText.value = "添加BOM";
   modalType.value = "add";
-  formData.data = {};
-  formData.data.productList = [
-    {
-      num: "",
-    },
-  ];
+  formData.data = {
+    productId: "",
+    addType: "1",
+    bomDetailList: [],
+  };
+  formConfig[0].disabled = false;
 };
 const submitForm = () => {
-  proxy.$refs.tableForm.validate((vaild) => {
-    if (vaild) {
-      byform.value.handleSubmit((valid) => {
+  byform.value.handleSubmit((valid) => {
+    if (!formData.data.bomDetailList.length > 0)
+      return ElMessage({
+        message: "请添加物料/半成品",
+        type: "info",
+      });
+    proxy.$refs.tableForm.validate((vaild) => {
+      if (vaild) {
         submitLoading.value = true;
-        proxy.post("/workshop/" + modalType.value, formData.data).then(
+        formData.data.bomDetailList = formData.data.bomDetailList.map((x) => ({
+          productId: x.productId,
+          quantity: x.quantity,
+          cost: x.cost,
+        }));
+        proxy.post("/bomInfo/" + modalType.value, formData.data).then(
           (res) => {
             ElMessage({
               message: modalType.value == "add" ? "添加成功" : "编辑成功",
@@ -371,22 +537,121 @@ const submitForm = () => {
             submitLoading.value = false;
           }
         );
-      });
-    }
+      }
+    });
+  });
+};
+const byform1 = ref(null);
+const handleChangeVer = () => {
+  if (formData.data1.versionNumber === "")
+    return ElMessage({
+      message: "请选择版本号",
+      type: "info",
+    });
+  byform1.value.handleSubmit((valid) => {
+    submitLoading.value = true;
+    proxy.post("/bomInfo/editVersion", formData.data1).then(
+      (res) => {
+        ElMessage({
+          message: "切换成功",
+          type: "success",
+        });
+        openVersion.value = false;
+        submitLoading.value = false;
+        getList();
+      },
+      (err) => {
+        submitLoading.value = false;
+      }
+    );
   });
 };
 
-const getDtl = (row) => {
-  modalType.value = "edit";
-  proxy.post("/workshop/detail", { id: row.id }).then((res) => {
-    res.type = res.type + "";
-    formData.data = res;
-    console.log(formData);
+const getDtl = (row, type, isNew) => {
+  formConfig[0].disabled = true; //禁止修改产品信息
+  modalType.value = type;
+  proxy.post("/bomInfo/detail", { id: row.id }).then((res) => {
+    if (isNew) {
+      titleText.value = "新建版本";
+      formData.data = {
+        addType: "2", //2为新建版本
+        productId: res.productId,
+        bomDetailList: [],
+      };
+    } else {
+      titleText.value = openVersion.value ? "版本详情" : "编辑BOM";
+      res.bomDetailList = res.bomDetailVoList;
+      formData.data = res;
+    }
     dialogVisible.value = true;
   });
 };
 
-// getList();
+const handleSelect = (row) => {
+  const flag = formData.data.bomDetailList.some((x) => x.productId === row.id);
+  if (flag)
+    return ElMessage({
+      message: "该物料已选择",
+      type: "info",
+    });
+  formData.data.bomDetailList.push({
+    productId: row.id,
+    productCode: row.code,
+    productName: row.name,
+    productUnit: row.unit,
+    quantity: null,
+    cost: null,
+  });
+  return ElMessage({
+    message: "选择成功",
+    type: "success",
+  });
+};
+
+const handleRemove = (index) => {
+  console.log(index, "as");
+  formData.data.bomDetailList.splice(index, 1);
+  return ElMessage({
+    message: "删除成功",
+    type: "success",
+  });
+};
+
+const versionData = ref([]);
+let isShowBtns = ref(true);
+let rowData = ref({});
+const hanldeOpenVer = (row) => {
+  formData.data1 = {
+    productId: "",
+    versionNumber: "",
+  };
+  rowData.value = row;
+  formData.data1.productId = row.productId;
+  proxy
+    .post("/bomInfo/getVersion", { productId: row.productId })
+    .then((res) => {
+      versionData.value = res;
+      formData.data1.versionNumber = res.filter(
+        (x) => x.currentVersion === 1
+      )[0].versionNumber;
+      openVersion.value = true;
+    });
+};
+
+const handleGetDetails = () => {
+  getDtl(rowData.value);
+};
+
+watchEffect(() => {
+  isShowBtns.value = openVersion.value ? false : true;
+  //监听是否是在切换版本中,如是隐藏提交添加弹窗的按钮模块
+});
+
+const changeRowData = (val) => {
+  const data = versionData.value.find((x) => x.versionNumber === val);
+  rowData.value = data ? data : {};
+};
+getList();
 </script>
   
 <style lang="scss" scoped>

+ 2 - 2
src/views/production/project/processes/index.vue

@@ -56,7 +56,7 @@
               :on-remove="handleRemove"
               :on-success="handleSuccess"
               :before-upload="handleBeforeUpload"
-              accept="pdf"
+              accept=".pdf"
             >
               <el-button type="primary">点击上传</el-button>
               <template #file>
@@ -341,7 +341,7 @@ const handleClickFile = (row) => {
   });
   let id = row.id;
   proxy.post("/fileInfo/getList", { businessIdList: [id] }).then((res) => {
-    const file = res[id];
+    const file = res[id][0];
     window.open(file.fileUrl, "_blank");
   });
 };

+ 124 - 73
src/views/production/project/technology/index.vue

@@ -21,15 +21,30 @@
         ]"
         @get-list="getList"
       >
-        <template #slotName="{ item }">
-          {{ item.createTime }}
+        <template #line="{ item }">
+          <span v-for="(x, i) in item.processRouteNameList" :key="i">
+            {{ x }}
+            <span
+              style="margin: 0 3px"
+              v-if="i + 1 < item.processRouteNameList.length"
+              >>
+            </span>
+          </span>
+        </template>
+        <template #product="{ item }">
+          <span v-for="(x, i) in item.applicableProductsNameList" :key="i">
+            {{ x }}
+            <span v-if="i + 1 < item.applicableProductsNameList.length"
+              >,
+            </span>
+          </span>
         </template>
       </byTable>
     </div>
     <el-dialog
       :title="modalType == 'add' ? '添加工艺' : '编辑工艺'"
       v-model="dialogVisible"
-      width="800"
+      width="650"
       v-loading="loading"
     >
       <byForm
@@ -46,15 +61,21 @@
             filter-placeholder="搜索"
             :data="lineData"
             :titles="['可选', '已选']"
+            target-order="push"
           >
             <template #default="{ option }">
-              <div
-                draggable="true"
-                @dragstart="dragStar($event, option)"
-                @dragover="dragOver($event, option)"
-                style="cursor: default"
-              >
-                {{ option.label }}
+              <div class="parent">
+                <!-- 只有选择的数据才能拖拽 -->
+                <div
+                  :draggable="selectLine.includes(option.key)"
+                  @dragstart="dragStar($event, option)"
+                  @dragover="dragOver($event, option)"
+                  @drop="handleDrop($event)"
+                  style="cursor: default"
+                  :id="option.key"
+                >
+                  {{ option.label }}
+                </div>
               </div>
             </template>
           </el-transfer>
@@ -66,7 +87,7 @@
             </el-button>
             <div style="margin-top: 15px">
               <el-tag
-                class="ml-2"
+                style="margin-right: 10px"
                 type="info"
                 closable
                 v-for="(product, index) in productList"
@@ -77,7 +98,6 @@
             </div>
           </div>
         </template>
-        <!-- :filter-method="filterMethod" -->
       </byForm>
       <template #footer>
         <el-button @click="dialogVisible = false" size="large">取 消</el-button>
@@ -95,7 +115,7 @@
       v-model="openProduct"
       title="选择产品"
       width="70%"
-      :before-close="handleClose"
+      append-to-body
     >
       <SelectProduct @handleSelect="handleSelect"></SelectProduct>
       <template #footer>
@@ -112,7 +132,7 @@
 import { ElMessage, ElMessageBox } from "element-plus";
 import byTable from "@/components/byTable/index";
 import byForm from "@/components/byForm/index";
-import SelectProduct from "@/components/iot/SelectProduct";
+import SelectProduct from "@/components/product/SelectProduct";
 
 import { computed, defineComponent, ref, toRaw } from "vue";
 const loading = ref(false);
@@ -125,7 +145,7 @@ const sourceList = ref({
     pageSize: 10,
   },
 });
-let lineData = reactive([
+let lineData = ref([
   {
     key: "1",
     label: "测试1",
@@ -152,11 +172,7 @@ let dialogVisible = ref(false);
 let openProduct = ref(false);
 let modalType = ref("add");
 let rules = ref({
-  type: [{ required: true, message: "请选择车间类型", trigger: "change" }],
-  name: [{ required: true, message: "请输入车间名称", trigger: "blur" }],
-  personLiableId: [
-    { required: true, message: "请选择负责人", trigger: "change" },
-  ],
+  name: [{ required: true, message: "请输入工艺名称", trigger: "blur" }],
 });
 const { proxy } = getCurrentInstance();
 const selectConfig = reactive([
@@ -188,26 +204,19 @@ const config = computed(() => {
         width: 150,
       },
     },
+
     {
       attrs: {
         label: "工艺路线",
-        prop: "remarks",
+
+        slot: "line",
       },
     },
     {
       attrs: {
         label: "适用产品",
-        prop: "type",
-        width: 120,
-      },
-      render(type) {
-        return type == 1
-          ? "普通车间"
-          : type == 2
-          ? "半自动化车间"
-          : type == "3"
-          ? "自动化车间"
-          : "";
+
+        slot: "product",
       },
     },
     {
@@ -256,7 +265,7 @@ const config = computed(() => {
               ).then(() => {
                 // 删除
                 proxy
-                  .post("/workshop/delete", {
+                  .post("/technology/delete", {
                     id: row.id,
                   })
                   .then((res) => {
@@ -277,7 +286,10 @@ const config = computed(() => {
 
 let formData = reactive({
   data: {
-    type: "1",
+    name: "",
+    processRouteList: [],
+    remarks: "",
+    productList: [],
   },
 });
 const formOption = reactive({
@@ -305,11 +317,10 @@ const formConfig = computed(() => {
       slotName: "productSlot",
       label: "适用产品",
     },
-
     {
       type: "input",
       prop: "remarks",
-      label: "车间说明",
+      label: "工艺说明",
       itemType: "textarea",
     },
   ];
@@ -317,26 +328,54 @@ const formConfig = computed(() => {
 const getList = async (req) => {
   sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
   loading.value = true;
-  proxy.post("/workshop/page", sourceList.value.pagination).then((message) => {
-    console.log(message);
-    sourceList.value.data = message.rows;
-    sourceList.value.pagination.total = message.total;
-    setTimeout(() => {
-      loading.value = false;
-    }, 200);
-  });
+  proxy
+    .post("/technology/page", sourceList.value.pagination)
+    .then((message) => {
+      console.log(message);
+      sourceList.value.data = message.rows;
+      sourceList.value.pagination.total = message.total;
+      setTimeout(() => {
+        loading.value = false;
+      }, 200);
+    });
+};
+const getProcesses = async () => {
+  proxy
+    .post("/productionProcesses/page", { pageNum: 1, pageSize: 9999 })
+    .then((message) => {
+      lineData.value = message.rows.map((x) => ({
+        key: x.id,
+        label: x.name,
+        disabled: false,
+      }));
+    });
 };
 const openModal = () => {
   dialogVisible.value = true;
   modalType.value = "add";
   formData.data = {};
+  selectLine.value = [];
+  productList.value = [];
 };
 
 const submitForm = () => {
-  console.log(byform.value);
   byform.value.handleSubmit((valid) => {
+    if (!selectLine.value.length > 0)
+      return ElMessage({
+        message: "请添加工艺路线",
+        type: "info",
+      });
+    if (!productList.value.length > 0)
+      return ElMessage({
+        message: "请添加适用产品",
+        type: "info",
+      });
     submitLoading.value = true;
-    proxy.post("/workshop/" + modalType.value, formData.data).then(
+    formData.data.processRouteList = selectLine.value; //选择的工序数据
+    formData.data.productList = productList.value.map((x) => ({
+      productId: x.id,
+    })); //选择的产品数据
+    proxy.post("/technology/" + modalType.value, formData.data).then(
       (res) => {
         ElMessage({
           message: modalType.value == "add" ? "添加成功" : "编辑成功",
@@ -344,6 +383,7 @@ const submitForm = () => {
         });
         dialogVisible.value = false;
         submitLoading.value = false;
+
         getList();
       },
       (err) => {
@@ -356,17 +396,14 @@ const submitForm = () => {
 
 const getDtl = (row) => {
   modalType.value = "edit";
-  proxy.post("/workshop/detail", { id: row.id }).then((res) => {
-    res.type = res.type + "";
+  proxy.post("/technology/detail", { id: row.id }).then((res) => {
+    productList.value = res.applicableProductsList;
+    selectLine.value = res.processRouteList.map((x) => x.id);
     formData.data = res;
-    console.log(formData);
     dialogVisible.value = true;
   });
 };
 
-const filterMethod = (query, item) => {
-  return item.initial.toLowerCase().includes(query.toLowerCase());
-};
 const productList = ref([]);
 
 const handleSelect = (row) => {
@@ -391,32 +428,46 @@ const handleRemove = (index) => {
   });
 };
 
-// const renderDom = (h, option) => {
-//   console.log(option, "ass");
-//    h(lineData.map(x=> {
-//     return `<div>${x.x.label}}</div>`
-//   })
-// };
-// getList();
-let dom = ref(null);
-let selectItem = ref({});
+getList();
+getProcesses();
+// 以下是实现拖拽排序的处理方法
 const dragStar = (e, option) => {
-  dom.value = e.target;
-  const target = e.target;
-  selectItem.value = option;
-  e.dataTransfer.setData("text/plain", target.id);
+  // if (!selectLine.value.includes(e.target.id)) return; //拖拽的数据如不是选择的数据直接return
+  e.dataTransfer.setData("text/plain", option.key);
   e.dataTransfer.effectAllowed = "move";
+  e.dataTransfer.dropEffect = "move";
 };
+
 const dragOver = (e, option) => {
   e.preventDefault();
-  const index = selectLine.value.findIndex(
-    (x) => x.key === selectItem.value.key
-  );
-  const index1 = selectLine.value.findIndex((x) => x.key === toRaw(option).key);
-  const item = selectLine.value[index];
-  selectLine.value[index] = selectLine.value[index1];
-  selectLine.value[index1] = item;
-  console.log(selectLine.value);
+};
+
+const handleDrop = (e) => {
+  // if (!selectLine.value.includes(e.target.id)) return; //拖拽的数据如不是选择的数据直接return
+  e.preventDefault();
+  const sourceKey = e.dataTransfer.getData("text/plain"); //获取拖动元素的key值
+  const targetKey = e.target.id; //获取被互换元素的id值
+  swapItems(sourceKey, targetKey);
+};
+
+// const findIndex = (target) => {
+//   while (target && target.parentNode) {
+//     let targetParent = target.parentNode;
+//     const index = Array.from(targetParent.children).indexOf(target);
+//     if (index !== -1) {
+//       return index;
+//     }
+//   }
+//   return -1;
+// };
+
+// 调换数据源位置
+const swapItems = (sourceKey, targetKey) => {
+  const sourceIndex = selectLine.value.findIndex((x) => x === sourceKey);
+  const targetIndex = selectLine.value.findIndex((x) => x === targetKey);
+  const temp = selectLine.value[sourceIndex];
+  selectLine.value[sourceIndex] = selectLine.value[targetIndex];
+  selectLine.value[targetIndex] = temp;
 };
 </script>