Browse Source

集团SKU

lxf 1 year ago
parent
commit
3cfed85951

+ 588 - 0
src/components/makeGroupProduct/index.vue

@@ -0,0 +1,588 @@
+<template>
+  <div style="height: calc(100vh - 114px); overflow-y: auto; overflow-x: hidden">
+    <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
+      <template #skuClassifyId>
+        <div style="width: 100%">
+          <el-cascader
+            v-model="formData.data.skuClassifyId"
+            :options="classifyList"
+            :props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false }"
+            clearable
+            style="width: 100%" />
+        </div>
+      </template>
+      <template #brand>
+        <div style="width: 100%">{{ formData.data.brand }}</div>
+      </template>
+      <template #specification>
+        <div style="width: 100%">
+          <div>
+            <el-icon style="cursor: pointer; transform: translateY(4px); font-size: 24px; margin-left: 10px" @click="addSpecification"><Plus /></el-icon>
+          </div>
+          <el-table :data="formData.data.skuSpecList" :row-style="{ height: '35px' }" header-row-class-name="tableHeader">
+            <el-table-column label="规格图" align="center" width="100">
+              <template #default="{ row, $index }">
+                <el-form-item :prop="'skuSpecList.' + $index + '.specImgUrl'">
+                  <el-upload
+                    class="avatar-uploader"
+                    action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+                    :data="uploadData"
+                    :show-file-list="false"
+                    :on-success="
+                      (response, uploadFile) => {
+                        return handleSuccess(uploadFile, $index);
+                      }
+                    "
+                    :before-upload="uploadFile">
+                    <el-image v-if="row.specImgUrl" :src="row.specImgUrl" fit="scale-down" class="avatar" />
+                    <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
+                  </el-upload>
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column label="设计图" align="center" width="100">
+              <template #default="{ row, $index }">
+                <el-form-item :prop="'skuSpecList.' + $index + '.designImgUrl'">
+                  <el-upload
+                    class="avatar-uploader"
+                    action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+                    :data="uploadDesignData"
+                    :show-file-list="false"
+                    :on-success="
+                      (response, uploadFile) => {
+                        return handleDesignSuccess(uploadFile, $index);
+                      }
+                    "
+                    :before-upload="uploadDesignFile">
+                    <el-image v-if="row.designImgUrl" :src="row.designImgUrl" fit="scale-down" class="avatar" />
+                    <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
+                  </el-upload>
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column label="品名" min-width="220">
+              <template #default="{ row, $index }">
+                <el-form-item :prop="'skuSpecList.' + $index + '.name'" :rules="rulesSpec.name" :inline-message="true" style="width: 100%">
+                  <el-input v-model="row.name" placeholder="请输入品名" />
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column label="品号" width="160">
+              <template #default="{ row, $index }">
+                <el-form-item :prop="'skuSpecList.' + $index + '.code'" :rules="rulesSpec.code" :inline-message="true" style="width: 100%">
+                  <el-input v-model="row.code" placeholder="请输入品号" />
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column label="加工版面" width="140">
+              <template #default="{ row, $index }">
+                <el-form-item :prop="'skuSpecList.' + $index + '.machinedPanel'" style="width: 100%">
+                  <el-select v-model="row.machinedPanel" placeholder="加工版面" clearable>
+                    <el-option v-for="item in useUserStore().allDict['processing_layout']" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" />
+                  </el-select>
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column label="尺寸(长宽高,cm)" align="center" width="300">
+              <template #default="{ row, $index }">
+                <el-row>
+                  <el-col :span="8">
+                    <el-form-item :prop="'skuSpecList.' + $index + '.length'" :rules="rulesSpec.length" :inline-message="true" style="width: 100%">
+                      <el-input-number
+                        onmousewheel="return false;"
+                        v-model="row.length"
+                        placeholder="长"
+                        style="width: 100%"
+                        :controls="false"
+                        :min="0"
+                        :precision="2" />
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="8">
+                    <el-form-item :prop="'skuSpecList.' + $index + '.width'" :rules="rulesSpec.width" :inline-message="true" style="width: 100%">
+                      <el-input-number
+                        onmousewheel="return false;"
+                        v-model="row.width"
+                        placeholder="宽"
+                        style="width: 100%"
+                        :controls="false"
+                        :min="0"
+                        :precision="2" />
+                    </el-form-item>
+                  </el-col>
+                  <el-col :span="8">
+                    <el-form-item :prop="'skuSpecList.' + $index + '.height'" :rules="rulesSpec.height" :inline-message="true" style="width: 100%">
+                      <el-input-number
+                        onmousewheel="return false;"
+                        v-model="row.height"
+                        placeholder="高"
+                        style="width: 100%"
+                        :controls="false"
+                        :min="0"
+                        :precision="2" />
+                    </el-form-item>
+                  </el-col>
+                </el-row>
+              </template>
+            </el-table-column>
+            <el-table-column label="净重(g)" width="120">
+              <template #default="{ row, $index }">
+                <el-form-item :prop="'skuSpecList.' + $index + '.netWeight'" :rules="rulesSpec.netWeight" :inline-message="true" style="width: 100%">
+                  <el-input-number
+                    onmousewheel="return false;"
+                    v-model="row.netWeight"
+                    placeholder="净重"
+                    style="width: 100%"
+                    :controls="false"
+                    :min="0"
+                    :precision="2" />
+                </el-form-item>
+              </template>
+            </el-table-column>
+            <el-table-column label="BOM" align="left" width="250">
+              <template #default="{ row, $index }">
+                <el-button
+                  v-if="!row.bomSpecId"
+                  type="primary"
+                  size="small"
+                  @click="handleOpen($index)"
+                  style="margin: 5px 0; background-color: #43b214; border-color: #43b214">
+                  选择BOM
+                </el-button>
+                <div style="width: 100%; display: flex; align-items: center" v-else>
+                  <el-icon style="font-size: 16px; cursor: pointer" @click="clickRemoveBOM($index)"><Remove /></el-icon>
+                  <span>{{ row.bomSpecName }}</span>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" width="60">
+              <template #default="{ $index }">
+                <el-button type="primary" @click="clickDelete($index)" text>删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </template>
+      <template #mainImgUrl>
+        <div style="width: 100%">
+          <div style="color: #aaaaaa">图片不超过5M,支持JPEG、JPG、PNG格式;</div>
+          <div style="color: #aaaaaa">建议主图大于640*640,主题鲜明、图片清晰、提升买家满意度;</div>
+          <el-upload
+            class="avatar-uploader-main"
+            action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+            :data="uploadMainData"
+            :show-file-list="false"
+            :on-success="handleMainSuccess"
+            :before-upload="uploadMainFile">
+            <el-image v-if="formData.data.mainImgUrl" :src="formData.data.mainImgUrl" fit="scale-down" class="avatar" />
+            <el-icon v-else class="avatar-uploader-main-icon"><Plus /></el-icon>
+          </el-upload>
+        </div>
+      </template>
+      <template #detailText>
+        <div style="width: 100%">
+          <div v-if="props.detailStatus">
+            <div v-html="getStyle(formData.data.detailText)"></div>
+          </div>
+          <Editor v-else :value="formData.data.detailText" @updateValue="updateValue" ref="editor" />
+        </div>
+      </template>
+    </byForm>
+    <div style="width: 100%; text-align: center; margin: 10px">
+      <el-button v-if="props.detailStatus" @click="clickCancel()" size="large">关 闭</el-button>
+      <el-button v-if="!props.detailStatus" @click="clickCancel()" size="large">取 消</el-button>
+      <el-button type="primary" @click="submitForm()" v-if="!props.detailStatus" :disabled="btnDisabled" size="large">确 定</el-button>
+    </div>
+
+    <el-dialog title="选择BOM" v-if="openBOM" v-model="openBOM" width="84%">
+      <SelectBOM :selectStatus="true" :bomClassifyId="1" @selectBOM="selectBOM"></SelectBOM>
+      <template #footer>
+        <el-button @click="openBOM = false" size="large">关 闭</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import byForm from "@/components/byForm/index";
+import { ElMessage } from "element-plus";
+import Editor from "@/components/Editor/index.vue";
+import SelectBOM from "@/views/group/BOM/management/index";
+
+const { proxy } = getCurrentInstance();
+const emit = defineEmits(["clickCancel"]);
+const submit = ref(null);
+const formOption = reactive({
+  inline: true,
+  labelWidth: "120px",
+  itemWidth: 100,
+  rules: [],
+  labelPosition: "right",
+});
+const formData = reactive({
+  data: {
+    brand: "胜德科技",
+    type: 1,
+    source: 1,
+    craftProductionLineId: "1",
+    mainImgUrl: "",
+    detailText: "",
+    skuSpecList: [
+      {
+        specImgUrl: "",
+        designImgUrl: "",
+        name: "",
+        code: "",
+        barCode: "",
+        machinedPanel: "",
+        length: undefined,
+        width: undefined,
+        height: undefined,
+        netWeight: undefined,
+        sharedFolder: "",
+        bomSpecId: "",
+        remark: "",
+        packagingMaterialList: [],
+        expressPackingList: [],
+      },
+    ],
+  },
+});
+const formConfig = computed(() => {
+  return [
+    {
+      type: "title",
+      title: "基本信息",
+      label: "",
+    },
+    {
+      type: "slot",
+      prop: "skuClassifyId",
+      slotName: "skuClassifyId",
+      label: "类目",
+      itemWidth: 50,
+    },
+    {
+      type: "input",
+      prop: "barCode",
+      label: "条码",
+      itemType: "text",
+      itemWidth: 50,
+    },
+    {
+      type: "input",
+      prop: "groupItemNumber",
+      label: "群组品号",
+      itemType: "text",
+      itemWidth: 50,
+    },
+    {
+      type: "input",
+      prop: "code",
+      label: "编码",
+      itemType: "text",
+      itemWidth: 50,
+    },
+    {
+      type: "input",
+      prop: "name",
+      label: "名称",
+      itemType: "text",
+    },
+    {
+      type: "title",
+      title: "材质特征",
+      label: "",
+    },
+    {
+      type: "slot",
+      slotName: "brand",
+      label: "品牌",
+    },
+    {
+      type: "input",
+      prop: "modelNumber",
+      label: "型号",
+      itemType: "text",
+      itemWidth: 50,
+    },
+    {
+      type: "input",
+      prop: "material",
+      label: "材质",
+      itemType: "text",
+      itemWidth: 50,
+    },
+    {
+      type: "slot",
+      slotName: "specification",
+      label: "规格",
+    },
+    {
+      type: "title",
+      title: "工艺路线",
+      label: "",
+    },
+    {
+      type: "select",
+      label: "工艺路线",
+      prop: "craftProductionLineId",
+      data: [],
+      itemWidth: 50,
+    },
+    {
+      type: "title",
+      title: "图片介绍",
+      label: "",
+    },
+    {
+      type: "slot",
+      slotName: "mainImgUrl",
+      label: "主图",
+    },
+    {
+      type: "slot",
+      slotName: "detailText",
+      label: "详情描述",
+    },
+  ];
+});
+const rules = ref({
+  groupItemNumber: [{ required: true, message: "请输入群组品号", trigger: "blur" }],
+  code: [{ required: true, message: "请输入编码", trigger: "blur" }],
+  name: [{ required: true, message: "请输入名称", trigger: "blur" }],
+});
+const rulesSpec = ref({
+  code: [{ required: true, message: "请输入品号", trigger: "blur" }],
+  name: [{ required: true, message: "请输入品名", trigger: "blur" }],
+  length: [{ required: true, message: "请输入长", trigger: "blur" }],
+  width: [{ required: true, message: "请输入宽", trigger: "blur" }],
+  height: [{ required: true, message: "请输入高", trigger: "blur" }],
+  netWeight: [{ required: true, message: "请输入净重", trigger: "blur" }],
+});
+const classifyList = ref([]);
+const getBomClassify = () => {
+  proxy.post("/skuClassify/tree", {}).then((res) => {
+    classifyList.value = res;
+  });
+};
+getBomClassify();
+const addSpecification = () => {
+  if (!props.detailStatus) {
+    if (formData.data.skuSpecList && formData.data.skuSpecList.length > 0) {
+      formData.data.skuSpecList.push({
+        specImgUrl: "",
+        designImgUrl: "",
+        name: "",
+        code: "",
+        barCode: "",
+        machinedPanel: "",
+        length: undefined,
+        width: undefined,
+        height: undefined,
+        netWeight: undefined,
+        sharedFolder: "",
+        bomSpecId: "",
+        remark: "",
+        packagingMaterialList: [],
+        expressPackingList: [],
+      });
+    } else {
+      formData.data.skuSpecList = [
+        {
+          specImgUrl: "",
+          designImgUrl: "",
+          name: "",
+          code: "",
+          barCode: "",
+          machinedPanel: "",
+          length: undefined,
+          width: undefined,
+          height: undefined,
+          netWeight: undefined,
+          sharedFolder: "",
+          bomSpecId: "",
+          remark: "",
+          packagingMaterialList: [],
+          expressPackingList: [],
+        },
+      ];
+    }
+  }
+};
+const uploadData = ref({});
+const uploadFile = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadData.value = res.uploadBody;
+  file.id = res.id;
+  file.fileName = res.fileName;
+  file.fileUrl = res.fileUrl;
+  return true;
+};
+const handleSuccess = (uploadFile, index) => {
+  formData.data.skuSpecList[index].specImgUrl = uploadFile.raw.fileUrl;
+};
+const uploadDesignData = ref({});
+const uploadDesignFile = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadDesignData.value = res.uploadBody;
+  file.id = res.id;
+  file.fileName = res.fileName;
+  file.fileUrl = res.fileUrl;
+  return true;
+};
+const handleDesignSuccess = (uploadFile, index) => {
+  formData.data.skuSpecList[index].designImgUrl = uploadFile.raw.fileUrl;
+};
+const clickDelete = (index) => {
+  formData.data.skuSpecList.splice(index, 1);
+};
+const editor = ref(null);
+const updateValue = (val) => {
+  formData.data.detailText = val;
+};
+const uploadMainData = ref({});
+const uploadMainFile = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadMainData.value = res.uploadBody;
+  file.id = res.id;
+  file.fileName = res.fileName;
+  file.fileUrl = res.fileUrl;
+  return true;
+};
+const handleMainSuccess = (response, uploadFile) => {
+  formData.data.mainImgUrl = uploadFile.raw.fileUrl;
+};
+const btnDisabled = ref(false);
+const submitForm = () => {
+  submit.value.handleSubmit(() => {
+    if (formData.data.skuSpecList && formData.data.skuSpecList.length > 0) {
+      let type = "add";
+      if (formData.data.id) {
+        type = "edit";
+      }
+      btnDisabled.value = true;
+      proxy.post("/sku/" + type, formData.data).then(
+        () => {
+          ElMessage({
+            message: type == "add" ? "添加成功" : "编辑成功",
+            type: "success",
+          });
+          emit("clickCancel", true);
+        },
+        (err) => {
+          console.log(err);
+          btnDisabled.value = false;
+        }
+      );
+    } else {
+      return ElMessage("请添加规格");
+    }
+  });
+};
+const clickCancel = () => {
+  emit("clickCancel", false);
+};
+const props = defineProps({
+  rowData: Object,
+  detailStatus: Boolean,
+});
+onMounted(() => {
+  formOption.disabled = props.detailStatus;
+  if (props.rowData && props.rowData.id) {
+    proxy.post("/sku/detail", { id: props.rowData.id }).then((res) => {
+      for (var text in res) {
+        formData.data[text] = res[text];
+      }
+      if (!props.detailStatus) {
+        editor.value.changeHtml(formData.data.detailText);
+      }
+    });
+  }
+});
+const getStyle = (text) => {
+  if (text) {
+    return text.replace(/\n|\r\n/g, "<br>");
+  } else {
+    return "";
+  }
+};
+const rowIndex = ref(null);
+const openBOM = ref(false);
+const handleOpen = (index) => {
+  rowIndex.value = index;
+  openBOM.value = true;
+};
+const selectBOM = (item) => {
+  if (item.id) {
+    formData.data.skuSpecList[rowIndex.value].bomSpecId = item.id;
+    formData.data.skuSpecList[rowIndex.value].bomSpecName = item.name;
+    ElMessage({ message: "选择完成", type: "success" });
+    openBOM.value = false;
+  }
+};
+const clickRemoveBOM = (index) => {
+  formData.data.skuSpecList[index].bomSpecId = "";
+  formData.data.skuSpecList[index].bomSpecName = "";
+};
+</script>
+
+<style lang="scss" scoped>
+.avatar-uploader .avatar {
+  width: 50px;
+  height: 50px;
+  display: block;
+  background-color: black;
+}
+.avatar-uploader .el-upload {
+  border: 1px dashed var(--el-border-color);
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  transition: var(--el-transition-duration-fast);
+}
+.avatar-uploader .el-upload:hover {
+  border-color: var(--el-color-primary);
+}
+.el-icon.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 50px;
+  height: 50px;
+  text-align: center;
+  border: 1px dashed var(--el-border-color);
+}
+::v-deep(.el-input-number .el-input__inner) {
+  text-align: left;
+}
+.avatar-uploader-main .avatar {
+  width: 148px;
+  height: 148px;
+  display: block;
+  background-color: black;
+}
+.avatar-uploader-main .el-upload {
+  border: 1px dashed var(--el-border-color);
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  transition: var(--el-transition-duration-fast);
+}
+.avatar-uploader-main .el-upload:hover {
+  border-color: var(--el-color-primary);
+}
+.el-icon.avatar-uploader-main-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 148px;
+  height: 148px;
+  text-align: center;
+  border: 1px dashed var(--el-border-color);
+}
+:deep(.el-dialog) {
+  margin-top: 10px !important;
+  margin-bottom: 10px !important;
+}
+</style>

