From 8a72d17f6cf36376017f51a8039a4ffc90bff885 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期一, 09 三月 2026 17:54:54 +0800
Subject: [PATCH] 主生产计划页面部分

---
 src/views/productionPlan/productionPlan/components/PIMTable.vue |  462 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 462 insertions(+), 0 deletions(-)

diff --git a/src/views/productionPlan/productionPlan/components/PIMTable.vue b/src/views/productionPlan/productionPlan/components/PIMTable.vue
new file mode 100644
index 0000000..c0f7616
--- /dev/null
+++ b/src/views/productionPlan/productionPlan/components/PIMTable.vue
@@ -0,0 +1,462 @@
+<template>
+  <el-table ref="multipleTable"
+            v-loading="tableLoading"
+            :border="border"
+            :data="tableData"
+            :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+            :height="height"
+            :highlight-current-row="highlightCurrentRow"
+            :row-class-name="rowClassName"
+            :row-style="rowStyle"
+            :row-key="rowKey"
+            :style="tableStyle"
+            tooltip-effect="dark"
+            :expand-row-keys="expandRowKeys"
+            :show-summary="isShowSummary"
+            :summary-method="summaryMethod"
+            @row-click="rowClick"
+            @current-change="currentChange"
+            @selection-change="handleSelectionChange"
+            @expand-change="expandChange"
+            @select-all="handleSelectAll"
+            class="lims-table">
+    <el-table-column align="center"
+                     type="selection"
+                     width="55"
+                     v-if="isSelection"
+                     :selectable="selectable" />
+    <el-table-column align="center"
+                     label="搴忓彿"
+                     type="index"
+                     width="60" />
+    <el-table-column v-for="(item, index) in column"
+                     :key="index"
+                     :column-key="item.columnKey"
+                     :filter-method="item.filterHandler"
+                     :filter-multiple="item.filterMultiple"
+                     :filtered-value="item.filteredValue"
+                     :filters="item.filters"
+                     :fixed="item.fixed"
+                     :label="item.label"
+                     :prop="item.prop"
+                     :show-overflow-tooltip="item.dataType !== 'action' && item.dataType !== 'slot'"
+                     :align="item.align"
+                     :sortable="!!item.sortable"
+                     :type="item.type"
+                     :width="item.width">
+      <template #header="scope">
+        <div class="pim-table-header-cell">
+          <div class="pim-table-header-title">
+            {{ item.label }}
+          </div>
+          <div v-if="item.headerSlot"
+               class="pim-table-header-extra">
+            <slot :name="item.headerSlot"
+                  :column="scope.column" />
+          </div>
+        </div>
+      </template>
+      <template v-if="item.hasOwnProperty('colunmTemplate')"
+                #[item.colunmTemplate]="scope">
+        <slot v-if="item.theadSlot"
+              :name="item.theadSlot"
+              :index="scope.$index"
+              :row="scope.row" />
+      </template>
+      <template #default="scope">
+        <!-- 鎻掓Ы -->
+        <div v-if="item.dataType == 'slot'">
+          <slot v-if="item.slot"
+                :index="scope.$index"
+                :name="item.slot"
+                :row="scope.row" />
+        </div>
+        <!-- 杩涘害鏉� -->
+        <div v-else-if="item.dataType == 'progress'">
+          <el-progress :percentage="Number(scope.row[item.prop])" />
+        </div>
+        <!-- 鍥剧墖 -->
+        <div v-else-if="item.dataType == 'image'">
+          <img :src="javaApi + '/img/' + scope.row[item.prop]"
+               alt=""
+               style="width: 40px; height: 40px; margin-top: 10px" />
+        </div>
+        <!-- tag -->
+        <div v-else-if="item.dataType == 'tag'">
+          <el-tag v-if="
+              typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
+              'string'
+            "
+                  :title="formatters(scope.row[item.prop], item.formatData)"
+                  :type="formatType(scope.row[item.prop], item.formatType)">
+            {{ formatters(scope.row[item.prop], item.formatData) }}
+          </el-tag>
+          <el-tag v-for="(tag, index) in dataTypeFn(
+              scope.row[item.prop],
+              item.formatData
+            )"
+                  v-else-if="
+              typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
+              'object'
+            "
+                  :key="index"
+                  :title="formatters(scope.row[item.prop], item.formatData)"
+                  :type="formatType(tag, item.formatType)">
+            {{ item.tagGroup ? tag[item.tagGroup.label] ?? tag : tag }}
+          </el-tag>
+          <el-tag v-else
+                  :title="formatters(scope.row[item.prop], item.formatData)"
+                  :type="formatType(scope.row[item.prop], item.formatType)">
+            {{ formatters(scope.row[item.prop], item.formatData) }}
+          </el-tag>
+        </div>
+        <!-- 鎸夐挳 -->
+        <div v-else-if="item.dataType == 'action'"
+             @click.stop>
+          <template v-for="(o, key) in item.operation"
+                    :key="key">
+            <el-button v-show="o.type != 'upload'"
+                       v-if="o.showHide ? o.showHide(scope.row) : true"
+                       :disabled="o.disabled ? o.disabled(scope.row) : false"
+                       :plain="o.plain"
+                       type="primary"
+                       :style="{
+                color:
+                  o.name === '鍒犻櫎' || o.name === 'delete'
+                    ? '#f56c6c'
+                    : o.color,
+              }"
+                       link
+                       @click.stop="o.clickFun(scope.row)"
+                       :key="key">
+              {{ o.name }}
+            </el-button>
+            <el-upload :action="
+                javaApi +
+                o.url +
+                '?id=' +
+                (o.uploadIdFun ? o.uploadIdFun(scope.row) : scope.row.id)
+              "
+                       ref="uploadRef"
+                       :multiple="o.multiple ? o.multiple : false"
+                       :limit="1"
+                       :disabled="o.disabled ? o.disabled(scope.row) : false"
+                       :accept="
+                o.accept
+                  ? o.accept
+                  : '.jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar'
+              "
+                       v-if="o.type == 'upload'"
+                       style="display: inline-block; width: 50px"
+                       v-show="o.showHide ? o.showHide(scope.row) : true"
+                       :headers="uploadHeader"
+                       :before-upload="(file) => beforeUpload(file, scope.$index)"
+                       :on-change="
+                (file, fileList) => handleChange(file, fileList, scope.$index)
+              "
+                       :on-error="
+                (error, file, fileList) =>
+                  onError(error, file, fileList, scope.$index)
+              "
+                       :on-success="
+                (response, file, fileList) =>
+                  handleSuccessUp(response, file, fileList, scope.$index)
+              "
+                       :on-exceed="onExceed"
+                       :show-file-list="false">
+              <el-button link
+                         type="primary"
+                         :disabled="o.disabled ? o.disabled(scope.row) : false">{{ o.name }}</el-button>
+            </el-upload>
+          </template>
+        </div>
+        <!-- 鍙偣鍑荤殑鏂囧瓧 -->
+        <div v-else-if="item.dataType == 'link'"
+             class="cell link"
+             style="width: 100%"
+             @click="goLink(scope.row, item.linkMethod)">
+          <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
+        </div>
+        <!-- 榛樿绾睍绀烘暟鎹� -->
+        <div v-else
+             class="cell"
+             style="width: 100%">
+          <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
+          <span v-else>{{
+            formatters(scope.row[item.prop], item.formatData)
+          }}</span>
+        </div>
+      </template>
+    </el-table-column>
+  </el-table>
+  <pagination v-if="isShowPagination"
+              :total="page.total"
+              :layout="page.layout"
+              :page="page.current"
+              :limit="page.size"
+              @pagination="paginationSearch" />
+</template>
+
+<script setup>
+  import pagination from "../../../../components/PIMTable/Pagination.vue";
+  import { ref, inject, getCurrentInstance } from "vue";
+  import { ElMessage } from "element-plus";
+
+  // 鑾峰彇鍏ㄥ眬鐨� uploadHeader
+  const { proxy } = getCurrentInstance();
+  const uploadHeader = proxy.uploadHeader;
+  const javaApi = proxy.javaApi;
+
+  const emit = defineEmits([
+    "pagination",
+    "expand-change",
+    "selection-change",
+    "row-click",
+  ]);
+
+  // Filters
+  const typeFn = (val, row) => {
+    return typeof val === "function" ? val(row) : val;
+  };
+
+  const formatters = (val, format) => {
+    return typeof format === "function" ? format(val) : val;
+  };
+
+  // Props锛堜娇鐢� defineProps 鐨勯潪 TS 褰㈠紡锛�
+  const props = defineProps({
+    tableLoading: {
+      type: Boolean,
+      default: false,
+    },
+    height: {
+      type: [Number, String],
+      default: "calc(100vh - 22em)",
+    },
+    expandRowKeys: {
+      type: Array,
+      default: () => [],
+    },
+    summaryMethod: {
+      type: Function,
+      default: () => {},
+    },
+    rowClick: {
+      type: Function,
+      default: () => {},
+    },
+    currentChange: {
+      type: Function,
+      default: () => {},
+    },
+    border: {
+      type: Boolean,
+      default: true,
+    },
+    isSelection: {
+      type: Boolean,
+      default: false,
+    },
+    selectable: {
+      type: Function,
+      default: () => true,
+    },
+    isShowPagination: {
+      type: Boolean,
+      default: true,
+    },
+    isShowSummary: {
+      type: Boolean,
+      default: false,
+    },
+    highlightCurrentRow: {
+      type: Boolean,
+      default: false,
+    },
+    headerCellStyle: {
+      type: Object,
+      default: () => ({}),
+    },
+    column: {
+      type: Array,
+      default: () => [],
+    },
+    rowClassName: {
+      type: Function,
+      default: () => "",
+    },
+    rowStyle: {
+      type: [Object, Function],
+      default: () => ({}),
+    },
+    tableData: {
+      type: Array,
+      default: () => [],
+    },
+    rowKey: {
+      type: String,
+      default: "id",
+    },
+    page: {
+      type: Object,
+      default: () => ({
+        total: 0,
+        current: 0,
+        size: 10,
+        layout: "total, sizes, prev, pager, next, jumper",
+      }),
+    },
+    total: {
+      type: Number,
+      default: 0,
+    },
+    tableStyle: {
+      type: [String, Object],
+      default: () => ({ width: "100%" }),
+    },
+  });
+
+  // Data
+  const multipleTable = ref(null);
+  const uploadRefs = ref([]);
+  const currentFiles = ref({});
+  const uploadKeys = ref({});
+
+  const indexMethod = index => {
+    return (props.page.current - 1) * props.page.size + index + 1;
+  };
+
+  // 鐐瑰嚮 link 浜嬩欢
+  const goLink = (row, linkMethod) => {
+    if (!linkMethod) {
+      return ElMessage.warning("璇烽厤缃� link 浜嬩欢");
+    }
+    const parentMethod = getParentMethod(linkMethod);
+    if (typeof parentMethod === "function") {
+      parentMethod(row);
+    } else {
+      console.warn(`鐖剁粍浠朵腑鏈壘鍒版柟娉�: ${linkMethod}`);
+    }
+  };
+
+  // 鑾峰彇鐖剁粍浠舵柟娉曪紙绀轰緥瀹炵幇锛�
+  const getParentMethod = methodName => {
+    const parentMethods = inject("parentMethods", {});
+    return parentMethods[methodName];
+  };
+
+  const dataTypeFn = (val, format) => {
+    if (typeof format === "function") {
+      return format(val);
+    } else return val;
+  };
+
+  const formatType = (val, format) => {
+    if (typeof format === "function") {
+      return format(val);
+    } else return "";
+  };
+
+  // 鏂囦欢鍙樺寲澶勭悊
+  const handleChange = (file, fileList, index) => {
+    if (fileList.length > 1) {
+      const earliestFile = fileList[0];
+      uploadRefs.value[index]?.handleRemove(earliestFile);
+    }
+    currentFiles.value[index] = file;
+  };
+
+  // 鏂囦欢涓婁紶鍓嶆牎楠�
+  const beforeUpload = (rawFile, index) => {
+    currentFiles.value[index] = {};
+    if (rawfile.size > 1024 * 1024 * 10 * 10) {
+      ElMessage.error("涓婁紶鏂囦欢涓嶈秴杩�10M");
+      return false;
+    }
+    return true;
+  };
+
+  // 涓婁紶鎴愬姛
+  const handleSuccessUp = (response, file, fileList, index) => {
+    if (response.code == 200) {
+      if (uploadRefs[index]) {
+        uploadRefs[index].clearFiles();
+      }
+      currentFiles[index] = file;
+      ElMessage.success("涓婁紶鎴愬姛");
+      resetUploadComponent(index);
+    } else {
+      ElMessage.error(response.message);
+    }
+  };
+
+  const resetUploadComponent = index => {
+    uploadKeys[index] = Date.now();
+  };
+
+  // 涓婁紶澶辫触
+  const onError = (error, file, fileList, index) => {
+    ElMessage.error("鏂囦欢涓婁紶澶辫触锛岃閲嶈瘯");
+    if (uploadRefs.value[index]) {
+      uploadRefs.value[index].clearFiles();
+    }
+  };
+
+  // 鏂囦欢鏁伴噺瓒呴檺鎻愮ず
+  const onExceed = () => {
+    ElMessage.warning("瓒呭嚭鏂囦欢涓暟");
+  };
+
+  const paginationSearch = ({ page, limit }) => {
+    emit("pagination", { page: page, limit: limit });
+  };
+
+  const rowClick = row => {
+    emit("row-click", row);
+  };
+
+  const expandChange = (row, expandedRows) => {
+    emit("expand-change", row, expandedRows);
+  };
+
+  const handleSelectionChange = newSelection => {
+    emit("selection-change", newSelection);
+  };
+
+  // 澶勭悊鍏ㄩ�夋搷浣�
+  const handleSelectAll = selection => {
+    if (selection.length) {
+      console.log(selection, "selection");
+      // 鍏ㄩ�夋椂锛屽彧閫夋嫨鍙�夋嫨鐨勮
+      const selectableRows = props.tableData.filter(row => props.selectable(row));
+      // 娓呯┖褰撳墠閫夋嫨
+      multipleTable.value.clearSelection();
+      // 鍙�夋嫨鍙�夋嫨鐨勮
+      selectableRows.forEach(row => {
+        multipleTable.value.toggleRowSelection(row, true);
+      });
+    } else {
+      // 鍙栨秷鍏ㄩ�夋椂锛屽彧鍙栨秷鍙�夋嫨鐨勮鐨勯�夋嫨
+      props.tableData.forEach(row => {
+        if (props.selectable(row)) {
+          multipleTable.value.toggleRowSelection(row, false);
+        }
+      });
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .cell {
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    padding-right: 0 !important;
+    padding-left: 0 !important;
+  }
+
+  .pim-table-header-extra :deep(.el-input),
+  .pim-table-header-extra :deep(.el-select) {
+    width: 100%;
+  }
+</style>

--
Gitblit v1.9.3