+ 1 - 5
src/components/makeProduct/index.vue

@@ -11,14 +11,13 @@
             style="width: 100%" />
         </div>
       </template>
-
       <template #specification>
         <div style="width: 100%">
           <div>
             <el-icon style="cursor: pointer; transform: translateY(4px); font-size: 24px; margin-left: 10px" @click="addSpecification"><Plus /></el-icon>
           </div>
           <el-table :data="formData.data.skuSpecList" :row-style="{ height: '35px' }" header-row-class-name="tableHeader">
-            <el-table-column label="图" align="center" width="100">
+            <el-table-column label="规格图" align="center" width="100">
               <template #default="{ row, $index }">
                 <el-form-item :prop="'skuSpecList.' + $index + '.specImgUrl'">
                   <el-upload
@@ -210,7 +209,6 @@ const formData = reactive({
         designImgUrl: "",
         name: "",
         code: "",
-        designImgUrl: "",
         barCode: "",
         machinedPanel: "",
         length: undefined,
@@ -355,7 +353,6 @@ const addSpecification = () => {
         designImgUrl: "",
         name: "",
         code: "",
-        designImgUrl: "",
         barCode: "",
         machinedPanel: "",
         length: undefined,
@@ -372,7 +369,6 @@ const addSpecification = () => {
       formData.data.skuSpecList = [
         {
           specImgUrl: "",
-          designImgUrl: "",
           name: "",
           code: "",
           designImgUrl: "",

+ 1 - 1
src/views/group/BOM/management/index.vue

@@ -71,7 +71,7 @@
                     {{ `${spec.length}  *  ${spec.width}  *  ${spec.height}(cm³)` }}
                   </div>
                   <div style="width: 60px; text-align: center" v-if="props.selectStatus">
-                    <el-button type="text" @click="selectBOM(row)" :disabled="selectBtnStatus" v-db-click>选择</el-button>
+                    <el-button type="text" @click="selectBOM(spec)" :disabled="selectBtnStatus" v-db-click>选择</el-button>
                   </div>
                 </div>
               </div>

+ 231 - 39
src/views/group/order/management/index.vue

@@ -1,53 +1,84 @@
 <template>
-  <el-card class="box-card">
-    <byTable
-      :source="sourceList.data"
-      :pagination="sourceList.pagination"
-      :config="config"
-      :loading="loading"
-      :selectConfig="selectConfig"
-      highlight-current-row
-      @get-list="getList">
-      <template #amount="{ item }">
-        <div>
-          <span style="padding-right: 4px">{{ item.currency }}</span>
-          <span>{{ moneyFormat(item.amount, 2) }}</span>
-        </div>
+  <div>
+    <el-card class="box-card">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        :searchConfig="searchConfig"
+        highlight-current-row
+        :action-list="[
+          {
+            text: '添加产品',
+            action: () => clickModal(),
+          },
+          // {
+          //   text: '操作日志',
+          //   action: () => viewLogs(),
+          // },
+        ]"
+        @get-list="getList"
+        @clickReset="clickReset">
+        <template #name="{ item }">
+          <div>
+            <a style="color: #409eff; cursor: pointer; word-break: break-all" @click="clickName(item)">{{ item.name }}</a>
+          </div>
+        </template>
+      </byTable>
+    </el-card>
+
+    <el-dialog :title="modalTitle" v-if="openDialog" v-model="openDialog" width="90%">
+      <MakeSKU :rowData="rowData" :detailStatus="detailStatus" @clickCancel="clickCancel"></MakeSKU>
+    </el-dialog>
+
+    <el-dialog title="操作日志" v-if="openLogs" v-model="openLogs" width="50%">
+      <byTable
+        :source="logsList.data"
+        :pagination="logsList.pagination"
+        :config="configLogs"
+        :loading="loadingLogs"
+        highlight-current-row
+        @get-list="getLogsList">
+      </byTable>
+      <template #footer>
+        <el-button @click="openLogs = false" size="large">关 闭</el-button>
       </template>
-    </byTable>
-  </el-card>
+    </el-dialog>
+  </div>
 </template>
 
 <script setup>
 import byTable from "@/components/byTable/index";
+import MakeSKU from "@/components/makeProduct/index";
 
 const { proxy } = getCurrentInstance();
 const sourceList = ref({
   data: [],
   pagination: {
-    total: 1000,
+    total: 0,
     pageNum: 1,
     pageSize: 10,
-    name: "",
   },
 });
 const loading = ref(false);
-const selectConfig = computed(() => {
+const searchConfig = computed(() => {
   return [
     {
-      label: "质检状态",
-      prop: "status",
-      data: [],
+      type: "input",
+      prop: "name",
+      label: "品名",
     },
     {
-      label: "订单类型",
-      prop: "payStatus",
-      data: [],
+      type: "input",
+      prop: "code",
+      label: "品号",
     },
     {
-      label: "结论",
-      prop: "payStatus",
-      data: [],
+      type: "select",
+      prop: "brand",
+      dictKey: "sku_brand",
+      label: "品牌",
     },
   ];
 });
@@ -55,35 +86,88 @@ const config = computed(() => {
   return [
     {
       attrs: {
-        label: "分类名称",
-        prop: "name",
-        "min-width": 180,
+        label: "群组品号",
+        prop: "code",
+        width: 180,
       },
     },
     {
       attrs: {
-        label: "排序",
-        prop: "sort",
-        width: 100,
+        label: "群组品名",
+        slot: "name",
+        "min-width": 240,
+      },
+    },
+    {
+      attrs: {
+        label: "产品来源",
+        prop: "source",
+        width: 120,
+      },
+      render(val) {
+        return val == 1 ? "MES" : "万里牛";
+      },
+    },
+    {
+      attrs: {
+        label: "品牌",
+        prop: "brand",
+        width: 120,
+      },
+      render(val) {
+        return proxy.dictKeyValue(val, proxy.useUserStore().allDict["sku_brand"]);
+      },
+    },
+    {
+      attrs: {
+        label: "型号",
+        prop: "modelNumber",
+        width: 140,
+      },
+    },
+    {
+      attrs: {
+        label: "材质",
+        prop: "material",
+        width: 140,
       },
     },
     {
       attrs: {
         label: "操作",
-        width: 160,
+        width: 80,
         align: "center",
         fixed: "right",
       },
       renderHTML(row) {
-        return [];
+        return [
+          {
+            attrs: {
+              label: "编辑",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              clickUpdate(row);
+            },
+          },
+        ];
       },
     },
   ];
 });
-const getList = async (req) => {
-  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+const getList = async (req, status) => {
+  if (status) {
+    sourceList.value.pagination = {
+      pageNum: sourceList.value.pagination.pageNum,
+      pageSize: sourceList.value.pagination.pageSize,
+    };
+  } else {
+    sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  }
   loading.value = true;
-  proxy.post("/bomClassify/tree", sourceList.value.pagination).then((res) => {
+  proxy.post("/orderInfo/page", sourceList.value.pagination).then((res) => {
     sourceList.value.data = res.rows;
     sourceList.value.pagination.total = res.total;
     setTimeout(() => {
@@ -92,7 +176,115 @@ const getList = async (req) => {
   });
 };
 getList();
+const clickReset = () => {
+  treeCategory.value.setCurrentKey(null);
+  getList("", true);
+};
+const modalTitle = ref("添加SKU");
+const openDialog = ref(false);
+const rowData = ref({});
+const detailStatus = ref(false);
+const clickModal = () => {
+  modalTitle.value = "添加SKU";
+  rowData.value = {};
+  detailStatus.value = false;
+  openDialog.value = true;
+};
+const clickUpdate = (row) => {
+  modalTitle.value = "编辑SKU";
+  rowData.value = row;
+  detailStatus.value = false;
+  openDialog.value = true;
+};
+const clickCancel = (status) => {
+  openDialog.value = false;
+  if (status) {
+    getList();
+  }
+};
+const openFile = (path) => {
+  window.open(path);
+};
+const clickName = (row) => {
+  modalTitle.value = "SKU详情";
+  rowData.value = row;
+  detailStatus.value = true;
+  openDialog.value = true;
+};
+const openLogs = ref(false);
+const loadingLogs = ref(false);
+const logsList = ref({
+  data: [],
+  pagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+});
+const type = ref([
+  { dictKey: "1", dictValue: "新增" },
+  { dictKey: "2", dictValue: "修改" },
+  { dictKey: "3", dictValue: "删除" },
+]);
+const configLogs = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "操作时间",
+        prop: "createTime",
+        width: 160,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "操作人",
+        prop: "operator",
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "SKU品号",
+        prop: "code",
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "行为",
+        prop: "type",
+        width: 100,
+        align: "center",
+      },
+      render(val) {
+        return proxy.dictKeyValue(val, type.value);
+      },
+    },
+  ];
+});
+const viewLogs = () => {
+  logsList.value.data = [];
+  logsList.value.pagination.total = 0;
+  openLogs.value = true;
+  getLogsList({ pageNum: 1, pageSize: 10 });
+};
+const getLogsList = async (req) => {
+  logsList.value.pagination = { ...logsList.value.pagination, ...req };
+  loadingLogs.value = true;
+  proxy.post("/bomOperatingLog/page", logsList.value.pagination).then((res) => {
+    logsList.value.data = res.rows;
+    logsList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loadingLogs.value = false;
+    }, 200);
+  });
+};
 </script>
 
 <style lang="scss" scoped>
+:deep(.el-dialog) {
+  margin-top: 10px !important;
+  margin-bottom: 10px !important;
+}
 </style>

+ 340 - 0
src/views/group/product/groupSKU/index.vue

@@ -0,0 +1,340 @@
+<template>
+  <div>
+    <el-card class="box-card">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        :searchConfig="searchConfig"
+        highlight-current-row
+        :action-list="[
+          {
+            text: '添加产品',
+            action: () => clickModal(),
+          },
+          // {
+          //   text: '操作日志',
+          //   action: () => viewLogs(),
+          // },
+        ]"
+        @get-list="getList"
+        @clickReset="clickReset">
+        <template #typeExpand="{ item }">
+          <div style="padding: 0px 20px; box-sizing: border-box" v-if="item.skuSpecList && item.skuSpecList.length > 0">
+            <div
+              v-for="spec in item.skuSpecList"
+              :key="spec.id"
+              style="display: flex; padding: 8px 16px; align-items: center; box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1); margin-bottom: 8px">
+              <div style="width: 80px">
+                <div v-if="spec.specImgUrl">
+                  <img
+                    style="width: 40px; height: 40px; object-fit: contain; vertical-align: middle; border: none; cursor: pointer"
+                    :src="spec.specImgUrl"
+                    @click="openFile(spec.specImgUrl)" />
+                </div>
+                <div
+                  v-else
+                  class="el-icon-picture-outline"
+                  style="width: 40px; height: 40px; font-size: 36px; line-height: 40px; text-align: center; color: rgb(229 228 228)"></div>
+              </div>
+              <div style="width: 180px">
+                {{ spec.code }}
+              </div>
+              <div style="flex: 1">
+                {{ spec.name }}
+              </div>
+              <div style="width: 180px">
+                {{ `${spec.length}  *  ${spec.width}  *  ${spec.height}(cm³)` }}
+              </div>
+              <!-- <div style="width: 60px; text-align: center" v-if="selectStatus">
+                <el-button type="text" @click="handleSelect(scope.row, index)" v-db-click>选择</el-button>
+              </div> -->
+            </div>
+          </div>
+        </template>
+        <template #name="{ item }">
+          <div>
+            <a style="color: #409eff; cursor: pointer; word-break: break-all" @click="clickName(item)">{{ item.name }}</a>
+          </div>
+        </template>
+      </byTable>
+    </el-card>
+
+    <el-dialog :title="modalTitle" v-if="openDialog" v-model="openDialog" width="90%">
+      <MakeSKU :rowData="rowData" :detailStatus="detailStatus" @clickCancel="clickCancel"></MakeSKU>
+    </el-dialog>
+
+    <el-dialog title="操作日志" v-if="openLogs" v-model="openLogs" width="50%">
+      <byTable
+        :source="logsList.data"
+        :pagination="logsList.pagination"
+        :config="configLogs"
+        :loading="loadingLogs"
+        highlight-current-row
+        @get-list="getLogsList">
+      </byTable>
+      <template #footer>
+        <el-button @click="openLogs = false" size="large">关 闭</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import byTable from "@/components/byTable/index";
+import MakeSKU from "@/components/makeGroupProduct/index";
+import { ElMessage, ElMessageBox } from "element-plus";
+
+const { proxy } = getCurrentInstance();
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    name: "",
+    code: "",
+    type: 1,
+    brand: "",
+  },
+});
+const loading = ref(false);
+const searchConfig = computed(() => {
+  return [
+    {
+      type: "input",
+      prop: "name",
+      label: "品名",
+    },
+    {
+      type: "input",
+      prop: "code",
+      label: "品号",
+    },
+  ];
+});
+const config = computed(() => {
+  return [
+    {
+      type: "expand",
+      attrs: {
+        label: " ",
+        slot: "typeExpand",
+        width: 50,
+      },
+    },
+    {
+      attrs: {
+        label: "群组品号",
+        prop: "code",
+        width: 180,
+      },
+    },
+    {
+      attrs: {
+        label: "群组品名",
+        slot: "name",
+        "min-width": 240,
+      },
+    },
+    {
+      attrs: {
+        label: "品牌",
+        prop: "brand",
+        width: 120,
+      },
+    },
+    {
+      attrs: {
+        label: "型号",
+        prop: "modelNumber",
+        width: 200,
+      },
+    },
+    {
+      attrs: {
+        label: "材质",
+        prop: "material",
+        width: 200,
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: 120,
+        align: "center",
+        fixed: "right",
+      },
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "编辑",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              clickUpdate(row);
+            },
+          },
+          {
+            attrs: {
+              label: "删除",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              clickDelete(row);
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+const getList = async (req, status) => {
+  if (status) {
+    sourceList.value.pagination = {
+      pageNum: sourceList.value.pagination.pageNum,
+      pageSize: sourceList.value.pagination.pageSize,
+    };
+  } else {
+    sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  }
+  loading.value = true;
+  proxy.post("/sku/page", sourceList.value.pagination).then((res) => {
+    sourceList.value.data = res.rows;
+    sourceList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loading.value = false;
+    }, 200);
+  });
+};
+getList();
+const clickReset = () => {
+  treeCategory.value.setCurrentKey(null);
+  getList("", true);
+};
+const modalTitle = ref("添加SKU");
+const openDialog = ref(false);
+const rowData = ref({});
+const detailStatus = ref(false);
+const clickModal = () => {
+  modalTitle.value = "添加SKU";
+  rowData.value = {};
+  detailStatus.value = false;
+  openDialog.value = true;
+};
+const clickUpdate = (row) => {
+  modalTitle.value = "编辑SKU";
+  rowData.value = row;
+  detailStatus.value = false;
+  openDialog.value = true;
+};
+const clickDelete = (row) => {
+  ElMessageBox.confirm("你是否确认此操作", "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(() => {
+    proxy.post("/sku/delete", { id: row.id }).then(() => {
+      ElMessage({ message: "删除成功", type: "success" });
+      getList();
+    });
+  });
+};
+const clickCancel = (status) => {
+  openDialog.value = false;
+  if (status) {
+    getList();
+  }
+};
+const openFile = (path) => {
+  window.open(path);
+};
+const clickName = (row) => {
+  modalTitle.value = "SKU详情";
+  rowData.value = row;
+  detailStatus.value = true;
+  openDialog.value = true;
+};
+const openLogs = ref(false);
+const loadingLogs = ref(false);
+const logsList = ref({
+  data: [],
+  pagination: {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+  },
+});
+const type = ref([
+  { dictKey: "1", dictValue: "新增" },
+  { dictKey: "2", dictValue: "修改" },
+  { dictKey: "3", dictValue: "删除" },
+]);
+const configLogs = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "操作时间",
+        prop: "createTime",
+        width: 160,
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "操作人",
+        prop: "operator",
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "SKU品号",
+        prop: "code",
+        align: "center",
+      },
+    },
+    {
+      attrs: {
+        label: "行为",
+        prop: "type",
+        width: 100,
+        align: "center",
+      },
+      render(val) {
+        return proxy.dictKeyValue(val, type.value);
+      },
+    },
+  ];
+});
+const viewLogs = () => {
+  logsList.value.data = [];
+  logsList.value.pagination.total = 0;
+  openLogs.value = true;
+  getLogsList({ pageNum: 1, pageSize: 10 });
+};
+const getLogsList = async (req) => {
+  logsList.value.pagination = { ...logsList.value.pagination, ...req };
+  loadingLogs.value = true;
+  proxy.post("/bomOperatingLog/page", logsList.value.pagination).then((res) => {
+    logsList.value.data = res.rows;
+    logsList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loadingLogs.value = false;
+    }, 200);
+  });
+};
+</script>
+
+<style lang="scss" scoped>
+:deep(.el-dialog) {
+  margin-top: 10px !important;
+  margin-bottom: 10px !important;
+}
+</style>

+ 2 - 2
src/views/group/product/management/index.vue

@@ -167,14 +167,14 @@ const config = computed(() => {
       attrs: {
         label: "型号",
         prop: "modelNumber",
-        width: 140,
+        width: 200,
       },
     },
     {
       attrs: {
         label: "材质",
         prop: "material",
-        width: 140,
+        width: 200,
       },
     },
     {