gaoluyang
2 天以前 df1406d0f571972d033dffd6a93fb4b94febeb56
Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
# src/views/index.vue
已修改21个文件
2830 ■■■■■ 文件已修改
.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/FileUpload/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Table/EtableModify.vue 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/archiveManagement/index.vue 207 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/archiveManagement/mould/archiveDialog.vue 77 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicInformation/index.vue 205 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicInformation/mould/coal.vue 253 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicInformation/mould/coalMeiZhiZiDuanWeiHu.vue 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicInformation/mould/coalQualityMaintenance.vue 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicInformation/mould/customer.vue 94 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicInformation/mould/supplier.vue 74 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index.vue 546 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procureMent/components/ProductionDialog.vue 160 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procureMent/index.vue 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/production/components/ProductionDetailsTable.vue 163 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/production/components/ProductionDialog.vue 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/production/components/useCoalData.js 152 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/production/components/useDialog.js 90 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/production/components/useTableData.js 236 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/production/index.vue 91 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -21,3 +21,4 @@
package-lock.json
yarn.lock
pnpm-lock.yaml
package.json
@@ -23,7 +23,7 @@
    "axios": "0.28.1",
    "clipboard": "2.0.11",
    "default-passive-events": "^4.0.0",
    "echarts": "5.5.1",
    "echarts": "^5.6.0",
    "element-china-area-data": "^6.1.0",
    "element-plus": "2.7.6",
    "file-saver": "2.0.5",
src/components/FileUpload/index.vue
@@ -231,10 +231,11 @@
  }
  // 桌面端下载
  const link = document.createElement("a");
  link.href = url;
  link.href = file.downloadUrl;
  link.download = file.bucketFilename || file.key;
  link.click();
};
// 上传前校验
const handleBeforeUpload = (file) => {
  // 校验文件名特殊字符
src/components/Table/EtableModify.vue
@@ -28,33 +28,35 @@
      type="index"
      width="60"
      align="center"
    />
    <template v-for="col in columns" :key="col.prop">
    />    <template v-for="col in columns" :key="col.prop">
      <el-table-column
        v-bind="col"
        :show-overflow-tooltip="false"
        align="center"
      >
        <template #default="scope">
          <template v-if="col.slot">
            <slot></slot>
          </template>
          <template v-else>
            <slot
              :name="col.prop"
              :row="scope.row"
              :column="scope.column"
              :index="scope.$index"
            ></slot>
          </template>          <template v-else>
            <div
              class="cell-edit"
              @dblclick="handleCellEdit(scope.row, col.prop)"
              :class="{ editable: isColumnEditable(col.prop) }"
            >
              <!-- 显示状态:使用格式化的值 -->
              <span
                v-if="!scope.row.editing || !scope.row.editing[col.prop]"
                class="cell-text"
              >
                {{
                  scope.row[col.prop] == null || scope.row[col.prop] === ""
                    ? "--"
                    : scope.row[col.prop]
                  formatCellValue(scope.row, scope.column, scope.row[col.prop], col)
                }}
              </span>
              <!-- 编辑状态:使用原始值,不经过格式化 -->
              <el-input
                v-else
                v-model="scope.row[col.prop]"
@@ -69,34 +71,6 @@
        </template>
      </el-table-column>
    </template>
    <!-- 操作列 -->
    <el-table-column
      v-if="showOperations"
      :label="operationsLabel"
      :width="operationsWidth"
      fixed="right"
      align="center"
    >
      <template #default="scope">
        <slot name="operations" :row="scope.row">
          <el-button
            v-if="operations.includes('edit')"
            link
            type="primary"
            size="small"
            @click="handleEdit(scope.row)"
            >编辑</el-button
          >
          <!--            <el-button-->
          <!--              v-if="operations.includes('delete')"-->
          <!--              link-->
          <!--              type="danger"-->
          <!--              size="small"-->
          <!--              @click="handleDelete(scope.row)"-->
          <!--            >删除</el-button>-->
        </slot>
      </template>
    </el-table-column>
  </el-table>
</template>
@@ -217,7 +191,22 @@
  // 检查该列在所有数据中是否有非空值
  return data.some((row) => row[col.prop] != null && row[col.prop] !== "");
};
// 默认的格式化函数
const defaultFormatter = (row, column, cellValue) => {
  return cellValue == null || cellValue === "" || cellValue === 0
    ? "0"
    : cellValue;
};
// 格式化单元格值
const formatCellValue = (row, column, cellValue, col) => {
  // 如果列有自定义格式化器,使用自定义格式化器
  if (col.formatter && typeof col.formatter === 'function') {
    return col.formatter(row, column, cellValue);
  }
  // 否则使用默认格式化器
  return defaultFormatter(row, column, cellValue);
};
// 处理单元格编辑
const handleCellEdit = (row, prop) => {
  // 如果不允许编辑单元格,直接返回
src/views/archiveManagement/index.vue
@@ -4,84 +4,87 @@
      <div class="left-content">
        <div class="tree-header">
          <h3>文档管理</h3>
          <el-button type="primary" size="small" @click="append('')" icon="Plus"
            >新增</el-button
          <el-button icon="Plus" size="small" type="primary" @click="append('')"
          >新增
          </el-button
          >
        </div>
        <!-- 搜索框 -->
        <div class="search-box">
          <el-input
            v-model="filterText"
            placeholder="输入关键字进行搜索"
            size="small"
            clearable
            @input="handleFilter"
              v-model="filterText"
              clearable
              placeholder="输入关键字进行搜索"
              size="small"
              @input="handleFilter"
          >
            <template #prefix>
              <el-icon><Search /></el-icon>
              <el-icon>
                <Search/>
              </el-icon>
            </template>
          </el-input>
        </div>
        <div class="tree-container">
          <el-tree
            ref="treeRef"
            :data="treeData"
            :props="props"
            :filter-node-method="filterNode"
            :expand-on-click-node="false"
            :default-expand-all="false"
            node-key="id"
            @node-click="handleNodeClick"
            class="custom-tree"
              ref="treeRef"
              :data="treeData"
              :default-expand-all="false"
              :expand-on-click-node="false"
              :filter-node-method="filterNode"
              :props="props"
              class="custom-tree"
              node-key="id"
              @node-click="handleNodeClick"
          >
            <template #default="{ node, data }">
              <div class="tree-node-content" @dblclick="headerDbClick(data)">
                <div class="node-icon">
                  <el-icon
                    v-if="!node.isLeaf"
                    :class="{ expanded: node.expanded }"
                      v-if="!node.isLeaf"
                      :class="{ expanded: node.expanded }"
                  >
                    <Folder />
                    <Folder/>
                  </el-icon>
                  <el-icon v-else>
                    <Document />
                    <Document/>
                  </el-icon>
                </div>
                <div class="node-label">
                  <span v-if="!data.isEdit" class="label-text">{{
                    node.label
                  }}</span>
                      node.label
                    }}</span>
                  <el-input
                    v-else
                    :ref="(el) => setInputRef(el, data)"
                    placeholder="请输入节点名称"
                    v-model="newName"
                    @blur="($event) => handleInputBlur($event, data, node)"
                    @keyup.enter="
                      ($event) => handleInputBlur($event, data, node)
                      v-else
                      :ref="(el) => setInputRef(el, data)"
                      v-model="newName"
                      autofocus
                      class="tree-input"
                      placeholder="请输入节点名称"
                      size="small"
                      @blur="(event) => handleInputBlur(event, data, node)"
                      @keyup.enter="
                      (event) => handleInputBlur(event, data, node)
                    "
                    size="small"
                    class="tree-input"
                    autofocus
                  />
                </div>
                <div class="node-actions" v-show="!data.isEdit">
                <div v-show="!data.isEdit" class="node-actions">
                  <el-button
                    link
                    size="small"
                    @click.stop="append(data)"
                    icon="Plus"
                    title="新增子节点"
                      icon="Plus"
                      link
                      size="small"
                      title="新增子节点"
                      @click.stop="append(data)"
                  ></el-button>
                  <el-button
                    link
                    size="small"
                    @click.stop="remove(node, data)"
                    icon="Delete"
                    title="删除"
                      icon="Delete"
                      link
                      size="small"
                      title="删除"
                      @click.stop="remove(node, data)"
                  ></el-button>
                </div>
              </div>
@@ -92,71 +95,64 @@
    </div>
    <div class="right">
      <el-row :gutter="24">
        <el-col :span="2" :offset="20"
          ><el-button :icon="Delete" type="danger" @click="delHandler">删除</el-button></el-col
        <el-col :offset="20" :span="2"
        >
          <el-button :icon="Delete" type="danger" @click="delHandler">删除</el-button>
        </el-col
        >
        <el-col :span="2"
          ><el-button
            :icon="Plus"
            type="primary"
            @click="add"
            :disabled="!tableData.length"
            >新增</el-button
          ></el-col
        >
          <el-button
              :disabled="!tableSwitch"
              :icon="Plus"
              type="primary"
              @click="add"
          >新增
          </el-button
          >
        </el-col
        >
      </el-row>
      <ETable
        :maxHeight="1200"
        :loading="loading"
        :table-data="tableData"
        :columns="columns"
        @selection-change="handleSelectionChange"
        @edit="handleEdit"
        :show-selection="true"
        :border="true"
          :border="true"
          :columns="columns"
          :loading="loading"
          :maxHeight="1200"
          :show-selection="true"
          :table-data="tableData"
          @edit="handleEdit"
          @selection-change="handleSelectionChange"
      >
      </ETable>
      <Pagination
        :total="total"
        :page="queryParams.current"
        :limit="queryParams.pageSize"
        :show-total="true"
        @pagination="handlePageChange"
        :layout="'total, prev, pager, next, jumper'"
          :layout="'total, prev, pager, next, jumper'"
          :limit="queryParams.pageSize"
          :page="queryParams.current"
          :show-total="true"
          :total="total"
          @pagination="handlePageChange"
      ></Pagination>
    </div>
    <archiveDialog
      v-model:centerDialogVisible="dialogVisible"
      @centerDialogVisible="centerDialogVisible"
      :row="row"
      @submitForm="submitForm"
      ref="archiveDialogs"
        ref="archiveDialogs"
        v-model:centerDialogVisible="dialogVisible"
        :row="row"
        @centerDialogVisible="centerDialogVisible"
        @submitForm="submitForm"
    >
    </archiveDialog>
  </el-card>
</template>
<script setup>
import { onMounted, ref, nextTick, reactive } from "vue";
import {nextTick, onMounted, reactive, ref} from "vue";
import ETable from "@/components/Table/ETable.vue";
import { ElButton, ElInput, ElIcon, ElMessage } from "element-plus";
import {ElButton, ElIcon, ElInput, ElMessage} from "element-plus";
import archiveDialog from "./mould/archiveDialog.vue";
import Pagination from "@/components/Pagination/index.vue";
import {
  Plus,
  Search,
  Folder,
  Document,
  Delete,
} from "@element-plus/icons-vue";
import {
  getTree,
  addOrEditTree,
  delTree,
  getArchiveList,
  addOrEditArchive,
  delArchive,
} from "@/api/archiveManagement";
import {Delete, Document, Folder, Plus, Search,} from "@element-plus/icons-vue";
import {addOrEditTree, delArchive, delTree, getArchiveList, getTree,} from "@/api/archiveManagement";
const dialogVisible = ref(false); // 控制归档对话框显示
const loading = ref(false);
const tableData = ref([]);
@@ -167,9 +163,9 @@
const treeRef = ref(); // 树组件引用
const total = ref(0); // 总记录数
const columns = [
  { prop: "name", label: "名称", minWidth: 180 },
  { prop: "type", label: "类型", minWidth: 120 },
  { prop: "status", label: "状态", minWidth: 100 },
  {prop: "name", label: "名称", minWidth: 180},
  {prop: "type", label: "类型", minWidth: 120},
  {prop: "status", label: "状态", minWidth: 100},
];
const selectedRows = reactive([]); // 存储选中行数据
const handleSelectionChange = (selection) => {
@@ -192,7 +188,6 @@
};
const submitForm = async (res) => {
  if (res && res.code === 200) {
    ElMessage.success("操作成功");
    // 刷新列表数据
    await getArchiveListData();
  } else {
@@ -201,8 +196,10 @@
}
const centerDialogVisible = (val) => {
};
const tableSwitch = ref(false);
// 处理节点点击
const handleNodeClick = async (data) => {
const handleNodeClick = (data) => {
  tableSwitch.value = true;
  // 切换节点时重置到第一页
  queryParams.current = 1;
  queryParams.treeId = data.id;
@@ -217,7 +214,7 @@
  archiveDialogs.value.initForm(); // 重置表单
};
// 处理分页变化
const handlePageChange = ({ page }) => {
const handlePageChange = ({page}) => {
  queryParams.current = page;
  // pageSize 固定为20,不再从参数中获取
  getArchiveListData();
@@ -238,7 +235,6 @@
      total.value = 0;
      return;
    }
    tableData.value = res.data?.records || res.data || [];
    total.value = res.data?.total || 0;
    // 确保分页参数正确更新
@@ -262,7 +258,7 @@
  }
  try {
    const ids = selectedRows.map((row) => row.id);
    const { code, msg } = await delArchive(ids);
    const {code, msg} = await delArchive(ids);
    if (code !== 200) {
      ElMessage.warning("删除失败: " + msg);
    } else {
@@ -303,6 +299,7 @@
// 处理输入框失焦
const handleInputBlur = async (event, comeTreeData, node) => {
  if (!comeTreeData.isEdit) return; // 如果不是编辑状态,直接返回
  console.log("handleInputBlur", event, comeTreeData, node);
  if (event.relatedTarget && event.relatedTarget.tagName === "BUTTON") {
    return;
  }
@@ -344,7 +341,7 @@
  if (!data || !data.id) {
    return;
  }
  let { code, msg } = await delTree([data.id]);
  let {code, msg} = await delTree([data.id]);
  if (code !== 200) {
    ElMessage.warning("删除失败, " + msg);
  } else {
@@ -378,9 +375,9 @@
    const isExpanded = node?.expanded; // 如果有子级且未展开,先展开节点
    if (hasChildren && !isExpanded) {
      if (
        treeRef.value &&
        treeRef.value.store &&
        treeRef.value.store.nodesMap[nodeKey]
          treeRef.value &&
          treeRef.value.store &&
          treeRef.value.store.nodesMap[nodeKey]
      ) {
        treeRef.value.store.nodesMap[nodeKey].expanded = true;
      }
@@ -439,7 +436,7 @@
  }
};
</script>
<style scoped lang="scss">
<style lang="scss" scoped>
.custom-tree-node {
  flex: 1;
  display: flex;
@@ -630,11 +627,13 @@
    }
  }
}
.el-card {
  width: calc(100% - 40px);
  height: calc(100vh - 130px);
  margin: 20px;
  box-sizing: border-box;
  .left {
    width: 30%;
    height: calc(100vh - 160px);
@@ -651,13 +650,15 @@
      flex-direction: column;
    }
  }
  .right {
    width: 70%;
    height: calc(100vh - 160px);
    padding: 0px 10px;
    padding: 0 10px;
    float: left;
  }
}
.archive-management-card {
  margin: 0;
}
src/views/archiveManagement/mould/archiveDialog.vue
@@ -1,28 +1,28 @@
<template>
  <el-dialog v-model="centerDialogVisible" title="文档管理" width="500" center>
    <el-form
      ref="ruleFormRef"
      style="max-width: 600px"
      :model="ruleForm"
      :rules="rules"
      label-width="auto"
        ref="ruleFormRef"
        style="max-width: 600px"
        :model="ruleForm"
        :rules="rules"
        label-width="auto"
    >
      <el-form-item label="名称" prop="name">
        <el-input v-model="ruleForm.name" placeholder="请输入文档名称" />
        <el-input v-model="ruleForm.name" placeholder="请输入文档名称"/>
      </el-form-item>
      <el-form-item label="请输入文档类型" prop="type">
        <el-select v-model="ruleForm.type" placeholder="请输入文档类型">
          <el-option label="合同" value="合同" />
          <el-option label="报告" value="报告" />
          <el-option label="合同" value="合同"/>
          <el-option label="报告" value="报告"/>
        </el-select>
      </el-form-item>
      <el-form-item label="请输入文档状态" prop="status">
        <el-select v-model="ruleForm.status" placeholder="请输入文档状态">
          <el-option
            v-for="option in options"
            :key="option.value"
            :label="option.label"
            :value="option.value"
              v-for="option in options"
              :key="option.value"
              :label="option.label"
              :value="option.value"
          />
        </el-select>
      </el-form-item>
@@ -31,26 +31,26 @@
      <el-row>
        <el-col :span="24" style="text-align: right">
          <el-button @click="centerDialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="submit"> 确 定 </el-button>
          <el-button type="primary" @click="submit"> 确 定</el-button>
        </el-col>
      </el-row>
    </template>
    <fileUpload
      ref="fileUploadRef"
      :fileSize="1024"
      :fileType="['pdf', 'docx', 'txt', 'xlsx', 'pptx....']"
      :limit="10"
      :drag="false"
      v-model:modelValue="modelValue"
        ref="fileUploadRef"
        :fileSize="1024"
        :fileType="['pdf', 'docx', 'txt', 'xlsx', 'pptx....']"
        :limit="10"
        :drag="false"
        v-model:modelValue="modelValue"
    />
  </el-dialog>
</template>
<script setup>
import { ref, watch } from "vue";
import { addOrEditArchive } from "@/api/archiveManagement";
import {ref, watch, nextTick} from "vue";
import {addOrEditArchive} from "@/api/archiveManagement";
import fileUpload from "@/components/FileUpload/index.vue";
import { ElMessage } from "element-plus";
import {ElMessage} from "element-plus";
const centerDialogVisible = defineModel("centerDialogVisible", {
  type: Boolean,
@@ -85,22 +85,24 @@
const copyForm = ref();
// 监听 row 的变化,更新 ruleForm
watch(
  () => props.row,
  (newRow) => {
    copyForm.value = initFormData(newRow);
    ruleForm.value = JSON.parse(JSON.stringify(copyForm.value));
  },
  { deep: true }
    () => props.row,
    (newRow) => {
      copyForm.value = initFormData(newRow);
      ruleForm.value = JSON.parse(JSON.stringify(copyForm.value));
    },
    {deep: true}
);
const rules = {
  name: [{ required: true, message: "请输入文档名称", trigger: "blur" }],
  type: [{ required: true, message: "请选择文档类型", trigger: "blur" }],
  status: [{ required: true, message: "请选择文档状态", trigger: "blur" }],
  name: [{required: true, message: "请输入文档名称", trigger: "blur"}],
  type: [{required: true, message: "请选择文档类型", trigger: "blur"}],
  status: [{required: true, message: "请选择文档状态", trigger: "blur"}],
};
const fileUploadRef = ref(null);
const initForm = () => {
  ruleForm.value = {};
  fileUploadRef.value.init();
  nextTick(() => {
    fileUploadRef.value.init();
  });
};
const editForm = (val) => {
  ruleForm.value = copyForm.value;
@@ -113,9 +115,9 @@
  editForm,
});
const options = [
  { value: "有效", label: "有效" },
  { value: "无效", label: "无效" },
  { value: "作废", label: "作废" },
  {value: "有效", label: "有效"},
  {value: "无效", label: "无效"},
  {value: "作废", label: "作废"},
];
const emit = defineEmits(["submitForm", "update:modelValue"]);
const modelValue = ref([]);
@@ -132,10 +134,6 @@
    // 调用 API
    try {
      const res = await addOrEditArchive(ruleForm.value);
      ElMessage({
        type: "success",
        message: res.msg || "操作成功",
      });
      emit("submitForm", res);
    } catch (error) {
      ElMessage({
@@ -147,6 +145,7 @@
    // 发送 emit 事件
    // 关闭对话框
    ElMessage.success("操作成功");
    centerDialogVisible.value = false;
  } catch (error) {
    ElMessage({
src/views/basicInformation/index.vue
@@ -1,7 +1,8 @@
<template>
  <div> <el-form :inline="true" :model="queryParams" class="search-form">
      <el-form-item label="搜索" v-if="shouldShowSearch">
        <el-input v-model="queryParams.searchAll" :placeholder="searchPlaceholder" clearable />
  <div>
    <el-form :inline="true" :model="queryParams" class="search-form">
      <el-form-item v-if="shouldShowSearch" label="搜索">
        <el-input v-model="queryParams.searchAll" :placeholder="searchPlaceholder" clearable/>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="search">查询</el-button>
@@ -11,58 +12,62 @@
    <el-card>
      <!-- 标签页 -->
      <el-tabs v-model="activeTab" class="info-tabs" @tab-click="handleTabClick">
        <el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name" />
        <el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name"/>
      </el-tabs>
      <!-- 操作按钮区 -->
      <el-row :gutter="24" class="table-toolbar">
        <el-button type="primary" :icon="Plus" @click="handleAdd">新建</el-button>
        <el-button type="danger" :icon="Delete" @click="handleDelete">删除</el-button>
        <el-button type="info" :icon="Download" @click="handleExport" v-show="canExport">导出</el-button>
        <el-button :icon="Plus" type="primary" @click="handleAdd">新建</el-button>
        <el-button :icon="Delete" type="danger" @click="handleDelete">删除</el-button>
        <el-button @click="jump">admins</el-button>
        <el-button v-show="canExport" :icon="Download" type="info" @click="handleExport">导出</el-button>
      </el-row> <!-- 表格组件 -->
      <div>
        <data-table :loading="loading" :table-data="tableData" :columns="columns"
          @selection-change="handleSelectionChange" @edit="handleEdit" :show-selection="true" :border="true">          <!-- 字段名称列的自定义插槽 - 显示为标签 -->
        <data-table :border="true" :columns="columns" :loading="loading"
                    :show-selection="true" :table-data="tableData" @edit="handleEdit" @selection-change="handleSelectionChange">
          <!-- 字段名称列的自定义插槽 - 显示为标签 -->
          <template v-if="tabName === 'coalQualityMaintenance'" #fieldIds="{ row }">
            <template v-if="typeof row.fieldIds === 'string' && row.fieldIds.includes(',')">
              <el-tag v-for="(field, index) in row.fieldIds.split(',')" :key="index" type="primary" size="small"
                style="margin-right: 4px; margin-bottom: 2px;">
              <el-tag v-for="(field, index) in row.fieldIds.split(',')" :key="index" size="small" style="margin-right: 4px; margin-bottom: 2px;"
                      type="primary">
                {{ getFieldDisplayName(field.trim()) }}
              </el-tag>
            </template>
            <template v-else>
              <el-tag type="primary" size="small">
              <el-tag size="small" type="primary">
                {{ getFieldDisplayName(row.fieldIds) || '--' }}
              </el-tag>
            </template>
          </template>
        </data-table>
      </div>
      <pagination v-if="total > 0" :page="pageNum" :limit="pageSizes" :total="total" @pagination="handPagination"
        :layout="'total, prev, pager, next, jumper'" />
      <pagination v-if="total > 0" :layout="'total, prev, pager, next, jumper'" :limit="pageSizes" :page="pageNum" :total="total"
                  @pagination="handPagination"/>
      <Supplier v-if="tabName === 'supplier'" v-model:copyForm="copyForm"
        v-model:supplierDialogFormVisible="dialogFormVisible" :form="form" :title="title" @submit="handleSubmit"
        @beforeClose="handleBeforeClose" @update:dialogFormVisible="handleDialogFormVisible" :addOrEdit="addOrEdit" />
                v-model:supplierDialogFormVisible="dialogFormVisible" :addOrEdit="addOrEdit" :form="form" :title="title"
                @beforeClose="handleBeforeClose" @submit="handleSubmit"
                @update:dialogFormVisible="handleDialogFormVisible"/>
      <Customer v-if="tabName === 'customer'" v-model:copyForm="copyForm"
        v-model:customerDialogFormVisible="dialogFormVisible" :form="form" :title="title" @submit="handleSubmit"
        :addOrEdit="addOrEdit" @beforeClose="handleBeforeClose" />
      <Coal v-if="tabName === 'coal'" v-model:copyForm="copyForm" v-model:coalDialogFormVisible="dialogFormVisible"
        :form="form" :title="title" :addOrEdit="addOrEdit" @submit="handleSubmit" />
      <coalQualityMaintenance v-if="tabName === 'coalQualityMaintenance'" v-model:copyForm="copyForm"
        v-model:coalQualityMaintenanceDialogFormVisible="dialogFormVisible" :form="form" :title="title"
        :addOrEdit="addOrEdit" @submit="handleSubmit" />
      <coalMeiZhiZiDuanWeiHu v-if="tabName === 'coalMeiZhiZiDuanWeiHu'" v-model:copyForm="copyForm"
        v-model:coalMaintenanceFieldDialogVisible="dialogFormVisible" :form="form" :title="title" :addOrEdit="addOrEdit"
        @submit="handleSubmit" />
                v-model:customerDialogFormVisible="dialogFormVisible" :addOrEdit="addOrEdit" :form="form" :title="title"
                @beforeClose="handleBeforeClose" @submit="handleSubmit"/>
      <Coal v-if="tabName === 'coal'" v-model:coalDialogFormVisible="dialogFormVisible" v-model:copyForm="copyForm"
            :addOrEdit="addOrEdit" :form="form" :title="title" @submit="handleSubmit"/>
      <coalQualityMaintenance v-if="tabName === 'coalQualityMaintenance'" v-model:coalQualityMaintenanceDialogFormVisible="dialogFormVisible"
                              v-model:copyForm="copyForm" :addOrEdit="addOrEdit"
                              :form="form"
                              :title="title" @submit="handleSubmit"/>
      <coalMeiZhiZiDuanWeiHu v-if="tabName === 'coalMeiZhiZiDuanWeiHu'" v-model:coalMaintenanceFieldDialogVisible="dialogFormVisible"
                             v-model:copyForm="copyForm" :addOrEdit="addOrEdit" :form="form"
                             :title="title"
                             @submit="handleSubmit"/>
    </el-card>
  </div>
</template>
<script setup>
// ===== 核心依赖导入 =====
import { ref, reactive, onMounted, computed, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { Plus, Edit, Delete, Download } from "@element-plus/icons-vue";
import {computed, getCurrentInstance, onMounted, reactive, ref, nextTick} from "vue";
import {ElMessage, ElMessageBox} from "element-plus";
import {Delete, Download, Plus} from "@element-plus/icons-vue";
// ===== 组件导入 =====
import DataTable from "@/components/Table/ETable.vue";
@@ -74,18 +79,20 @@
import coalMeiZhiZiDuanWeiHu from "./mould/coalMeiZhiZiDuanWeiHu.vue";
// ===== API 服务导入 =====
import { getSupply, delSupply } from "@/api/basicInformation/supplier.js";
import { getCoalInfo, delCoalInfo } from "@/api/basicInformation/coal.js";
import { testUserList } from "@/api/tool/publicInterface.js";
import { getAreaOptions } from "@/api/system/area.js";
import { getCustomerList, delCustomer } from "@/api/basicInformation/customer.js";
import { coalField, deleteCoalField } from "@/api/basicInformation/coalFieldMaintenance.js";
import { getCoalFieldList, getCoalPlanList } from "@/api/basicInformation/coalQualityMaintenance";
import {delSupply, getSupply} from "@/api/basicInformation/supplier.js";
import {delCoalInfo, getCoalInfo} from "@/api/basicInformation/coal.js";
import {testUserList} from "@/api/tool/publicInterface.js";
import {getAreaOptions} from "@/api/system/area.js";
import {delCustomer, getCustomerList} from "@/api/basicInformation/customer.js";
import {coalField, deleteCoalField} from "@/api/basicInformation/coalFieldMaintenance.js";
import {getCoalFieldList, getCoalPlanList} from "@/api/basicInformation/coalQualityMaintenance";
const { proxy } = getCurrentInstance();
const {proxy} = getCurrentInstance();
import router from "@/router";
// ===== 响应式状态管理 =====
const jump = () => {
};
// 弹窗控制状态
const dialogFormVisible = ref(false);
const form = ref({});
@@ -124,11 +131,11 @@
// 标签页配置
const tabs = reactive([
  { name: "supplier", label: "供应商信息" },
  { name: "customer", label: "客户信息" },
  { name: "coal", label: "煤种信息" },
  { name: "coalQualityMaintenance", label: "煤质方案" },
  { name: "coalMeiZhiZiDuanWeiHu", label: "煤质字段" }
  {name: "supplier", label: "供应商信息"},
  {name: "customer", label: "客户信息"},
  {name: "coal", label: "煤种信息"},
  {name: "coalQualityMaintenance", label: "煤质方案"},
  {name: "coalMeiZhiZiDuanWeiHu", label: "煤质字段"}
]);
// ===== 工具函数 =====
@@ -178,18 +185,18 @@
 * @description 将地址ID数组转换为可读的地址字符串
 */
const formatAddressArray = (addressIds) => {
  if (!addressMap.value || Object.keys(addressMap.value).length === 0 ||
      !addressIds || !Array.isArray(addressIds) || addressIds.length === 0 ||
  if (!addressMap.value || Object.keys(addressMap.value).length === 0 ||
      !addressIds || !Array.isArray(addressIds) || addressIds.length === 0 ||
      addressIds.every(id => !id)) {
    return '--';
  }
  const addressNames = addressIds.map(id => addressMap.value[id]?.name || '--');
  if (addressNames.every(name => name === '--')) {
    return '--';
  }
  return addressNames.filter(name => name !== '--').join(' / ');
};
@@ -217,7 +224,7 @@
 */
const coalFieldData = async () => {
  try {
    const { data, code } = await getCoalFieldList();
    const {data, code} = await getCoalFieldList();
    if (code === 200) {
      coalFieldList.value = data;
    }
@@ -234,10 +241,10 @@
 */
const getFieldDisplayName = (fieldId) => {
  if (!fieldId) return '--';
  const numId = parseInt(fieldId);
  const matchedField = coalFieldList.value.find(item => item.id === numId);
  return matchedField ? matchedField.fieldName : numId;
};
@@ -256,7 +263,7 @@
const searchPlaceholder = computed(() => {
  const placeholderMap = {
    supplier: "供应商/识别码/详细地址",
    customer: "供应商/识别码/详细地址",
    customer: "供应商/识别码/详细地址",
    coal: "请输入搜索信息",
    coalQualityMaintenance: "请输入搜索信息",
    coalMeiZhiZiDuanWeiHu: "请输入搜索信息"
@@ -268,7 +275,7 @@
 * 是否显示搜索框
 */
const shouldShowSearch = computed(() => {
  return ['supplier', 'customer', 'coal', 'coalQualityMaintenance','coalMeiZhiZiDuanWeiHu'].includes(tabName.value);
  return ['supplier', 'customer', 'coal', 'coalQualityMaintenance', 'coalMeiZhiZiDuanWeiHu'].includes(tabName.value);
});
/**
@@ -287,8 +294,8 @@
 * 供应商表格列配置
 */
const supplierColumns = ref([
  { prop: "supplierName", label: "供应商名称", minWidth: 100 },
  { prop: "taxpayerId", label: "统一人识别号", minWidth: 170 },
  {prop: "supplierName", label: "供应商名称", minWidth: 100},
  {prop: "taxpayerId", label: "统一人识别号", minWidth: 170},
  {
    prop: "bids",
    label: "经营地址",
@@ -299,10 +306,10 @@
      return formatAddressArray(addressIds);
    }
  },
  { prop: "businessAddress", label: "经营详细地址", minWidth: 150 },
  { prop: "bankAccount", label: "开户行", minWidth: 120 },
  { prop: "bankName", label: "银行账号", minWidth: 150 },
  { prop: "contactPerson", label: "联系人", minWidth: 100 },
  {prop: "businessAddress", label: "经营详细地址", minWidth: 150},
  {prop: "bankAccount", label: "开户行", minWidth: 120},
  {prop: "bankName", label: "银行账号", minWidth: 150},
  {prop: "contactPerson", label: "联系人", minWidth: 100},
  {
    prop: "cids",
    label: "联系人地址",
@@ -313,16 +320,16 @@
      return formatAddressArray(addressIds);
    }
  },
  { prop: "contactAddress", label: "联系人详细地址", minWidth: 120 },
  { prop: "updateTime", label: "维护日期", minWidth: 120 },
  {prop: "contactAddress", label: "联系人详细地址", minWidth: 120},
  {prop: "updateTime", label: "维护日期", minWidth: 120},
]);
/**
 * 客户表格列配置
 */
const customerColumns = ref([
  { prop: "customerName", label: "客户名称", minWidth: 100 },
  { prop: "taxpayerId", label: "统一人识别号", minWidth: 120 },
  {prop: "customerName", label: "客户名称", minWidth: 100},
  {prop: "taxpayerId", label: "统一人识别号", minWidth: 120},
  {
    prop: "bids",
    label: "经营地址",
@@ -333,11 +340,11 @@
      return formatAddressArray(addressIds);
    }
  },
  { prop: "businessAddress", label: "详细地址", minWidth: 150 },
  { prop: "bankName", label: "开户行", minWidth: 120 },
  { prop: "bankAccount", label: "银行账号", minWidth: 150 },
  { prop: "contactPerson", label: "联系人", minWidth: 100 },
  { prop: "contactPhone", label: "联系人电话", minWidth: 100 },
  {prop: "businessAddress", label: "详细地址", minWidth: 150},
  {prop: "bankName", label: "开户行", minWidth: 120},
  {prop: "bankAccount", label: "银行账号", minWidth: 150},
  {prop: "contactPerson", label: "联系人", minWidth: 100},
  {prop: "contactPhone", label: "联系人电话", minWidth: 100},
  {
    prop: "cids",
    label: "联系人地址",
@@ -348,15 +355,15 @@
      return formatAddressArray(addressIds);
    }
  },
  { prop: "contactAddress", label: "联系人详细地址", minWidth: 150 },
  { prop: "updateTime", label: "维护日期", minWidth: 100 },
  {prop: "contactAddress", label: "联系人详细地址", minWidth: 150},
  {prop: "updateTime", label: "维护日期", minWidth: 100},
]);
/**
 * 煤种表格列配置
 */
const coalColumns = ref([
  { prop: "coal", label: "煤种名称", minWidth: 200 },
  {prop: "coal", label: "煤种名称", minWidth: 200},
  {
    prop: "maintainerId",
    label: "维护人",
@@ -371,14 +378,14 @@
      return userMap.value[cellValue] || '--';
    }
  },
  { prop: "maintenanceDate", label: "维护日期", minWidth: 150 },
  {prop: "maintenanceDate", label: "维护日期", minWidth: 150},
]);
/**
 * 煤质方案表格列配置
 */
const coalQualityMaintenanceColumns = ref([
  { prop: "plan", label: "方案名称", minWidth: 100 },
  {prop: "plan", label: "方案名称", minWidth: 100},
  {
    prop: "fieldIds",
    label: "字段名称",
@@ -392,15 +399,15 @@
      return cellValue || '--';
    }
  },
  { prop: "schemeDesc", label: "字段描述", minWidth: 100 },
  {prop: "schemeDesc", label: "字段描述", minWidth: 100},
]);
/**
 * 煤质字段表格列配置
 */
const coalMeiZhiZiDuanWeiHuColumns = ref([
  { prop: "fieldName", label: "字段名称", minWidth: 200 },
  { prop: "fieldDescription", label: "字段描述", minWidth: 200 },
  {prop: "fieldName", label: "字段名称", minWidth: 200},
  {prop: "fieldDescription", label: "字段描述", minWidth: 200},
]);
// ===== 事件处理函数 =====
@@ -489,10 +496,10 @@
 */
const handleAddEdit = (currentTabName) => {
  const actionText = addOrEdit.value === "add" ? "新增" : "编辑";
  const tabTitleMap = {
    supplier: "供应商信息",
    customer: "客户信息",
    customer: "客户信息",
    coal: "煤种信息",
    coalQualityMaintenance: "煤质方案维护",
    coalMeiZhiZiDuanWeiHu: "煤质字段维护"
@@ -561,27 +568,27 @@
 */
const handleEdit = (row) => {
  form.value = JSON.parse(JSON.stringify(row));
  // 构建供应商业务地址数组
  if (form.value.bprovinceId && form.value.bdistrictId && form.value.bcityId) {
    form.value.bids = [row.bprovinceId, row.bcityId, row.bdistrictId];
  }
  // 构建供应商联系地址数组
  if (form.value.cprovinceId && form.value.cdistrictId && form.value.ccityId) {
    form.value.cids = [row.cprovinceId, row.ccityId, row.cdistrictId];
  }
  // 构建客户业务地址数组
  if (form.value.businessCityId && form.value.businessDistrictId && form.value.businessProvinceId) {
    form.value.bids = [row.businessProvinceId, row.businessCityId, row.businessDistrictId];
  }
  // 构建客户联系地址数组
  if (form.value.cityId && form.value.districtId && form.value.provinceId) {
    form.value.cids = [row.provinceId, row.cityId, row.districtId];
  }
  addOrEdit.value = "edit";
  handleAddEdit(tabName.value);
};
@@ -597,7 +604,7 @@
  }
  const deleteIds = selectedRows.value.map(item => item.id);
  try {
    await ElMessageBox.confirm("确定删除选中的数据吗?", "提示", {
      confirmButtonText: "确定",
@@ -608,7 +615,9 @@
    const deleteApiMap = {
      supplier: delSupply,
      coal: delCoalInfo,
      coalQualityMaintenance: () => { throw new Error('delCoalQuality API not imported'); },
      coalQualityMaintenance: () => {
        throw new Error('delCoalQuality API not imported');
      },
      customer: delCustomer,
      coalMeiZhiZiDuanWeiHu: deleteCoalField
    };
@@ -620,12 +629,12 @@
    }
    console.log(deleteIds)
    const res = await deleteApi(deleteIds);
    if (res.code !== 200 && res.msg !== "操作成功") {
      ElMessage.error("删除失败:" + res.msg);
      return;
    }
    ElMessage.success("删除成功");
    await getList();
  } catch (error) {
@@ -653,10 +662,10 @@
 */
const handleExport = () => {
  const exportConfig = {
    supplier: { api: "/supply/export", name: "供应商信息" },
    customer: { api: "/customer/export", name: "客户信息" },
    coal: { api: "/supply/export", name: "煤种信息" },
    coalQualityMaintenance: { api: "/supply/export", name: "煤质维护信息" }
    supplier: {api: "/supply/export", name: "供应商信息"},
    customer: {api: "/customer/export", name: "客户信息"},
    coal: {api: "/supply/export", name: "煤种信息"},
    coalQualityMaintenance: {api: "/supply/export", name: "煤质维护信息"}
  };
  const config = exportConfig[tabName.value];
@@ -671,7 +680,7 @@
 * @param {string} name - 导出文件名前缀
 */
const exportData = (api, name) => {
  proxy.download(api, { ...queryParams }, `${name}${new Date().getTime()}.xlsx`);
  proxy.download(api, {...queryParams}, `${name}${new Date().getTime()}.xlsx`);
  ElMessage.success("导出数据:" + name);
};
// ===== 数据获取函数 =====
@@ -707,13 +716,13 @@
const getList = async () => {
  try {
    loading.value = true;
    const { data, code } = await selectInterface();
    const {data, code} = await selectInterface();
    if (code !== 200) {
      ElMessage.error("获取数据失败:" + (data?.msg || '未知错误'));
      return;
    }
    tableData.value = data.records || [];
    total.value = data.total || 0;
  } catch (error) {
@@ -733,7 +742,7 @@
  try {
    // 并行执行初始化操作
    await Promise.all([
      handleTabClick({ props: { name: "supplier" } }),
      handleTabClick({props: {name: "supplier"}}),
      fetchAreaOptions(),
      getUserList()
    ]);
@@ -778,7 +787,7 @@
/* 表格工具栏 */
.table-toolbar,
.table-toolbar>* {
.table-toolbar > * {
  margin: 0 0 0 0 !important;
}
src/views/basicInformation/mould/coal.vue
@@ -1,73 +1,77 @@
<template>
    <div>
        <el-dialog
            v-model="dialogVisible"
            :title="title"
            width="500"
            :close-on-click-modal="false"
            :before-close="handleClose"
        >            <el-form
                ref="formRef"
                style="max-width: 600px; margin: 0 auto"
                :model="formData"
                :rules="rules"
                label-width="120px"
            >
                <el-form-item label="煤种名称" prop="coal">
                    <el-input
                        v-model="formData.coal"
                        placeholder="请输入煤种名称"
                    />
                </el-form-item>                <el-form-item label="维护人姓名" prop="maintainerId">
                    <el-input
                        :value="userStore.name || ''"
                        placeholder="维护人姓名"
                        disabled
                    />
                </el-form-item>                <el-form-item label="维护日期" prop="maintenanceDate">
                    <el-input
                        :value="getCurrentDate()"
                        placeholder="维护日期"
                        disabled
                    />
                </el-form-item>
                <el-form-item class="dialog-footer">
                    <el-button v-if="addOrEdit === 'edit'" @click="resetForm">重置</el-button>
                    <el-button v-if="addOrEdit === 'add'" @click="cancelForm">取消</el-button>
                    <el-button type="primary" @click="submitForm">
                        确定
                    </el-button>
                </el-form-item>
            </el-form>
        </el-dialog>
    </div>
  <div>
    <el-dialog
        v-model="dialogVisible"
        :title="title"
        width="500"
        :close-on-click-modal="false"
        :before-close="handleClose"
    >
      <el-form
          ref="formRef"
          style="max-width: 600px; margin: 0 auto"
          :model="formData"
          :rules="rules"
          label-width="120px"
      >
        <el-form-item label="煤种名称" prop="coal">
          <el-input
              v-model="formData.coal"
              placeholder="请输入煤种名称"
          />
        </el-form-item>
        <el-form-item label="维护人姓名" prop="maintainerId">
          <el-input
              :value="userStore.name || ''"
              placeholder="维护人姓名"
              disabled
          />
        </el-form-item>
        <el-form-item label="维护日期" prop="maintenanceDate">
          <el-input
              :value="getCurrentDate()"
              placeholder="维护日期"
              disabled
          />
        </el-form-item>
        <el-form-item class="dialog-footer">
          <el-button v-if="addOrEdit === 'edit'" @click="resetForm">重置</el-button>
          <el-button v-if="addOrEdit === 'add'" @click="cancelForm">取消</el-button>
          <el-button type="primary" @click="submitForm">
            确定
          </el-button>
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, watch, defineProps, reactive, onMounted } from 'vue'
import { addOrEditCoalInfo } from '@/api/basicInformation/coal'
import {ref, watch, defineProps, reactive, onMounted} from 'vue'
import {addOrEditCoalInfo} from '@/api/basicInformation/coal'
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
const props = defineProps({
    beforeClose: {
        type: Function,
        default: () => {}
    },
    form: {
        type: Object,
        default: () => ({})
    },
    addOrEdit: {
        type: String,
        default: 'add'
    },
    title: {
        type: String,
        default: ''
    },
  beforeClose: {
    type: Function,
    default: () => {
    }
  },
  form: {
    type: Object,
    default: () => ({})
  },
  addOrEdit: {
    type: String,
    default: 'add'
  },
  title: {
    type: String,
    default: ''
  },
})
const copyForm = defineModel("copyForm", {
  required: true,
@@ -75,99 +79,100 @@
});
// 在组件挂载时获取用户信息
onMounted(async () => {
    // 如果store中没有用户信息,则获取用户信息
    if (!userStore.name) {
        try {
            await userStore.getInfo()
            // 自动填充维护人ID
            if (props.addOrEdit === 'add') {
                formData.value.maintainerId = userStore.id
            }
        } catch (error) {
            console.error('获取用户信息失败:', error)
        }
    } else {
        // 自动填充维护人ID
        if (props.addOrEdit === 'add') {
            formData.value.maintainerId = userStore.id
        }
  // 如果store中没有用户信息,则获取用户信息
  if (!userStore.name) {
    try {
      await userStore.getInfo()
      // 自动填充维护人ID
      if (props.addOrEdit === 'add') {
        formData.value.maintainerId = userStore.id
      }
    } catch (error) {
      console.error('获取用户信息失败:', error)
    }
  } else {
    // 自动填充维护人ID
    if (props.addOrEdit === 'add') {
      formData.value.maintainerId = userStore.id
    }
  }
})
const emit = defineEmits(['submit', 'handleBeforeClose','update:coalDialogFormVisible'])
const emit = defineEmits(['submit', 'handleBeforeClose', 'update:coalDialogFormVisible'])
// 表单引用
const formRef = ref(null)
// 表单数据
const formData = ref({ ...props.form })
const formData = ref({...props.form})
// 弹窗可见性
const dialogVisible = defineModel("coalDialogFormVisible",{required:true,type:Boolean})
const dialogVisible = defineModel("coalDialogFormVisible", {required: true, type: Boolean})
// 监听外部传入的表单数据变化
watch(() => props.form, (newVal) => {
    formData.value = { ...newVal }
    // 如果是新增模式,设置维护人
    if (props.addOrEdit === 'add' && userStore.id) {
        formData.value.maintainerId = userStore.id
    }
}, { deep: true })
  formData.value = {...newVal}
  // 如果是新增模式,设置维护人
  if (props.addOrEdit === 'add' && userStore.id) {
    formData.value.maintainerId = userStore.id
  }
}, {deep: true})
// 监听内部弹窗状态变化
watch(() => dialogVisible.value, (newVal) => {
    emit('update:coalDialogFormVisible', newVal)
  emit('update:coalDialogFormVisible', newVal)
})
// 提交表单
const submitForm = async () => {
    if (!formRef.value) return
    await formRef.value.validate(async (valid, fields) => {
        if (valid) {
            delete formData.value.maintainerName // 删除显示用的字段,只保留ID
            // 确保maintainerId有值
            if (!formData.value.maintainerId) {
                formData.value.maintainerId = userStore.id
            }
            // 设置维护日期
            formData.value.maintenanceDate = getCurrentDate()
            let result = await addOrEditCoalInfo({
                ...formData.value,
            })
            let obj = {
                title: props.title,
                result,
            }
            emit('submit', obj)
        }
    })
  if (!formRef.value) return
  await formRef.value.validate(async (valid, fields) => {
    if (valid) {
      delete formData.value.maintainerName // 删除显示用的字段,只保留ID
      // 确保maintainerId有值
      if (!formData.value.maintainerId) {
        formData.value.maintainerId = userStore.id
      }
      // 设置维护日期
      formData.value.maintenanceDate = getCurrentDate()
      let result = await addOrEditCoalInfo({
        ...formData.value,
      })
      let obj = {
        title: props.title,
        result,
      }
      emit('submit', obj)
    }
  })
}
// 取消表单
const cancelForm = () => {
    emit('update:coalDialogFormVisible', false)
    formData.value = {}
  emit('update:coalDialogFormVisible', false)
  formData.value = {}
}
// 重置表单
const resetForm = () => {
    if (!formRef.value) return
  if (!formRef.value) return
  formData.value = JSON.parse(JSON.stringify(copyForm.value));
    // formRef.value.resetFields()
  // formRef.value.resetFields()
}
// 关闭弹窗
const handleClose = () => {
    // 触发父组件的关闭函数
    emit("handleBeforeClose")
    emit('update:coalDialogFormVisible', false)
  // 触发父组件的关闭函数
  emit("handleBeforeClose")
  emit('update:coalDialogFormVisible', false)
}
const rules = reactive({
  supplierName: [
    { required: true, message: "请输入供货商名称", trigger: "blur" },
    {required: true, message: "请输入供货商名称", trigger: "blur"},
  ],
  identifyNumber: [
    { required: true, message: "请正确输入纳税人识别号", trigger: "blur" },
    { min: 17, max: 20, message: "请输入17-20位纳税人识别号", trigger: "blur" },
    {required: true, message: "请正确输入纳税人识别号", trigger: "blur"},
    {min: 17, max: 20, message: "请输入17-20位纳税人识别号", trigger: "blur"},
  ],
});
// 获取当前日期并格式化为 YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
@@ -180,9 +185,9 @@
<style lang="scss" scoped>
.dialog-footer {
    display: flex;
    margin-top: 20px;
    flex-direction: column;
    align-items: flex-end;
  display: flex;
  margin-top: 20px;
  flex-direction: column;
  align-items: flex-end;
}
</style>
src/views/basicInformation/mould/coalMeiZhiZiDuanWeiHu.vue
@@ -1,35 +1,38 @@
<template>
  <div>
    <el-dialog
      v-model="dialogVisible"
      :title="title"
      width="600"
      :close-on-click-modal="false"
      :before-close="handleClose"
        v-model="dialogVisible"
        :title="title"
        width="600"
        :close-on-click-modal="false"
        :before-close="handleClose"
    >
      <el-form
        ref="formRef"
        style="max-width: 400px; margin: 0 auto"
        :model="formData"
        :rules="rules"
        label-width="auto"
          ref="formRef"
          style="max-width: 400px; margin: 0 auto"
          :model="formData"
          :rules="rules"
          label-width="auto"
      >
        <el-form-item label="字段名称" prop="fieldName">
          <el-input
            v-model="formData.fieldName"
            placeholder="请输入字段名称"
              v-model="formData.fieldName"
              placeholder="请输入字段名称"
          />
        </el-form-item>        <el-form-item label="字段描述" prop="fieldDescription">
          <el-input v-model="formData.fieldDescription" type="textarea" placeholder="请输入字段描述" />
        </el-form-item>
        <el-form-item label="字段描述" prop="fieldDescription">
          <el-input v-model="formData.fieldDescription" type="textarea" placeholder="请输入字段描述"/>
        </el-form-item>
        <el-form-item class="dialog-footer">
          <el-button v-if="addOrEdit === 'edit'" @click="resetForm"
            >重置</el-button
          >重置
          </el-button
          >
          <el-button v-if="addOrEdit === 'add'" @click="cancelForm"
            >取消</el-button
          >取消
          </el-button
          >
          <el-button type="primary" @click="submitForm"> 确定 </el-button>
          <el-button type="primary" @click="submitForm"> 确定</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>
@@ -37,8 +40,9 @@
</template>
<script setup>
import { ref, reactive, watch, defineProps } from "vue";
import { addOrEditCoalField } from "@/api/basicInformation/coalFieldMaintenance.js";
import {ref, reactive, watch, defineProps} from "vue";
import {addOrEditCoalField} from "@/api/basicInformation/coalFieldMaintenance.js";
const props = defineProps({
  form: {
    type: Object,
@@ -61,12 +65,12 @@
const formRef = ref();
const emit = defineEmits(["submit", "handleBeforeClose"]);
// 表单数据
const formData = ref({ ...props.form });
const formData = ref({...props.form});
// 监听props.form的变化,更新formData
watch(() => props.form, (newForm) => {
  formData.value = { ...newForm };
}, { deep: true, immediate: true });
  formData.value = {...newForm};
}, {deep: true, immediate: true});
// 弹窗可见性
const dialogVisible = defineModel("coalMaintenanceFieldDialogVisible", {
  required: true,
@@ -82,8 +86,8 @@
      console.log("提交表单", formData.value);
      if (props.title.includes('新增')) {
        let result = await addOrEditCoalField(
         {...formData.value}
          ,)
            {...formData.value}
            ,)
        console.log(result);
        obj.value = {
          title: "新增",
@@ -111,7 +115,7 @@
};
// 重置表单
const resetForm = () => {
    if (!formRef.value) return
  if (!formRef.value) return
  formData.value = JSON.parse(JSON.stringify(copyForm.value));
}
// 关闭弹窗
@@ -122,7 +126,7 @@
};
const rules = reactive({
  fieldName: [
    { required: true, message: "请输入煤种名称", trigger: "blur" },
    {required: true, message: "请输入煤种名称", trigger: "blur"},
  ],
});
</script>
src/views/basicInformation/mould/coalQualityMaintenance.vue
@@ -1,25 +1,26 @@
<!-- 煤质方案维护弹窗组件 -->
<template>
  <el-dialog v-model="dialogVisible" :title="title" width="600" :close-on-click-modal="false"
    :before-close="handleClose">
             :before-close="handleClose">
    <!-- 表单区域 -->
    <el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px"
      style="max-width: 400px; margin: 0 auto">
             style="max-width: 400px; margin: 0 auto">
      <!-- 方案名称输入框 -->
      <el-form-item label="煤质方案名称" prop="plan">
        <el-input v-model="formData.plan" placeholder="请输入煤质方案名称" clearable />
        <el-input v-model="formData.plan" placeholder="请输入煤质方案名称" clearable/>
      </el-form-item>
      <!-- 煤质字段多选下拉框 -->
      <el-form-item label="煤质方案类型" prop="coalFieldList">
        <el-select v-model="formData.coalFieldList" placeholder="请选择煤质方案类型" style="width: 100%" clearable multiple>
          <el-option v-for="item in fieldOptions" :key="item.id" :label="item.label" :value="item" />
        <el-select v-model="formData.coalFieldList" placeholder="请选择煤质方案类型" style="width: 100%" clearable
                   multiple>
          <el-option v-for="item in fieldOptions" :key="item.id" :label="item.label" :value="item"/>
        </el-select>
      </el-form-item>
      <!-- 方案描述文本域 -->
      <el-form-item label="煤质方案描述" prop="schemeDesc">
        <el-input v-model="formData.schemeDesc" type="textarea" placeholder="请输入煤质方案描述" :rows="3" />
        <el-input v-model="formData.schemeDesc" type="textarea" placeholder="请输入煤质方案描述" :rows="3"/>
      </el-form-item>
      <!-- 操作按钮区域 -->
@@ -32,15 +33,16 @@
  </el-dialog>
</template>
<script setup>
import { ref, reactive, watch, computed, onMounted } from "vue";
import { getCoalFieldList, addOrEditCoalPlan } from "@/api/basicInformation/coalQualityMaintenance";
import {ref, reactive, watch, computed, onMounted} from "vue";
import {getCoalFieldList, addOrEditCoalPlan} from "@/api/basicInformation/coalQualityMaintenance";
// ===== 组件属性定义 =====
const props = defineProps({
  /** 关闭弹窗前的回调函数 */
  beforeClose: {
    type: Function,
    default: () => { },
    default: () => {
    },
  },
  /** 表单数据 */
  form: {
@@ -58,7 +60,10 @@
    default: "",
  },
});
const copyForm = defineModel("copyForm", {
  required: true,
  type: Object,
});
// ===== 事件定义 =====
const emit = defineEmits(["submit", "handleBeforeClose"]);
@@ -85,10 +90,10 @@
// ===== 表单验证规则 =====
const formRules = reactive({
  plan: [
    { required: true, message: "请输入方案名称", trigger: "blur" },
    {required: true, message: "请输入方案名称", trigger: "blur"},
  ],
  coalFieldList: [
    { required: true, message: "请选择方案类型", trigger: "blur" },
    {required: true, message: "请选择方案类型", trigger: "blur"},
  ],
});
@@ -99,9 +104,9 @@
const parseCoalFields = (coalFieldsStr) => {
  if (!coalFieldsStr || typeof coalFieldsStr !== 'string') return [];
  return coalFieldsStr
    .split(',')
    .map(id => parseInt(id.trim()))
    .filter(id => !isNaN(id));
      .split(',')
      .map(id => parseInt(id.trim()))
      .filter(id => !isNaN(id));
};
/**
@@ -117,7 +122,7 @@
 * @param {Object} newForm - 新的表单数据
 */
const initFormData = (newForm) => {
  formData.value = { ...newForm };
  formData.value = {...newForm};
  // 处理 coalFieldList 字段:编辑时需要将字符串转换为数组供多选组件使用
  if (newForm.fieldIds) {
    if (typeof newForm.fieldIds === 'string') {
@@ -125,7 +130,7 @@
      const ids = parseCoalFields(newForm.fieldIds);
      formData.value.coalFieldList = ids.map(id => {
        const option = fieldOptions.find(opt => opt.value === id);
        return option || { fields: `字段${id}`, value: id };
        return option || {fields: `字段${id}`, value: id};
      });
    } else if (Array.isArray(newForm.coalFieldList)) {
      // 确保数组中的值都是正确的对象格式
@@ -136,7 +141,7 @@
          // 如果是纯ID,需要匹配对应的选项
          const id = parseInt(item);
          const option = fieldOptions.find(opt => opt.value === id);
          return option || { fields: `字段${id}`, value: id };
          return option || {fields: `字段${id}`, value: id};
        }
      });
    }
@@ -152,13 +157,13 @@
 */
onMounted(async () => {
  try {
    const { data, code } = await getCoalFieldList();
    const {data, code} = await getCoalFieldList();
    if (code === 200) {
      // 构建选项数据,格式化为{ label, value }
      fieldOptions.push(...data.map(item => ({
        label: item.fieldName,
        value: item.id,
        fields:item.fields
        fields: item.fields
      })));
    } else {
      console.error("获取煤质字段列表失败", data);
@@ -248,8 +253,8 @@
 * 重置表单
 */
const resetForm = () => {
  if (!formRef.value) return;
  formRef.value.resetFields();
  formData.value = JSON.parse(JSON.stringify(copyForm.value));
  initFormData(formData.value);
};
/**
src/views/basicInformation/mould/customer.vue
@@ -1,44 +1,47 @@
<template>
  <div>
    <el-dialog v-model="dialogVisible" :title="title" width="600" :close-on-click-modal="false"
      :before-close="handleClose">
      <el-form ref="formRef" style="max-width: 400px; margin: 0 auto" :model="formData" :rules="rules" label-width="auto">
               :before-close="handleClose">
      <el-form ref="formRef" style="max-width: 400px; margin: 0 auto" :model="formData" :rules="rules"
               label-width="auto">
        <el-form-item label="客户名称" prop="customerName">
          <el-input v-model="formData.customerName" placeholder="请输入客户名称" />
          <el-input v-model="formData.customerName" placeholder="请输入客户名称"/>
        </el-form-item>
        <el-form-item label="纳税人识别号" prop="taxpayerId">
          <el-input v-model="formData.taxpayerId" placeholder="请输入纳税人识别号" />
          <el-input v-model="formData.taxpayerId" placeholder="请输入纳税人识别号"/>
        </el-form-item>
        <el-form-item label="经营地址" prop="bids">
          <el-cascader placeholder="请选择经营地址" size="default" :options="addressSelectOptions" v-model="formData.bids"
           :props="cascaderProps" @change="handleChange">
          <el-cascader placeholder="请选择经营地址" size="default" :options="addressSelectOptions"
                       v-model="formData.bids"
                       :props="cascaderProps" @change="handleChange">
          </el-cascader>
        </el-form-item>
        <el-form-item label="经营详细地址" prop="businessAddress">
          <el-input v-model="formData.businessAddress" placeholder="请输入经营详细地址" />
          <el-input v-model="formData.businessAddress" placeholder="请输入经营详细地址"/>
        </el-form-item>
        <el-form-item label="开户行" prop="bankName">
          <el-input v-model="formData.bankName" placeholder="请输入开户行" />
          <el-input v-model="formData.bankName" placeholder="请输入开户行"/>
        </el-form-item>
        <el-form-item label="银行账户" prop="bankAccount">
          <el-input v-model="formData.bankAccount" placeholder="请输入银行账户" />
          <el-input v-model="formData.bankAccount" placeholder="请输入银行账户"/>
        </el-form-item>
        <el-form-item label="联系人" prop="contactPerson">
          <el-input v-model="formData.contactPerson" placeholder="请输入联系人" />
          <el-input v-model="formData.contactPerson" placeholder="请输入联系人"/>
        </el-form-item>
        <el-form-item label="联系电话" prop="contactPhone">
          <el-input v-model="formData.contactPhone" placeholder="请输入联系电话" />
          <el-input v-model="formData.contactPhone" placeholder="请输入联系电话"/>
        </el-form-item>
        <el-form-item label="联系人地址" prop="cids">
          <el-cascader placeholder="请选择联系人地址" size="default" :options="addressSelectOptions" v-model="formData.cids"
            :props="cascaderProps" @change="handleChange">
          <el-cascader placeholder="请选择联系人地址" size="default" :options="addressSelectOptions"
                       v-model="formData.cids"
                       :props="cascaderProps" @change="handleChange">
          </el-cascader>
        </el-form-item>
        <el-form-item label="联系人详细" prop="contactAddress">
          <el-input v-model="formData.contactAddress" placeholder="请输入联系人详细地址" />
          <el-input v-model="formData.contactAddress" placeholder="请输入联系人详细地址"/>
        </el-form-item>
        <el-form-item class="dialog-footer">
        <el-form-item class="dialog-footer">
          <el-button v-if="addOrEdit === 'edit'" @click="resetForm">重置</el-button>
          <el-button v-if="addOrEdit === 'add'" @click="cancelForm">取消</el-button>
          <el-button type="primary" @click="submitForm">
@@ -51,15 +54,16 @@
</template>
<script setup>
import { ref, watch, onMounted } from "vue";
import { getAreaOptions } from "@/api/system/area.js";
import {ref, watch, onMounted} from "vue";
import {getAreaOptions} from "@/api/system/area.js";
import addressList from "@/api/jsonApi/areaList.json";
import { addOrEditCustomer } from "@/api/basicInformation/customer";
import {addOrEditCustomer} from "@/api/basicInformation/customer";
const props = defineProps({
  beforeClose: {
    type: Function,
    default: () => { },
    default: () => {
    },
  },
  form: {
    type: Object,
@@ -111,7 +115,7 @@
// 表单引用
const formRef = ref(null);
// 表单数据
const formData = ref({ ...props.form });
const formData = ref({...props.form});
// 弹窗可见性
const dialogVisible = defineModel("customerDialogFormVisible", {
  required: true,
@@ -120,26 +124,26 @@
// 监听外部传入的表单数据变化
watch(
  () => props.form,
  (newVal) => {
    formData.value = { ...newVal };
  },
  { deep: true }
    () => props.form,
    (newVal) => {
      formData.value = {...newVal};
    },
    {deep: true}
);
watch(
  () => props.form,
  (newVal) => {
    formData.value = { ...newVal };
  },
  { deep: true }
    () => props.form,
    (newVal) => {
      formData.value = {...newVal};
    },
    {deep: true}
);
// 监听内部弹窗状态变化
watch(
  () => dialogVisible.value,
  (newVal) => {
    emit("update:customerDialogFormVisible", newVal);
  }
    () => dialogVisible.value,
    (newVal) => {
      emit("update:customerDialogFormVisible", newVal);
    }
);
// 提交表单
@@ -191,11 +195,11 @@
};
const rules = reactive({
  customerName: [
    { required: true, message: "请输入供货商名称", trigger: "blur" },
    {required: true, message: "请输入供货商名称", trigger: "blur"},
  ],
  taxpayerId: [
    { required: true, message: "请正确输入纳税人识别号", trigger: "blur" },
    { min: 17, max: 20, message: "请输入17-20位纳税人识别号", trigger: "blur" },
    {required: true, message: "请正确输入纳税人识别号", trigger: "blur"},
    {min: 17, max: 20, message: "请输入17-20位纳税人识别号", trigger: "blur"},
  ],
  address: [
    {
@@ -204,14 +208,14 @@
      trigger: "change",
    },
  ],
  bankAccount: [{ required: true, message: "请输入银行账户", trigger: "blur" }],
  bankName: [{ required: true, message: "请输入开户行", trigger: "blur" }],
  contactPerson: [{ required: true, message: "请输入开户行", trigger: "blur" }],
  cids: [{ required: true, message: "请输入开户行", trigger: "blur" }],
  bids: [{ required: true, message: "请输入开户行", trigger: "blur" }],
  bankAccount: [{required: true, message: "请输入银行账户", trigger: "blur"}],
  bankName: [{required: true, message: "请输入开户行", trigger: "blur"}],
  contactPerson: [{required: true, message: "请输入开户行", trigger: "blur"}],
  cids: [{required: true, message: "请输入开户行", trigger: "blur"}],
  bids: [{required: true, message: "请输入开户行", trigger: "blur"}],
  contactPhone: [
    { required: true, message: "请输入联系人", trigger: "blur" },
    { min: 11, max: 11, message: "请输入11位联系人电话", trigger: "blur" },
    {required: true, message: "请输入联系人", trigger: "blur"},
    {min: 11, max: 11, message: "请输入11位联系人电话", trigger: "blur"},
  ],
});
</script>
src/views/basicInformation/mould/supplier.vue
@@ -1,41 +1,44 @@
<template>
  <div>
    <el-dialog v-model="dialogVisible" :title="title" width="600" :close-on-click-modal="false"
      :before-close="handleClose">
      <el-form ref="formRef" style="max-width: 400px; margin: 0 auto" :model="formData" :rules="rules" label-width="auto">
               :before-close="handleClose">
      <el-form ref="formRef" style="max-width: 400px; margin: 0 auto" :model="formData" :rules="rules"
               label-width="auto">
        <el-form-item label="供应商名称" prop="supplierName">
          <el-input v-model="formData.supplierName" placeholder="请输入供货商名称" />
          <el-input v-model="formData.supplierName" placeholder="请输入供货商名称"/>
        </el-form-item>
        <el-form-item label="纳税人识别号" prop="taxpayerId">
          <el-input v-model="formData.taxpayerId" placeholder="请输入纳税人识别号" />
          <el-input v-model="formData.taxpayerId" placeholder="请输入纳税人识别号"/>
        </el-form-item>
        <el-form-item label="经营地址" prop="bids">
          <el-cascader placeholder="请选择经营地址" size="default" :options="addressSelectOptions" v-model="formData.bids"
            :props="cascaderProps" @change="handleChange">
          <el-cascader placeholder="请选择经营地址" size="default" :options="addressSelectOptions"
                       v-model="formData.bids"
                       :props="cascaderProps" @change="handleChange">
          </el-cascader>
        </el-form-item>
        <el-form-item label="详细地址" prop="businessAddress">
          <el-input v-model="formData.businessAddress" placeholder="请输入客户详细地址" />
          <el-input v-model="formData.businessAddress" placeholder="请输入客户详细地址"/>
        </el-form-item>
        <el-form-item label="开户行" prop="bankAccount">
          <el-input v-model="formData.bankAccount" placeholder="请输入开户行" />
          <el-input v-model="formData.bankAccount" placeholder="请输入开户行"/>
        </el-form-item>
        <el-form-item label="银行账户" prop="bankName">
          <el-input v-model="formData.bankName" placeholder="请输入银行账户" />
          <el-input v-model="formData.bankName" placeholder="请输入银行账户"/>
        </el-form-item>
        <el-form-item label="联系人" prop="contactPerson">
          <el-input v-model="formData.contactPerson" placeholder="请输入联系人" />
          <el-input v-model="formData.contactPerson" placeholder="请输入联系人"/>
        </el-form-item>
        <el-form-item label="联系人电话" prop="contactPhone">
          <el-input v-model="formData.contactPhone" placeholder="请输入联系人电话" />
          <el-input v-model="formData.contactPhone" placeholder="请输入联系人电话"/>
        </el-form-item>
        <el-form-item label="联系人地址" prop="cids">
          <el-cascader placeholder="请选择联系人地址" size="default" :options="addressSelectOptions" v-model="formData.cids"
            :props="cascaderProps" @change="handleChange">
          <el-cascader placeholder="请选择联系人地址" size="default" :options="addressSelectOptions"
                       v-model="formData.cids"
                       :props="cascaderProps" @change="handleChange">
          </el-cascader>
        </el-form-item>
        <el-form-item label="联系人详细地址" prop="contactAddress">
          <el-input v-model="formData.contactAddress" placeholder="请输入联系人地址" />
          <el-input v-model="formData.contactAddress" placeholder="请输入联系人地址"/>
        </el-form-item>
        <el-form-item class="dialog-footer">
          <el-button v-if="addOrEdit === 'edit'" @click="resetForm">重置</el-button>
@@ -48,9 +51,9 @@
</template>
<script setup>
import { ref, watch, defineProps, onMounted } from "vue";
import { addOrEditSupply } from "@/api/basicInformation/supplier";
import { getAreaOptions } from "@/api/system/area.js";
import {ref, watch, defineProps, onMounted} from "vue";
import {addOrEditSupply} from "@/api/basicInformation/supplier";
import {getAreaOptions} from "@/api/system/area.js";
const props = defineProps({
  beforeClose: {
@@ -111,7 +114,7 @@
// 表单引用
const formRef = ref(null);
// 表单数据
const formData = ref({ ...props.form });
const formData = ref({...props.form});
// 弹窗可见性
const dialogVisible = defineModel("supplierDialogFormVisible", {
  required: true,
@@ -119,19 +122,19 @@
});
// 监听外部传入的表单数据变化
watch(
  () => props.form,
  (newVal) => {
    formData.value = { ...newVal };
  },
  { deep: true }
    () => props.form,
    (newVal) => {
      formData.value = {...newVal};
    },
    {deep: true}
);
// 监听内部弹窗状态变化
watch(
  () => dialogVisible.value,
  (newVal) => {
    emit("update:supplierDialogFormVisible", newVal);
  }
    () => dialogVisible.value,
    (newVal) => {
      emit("update:supplierDialogFormVisible", newVal);
    }
);
// 处理地址选择变化
const handleChange = (value) => {
@@ -175,7 +178,6 @@
const resetForm = () => {
  if (!formRef.value) return;
  formData.value = JSON.parse(JSON.stringify(copyForm.value));
  // formRef.value.resetFields();
};
// 关闭弹窗
const handleClose = () => {
@@ -185,11 +187,11 @@
};
const rules = reactive({
  supplierName: [
    { required: true, message: "请输入供货商名称", trigger: "blur" },
    {required: true, message: "请输入供货商名称", trigger: "blur"},
  ],
  taxpayerId: [
    { required: true, message: "请正确输入纳税人识别号", trigger: "blur" },
    { min: 17, max: 20, message: "请输入17-20位纳税人识别号", trigger: "blur" },
    {required: true, message: "请正确输入纳税人识别号", trigger: "blur"},
    {min: 17, max: 20, message: "请输入17-20位纳税人识别号", trigger: "blur"},
  ],
  // bids: [
  //   {
@@ -198,12 +200,12 @@
  //     trigger: "change",
  //   },
  // ],
  bankName: [{ required: true, message: "请输入银行账户", trigger: "blur" }],
  bankAccount: [{ required: true, message: "请输入开户行", trigger: "blur" }],
  contactPerson: [{ required: true, message: "联系人", trigger: "blur" }],
  bankName: [{required: true, message: "请输入银行账户", trigger: "blur"}],
  bankAccount: [{required: true, message: "请输入开户行", trigger: "blur"}],
  contactPerson: [{required: true, message: "联系人", trigger: "blur"}],
  contactPhone: [
    { required: true, message: "请输入联系人", trigger: "blur" },
    { min: 11, max: 11, message: "请输入11位联系人电话", trigger: "blur" },
    {required: true, message: "请输入联系人", trigger: "blur"},
    {min: 11, max: 11, message: "请输入11位联系人电话", trigger: "blur"},
  ],
});
</script>
src/views/index.vue
@@ -1,14 +1,550 @@
<template>
  <div class="app-container">
  <div class="dashboard">
    <!-- 顶部统计卡片 -->
    <div class="top-cards">
      <div class="stat-card revenue">
        <div class="card-icon">
          <i class="el-icon-money"></i>
        </div>
        <div class="card-content">
          <div class="card-title">营收金额</div>
          <div class="card-value">¥1,234,567</div>
          <div class="card-trend">
            <span class="trend-label">较昨日</span>
            <span class="trend-value up">+12.5%</span>
          </div>
        </div>
      </div>
      <div class="stat-card supply">
        <div class="card-icon">
          <i class="el-icon-truck"></i>
        </div>
        <div class="card-content">
          <div class="card-title">供应量</div>
          <div class="card-value">8,965 吨</div>
          <div class="card-trend">
            <span class="trend-label">较昨日</span>
            <span class="trend-value up">+8.2%</span>
          </div>
        </div>
      </div>
    </div>
    <!-- 中间图表区域 -->
    <div class="chart-section">
      <div class="chart-container">
        <div class="chart-title">营收分布</div>
        <div ref="pieChart" class="chart-content pie-chart"></div>
      </div>
      <div class="chart-container">
        <div class="chart-title">供应量趋势</div>
        <div ref="areaChart" class="chart-content area-chart"></div>
      </div>
    </div>
    <!-- 底部三栏布局 -->
    <div class="bottom-section">
      <!-- 库存统计 -->
      <div class="bottom-card inventory">
        <div class="card-header">
          <h3>库存统计</h3>
        </div>
        <div class="inventory-items">
          <div class="inventory-item">
            <div class="item-name">原煤</div>
            <div class="item-value">15,432 吨</div>
            <div class="item-status normal">正常</div>
          </div>
          <div class="inventory-item">
            <div class="item-name">精煤</div>
            <div class="item-value">8,765 吨</div>
            <div class="item-status normal">正常</div>
          </div>
          <div class="inventory-item">
            <div class="item-name">焦煤</div>
            <div class="item-value">3,241 吨</div>
            <div class="item-status low">偏低</div>
          </div>
          <div class="inventory-item">
            <div class="item-name">块煤</div>
            <div class="item-value">6,789 吨</div>
            <div class="item-status normal">正常</div>
          </div>
        </div>
      </div>
      <!-- 柱状图 -->
      <div class="bottom-card chart">
        <div class="card-header">
          <h3>月度对比</h3>
        </div>
        <div ref="barChart" class="chart-content bar-chart"></div>
      </div>
      <!-- 销售数据表格 -->
      <div class="bottom-card table">
        <div class="card-header">
          <h3>销售数据</h3>
        </div>
        <el-table
          :data="salesData"
          style="width: 100%"
          :header-cell-style="tableHeaderStyle"
        >
          <el-table-column prop="product" label="产品" width="80"></el-table-column>
          <el-table-column prop="quantity" label="数量" width="80"></el-table-column>
          <el-table-column prop="amount" label="金额" width="90"></el-table-column>
          <el-table-column prop="status" label="状态" width="70">
            <template #default="scope">
              <el-tag
                :type="scope.row.status === '已完成' ? 'success' : 'warning'"
                size="small"
              >
                {{ scope.row.status }}
              </el-tag>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </div>
  </div>
</template>
<script setup name="Index">
<script>
import * as echarts from 'echarts'
export default {
  name: 'Dashboard',
  data() {
    return {
      salesData: [
        { product: '原煤', quantity: '1,234吨', amount: '¥456,789', status: '已完成' },
        { product: '精煤', quantity: '567吨', amount: '¥234,567', status: '已完成' },
        { product: '焦煤', quantity: '890吨', amount: '¥345,678', status: '进行中' },
        { product: '块煤', quantity: '432吨', amount: '¥123,456', status: '已完成' },
        { product: '煤泥', quantity: '678吨', amount: '¥234,567', status: '进行中' }
      ],
      tableHeaderStyle: {
        backgroundColor: '#f5f7fa',
        color: '#606266',
        fontSize: '12px'
      }
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.initCharts()
    })
  },
  methods: {
    initCharts() {
      this.initPieChart()
      this.initAreaChart()
      this.initBarChart()
    },
    initPieChart() {
      const chart = echarts.init(this.$refs.pieChart)
      const option = {
        tooltip: {
          trigger: 'item',
          formatter: '{a} <br/>{b}: {c} ({d}%)'
        },
        legend: {
          orient: 'vertical',
          left: 'right',
          top: 'center',
          textStyle: {
            fontSize: 12
          }
        },
        series: [
          {
            name: '营收分布',
            type: 'pie',
            radius: ['30%', '70%'],
            center: ['40%', '50%'],
            avoidLabelOverlap: false,
            label: {
              show: false,
              position: 'center'
            },
            emphasis: {
              label: {
                show: true,
                fontSize: '16',
                fontWeight: 'bold'
              }
            },
            labelLine: {
              show: false
            },
            data: [
              { value: 335, name: '原煤', itemStyle: { color: '#409EFF' } },
              { value: 310, name: '精煤', itemStyle: { color: '#67C23A' } },
              { value: 234, name: '焦煤', itemStyle: { color: '#E6A23C' } },
              { value: 135, name: '块煤', itemStyle: { color: '#F56C6C' } },
              { value: 155, name: '其他', itemStyle: { color: '#909399' } }
            ]
          }
        ]
      }
      chart.setOption(option)
      // 响应式
      window.addEventListener('resize', () => {
        chart.resize()
      })
    },
    initAreaChart() {
      const chart = echarts.init(this.$refs.areaChart)
      const option = {
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'cross',
            label: {
              backgroundColor: '#6a7985'
            }
          }
        },
        legend: {
          data: ['供应量'],
          top: 10
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: [
          {
            type: 'category',
            boundaryGap: false,
            data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月'],
            axisLabel: {
              fontSize: 12
            }
          }
        ],
        yAxis: [
          {
            type: 'value',
            axisLabel: {
              fontSize: 12
            }
          }
        ],
        series: [
          {
            name: '供应量',
            type: 'line',
            stack: 'Total',
            areaStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: 'rgba(64, 158, 255, 0.3)' },
                { offset: 1, color: 'rgba(64, 158, 255, 0.1)' }
              ])
            },
            emphasis: {
              focus: 'series'
            },
            data: [1200, 1320, 1010, 1340, 900, 1230, 1100],
            lineStyle: {
              color: '#409EFF'
            },
            itemStyle: {
              color: '#409EFF'
            }
          }
        ]
      }
      chart.setOption(option)
      // 响应式
      window.addEventListener('resize', () => {
        chart.resize()
      })
    },
    initBarChart() {
      const chart = echarts.init(this.$refs.barChart)
      const option = {
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'shadow'
          }
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: {
          type: 'category',
          data: ['原煤', '精煤', '焦煤', '块煤', '煤泥'],
          axisLabel: {
            fontSize: 11
          }
        },
        yAxis: {
          type: 'value',
          axisLabel: {
            fontSize: 11
          }
        },
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [320, 302, 301, 334, 290],
            itemStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: '#409EFF' },
                { offset: 1, color: '#79bbff' }
              ])
            },
            barWidth: '60%'
          }
        ]
      }
      chart.setOption(option)
      // 响应式
      window.addEventListener('resize', () => {
        chart.resize()
      })
    }
  }
}
</script>
<style scoped lang="scss">
<style scoped>
.dashboard {
  padding: 20px;
  background-color: #f5f7fa;
  min-height: 100vh;
}
/* 顶部统计卡片 */
.top-cards {
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
}
.stat-card {
  flex: 1;
  background: white;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  display: flex;
  align-items: center;
  gap: 15px;
}
.card-icon {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  color: white;
}
.revenue .card-icon {
  background: linear-gradient(135deg, #409EFF, #79bbff);
}
.supply .card-icon {
  background: linear-gradient(135deg, #67C23A, #95d475);
}
.card-content {
  flex: 1;
}
.card-title {
  font-size: 14px;
  color: #909399;
  margin-bottom: 8px;
}
.card-value {
  font-size: 24px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 5px;
}
.card-trend {
  font-size: 12px;
}
.trend-label {
  color: #909399;
  margin-right: 5px;
}
.trend-value.up {
  color: #67C23A;
}
/* 中间图表区域 */
.chart-section {
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
}
.el-scrollbar__view{
  width: 100%;
}
.chart-container {
  flex: 1;
  background: white;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.chart-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 15px;
  padding-bottom: 10px;
  border-bottom: 2px solid #f0f0f0;
}
.chart-content {
  height: 280px;
}
/* 底部三栏布局 */
.bottom-section {
  display: flex;
  gap: 20px;
}
.bottom-card {
  flex: 1;
  background: white;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card-header {
  margin-bottom: 15px;
  padding-bottom: 10px;
  border-bottom: 2px solid #f0f0f0;
}
.card-header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: bold;
  color: #303133;
}
/* 库存统计样式 */
.inventory-items {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.inventory-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px;
  background: #f8f9fa;
  border-radius: 6px;
  border-left: 3px solid #409EFF;
}
.item-name {
  font-weight: bold;
  color: #303133;
}
.item-value {
  color: #606266;
  font-size: 14px;
}
.item-status {
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: bold;
}
.item-status.normal {
  background: #f0f9ff;
  color: #67C23A;
}
.item-status.low {
  background: #fef0e6;
  color: #E6A23C;
}
/* 柱状图容器 */
.bar-chart {
  height: 200px;
}
/* 表格样式调整 */
.bottom-card.table {
  min-width: 320px;
}
.bottom-card.table .el-table {
  font-size: 12px;
}
.bottom-card.table .el-table td,
.bottom-card.table .el-table th {
  padding: 8px 0;
}
/* 响应式设计 */
@media (max-width: 1200px) {
  .bottom-section {
    flex-direction: column;
  }
  .chart-section {
    flex-direction: column;
  }
}
@media (max-width: 768px) {
  .top-cards {
    flex-direction: column;
  }
  .dashboard {
    padding: 10px;
  }
  .stat-card {
    padding: 15px;
  }
  .card-value {
    font-size: 20px;
  }
}
</style>
src/views/procureMent/components/ProductionDialog.vue
@@ -1,57 +1,48 @@
<template>
  <div>
    <el-dialog
      v-model="dialogFormVisible"
      :title="title"
      width="600"
      :close-on-click-modal="false"
      @close="handleClose"
        v-model="dialogFormVisible"
        :title="title"
        width="600"
        :close-on-click-modal="false"
        @close="handleClose"
    >
      <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="auto"
        class="production-form"
        label-position="right"
        style="max-width: 400px; margin: 0 auto"
          ref="formRef"
          :model="form"
          :rules="rules"
          label-width="auto"
          class="production-form"
          label-position="right"
          style="max-width: 400px; margin: 0 auto"
      >
        <el-form-item label="供应商名称" prop="supplierId">
          <el-select v-model="form.supplierId" placeholder="请选择供应商">
            <el-option :label="item.label" v-for="item in supplyList" :key="item.value" :value="item.value" />
            <el-option :label="item.label" v-for="item in supplyList" :key="item.value" :value="item.value"/>
          </el-select>
        </el-form-item>
        <el-form-item label="煤种" prop="coalId">
          <el-select v-model="form.coalId" placeholder="请选择煤种">
            <el-option :label="item.label" v-for="item in coalList" :key="item.value" :value="item.value" />
          </el-select>
        </el-form-item>
        <el-form-item label="单位" prop="unit">
          <el-select
            v-model="form.unit"
            placeholder="请选择单位"
            clearable
            style="width: 100%"
          >
            <el-option label="吨" value="吨" />
            <el-option label="千克" value="千克" />
            <el-option :label="item.label" v-for="item in coalList" :key="item.value" :value="item.value"/>
          </el-select>
        </el-form-item>
        <el-form-item label="采购数量" prop="purchaseQuantity">
          <el-input
            v-model.number="form.purchaseQuantity"
            placeholder="请输入"
            @blur="handleQuantityBlur"
              v-model.number="form.purchaseQuantity"
              placeholder="请输入"
              @blur="handleQuantityBlur"
          >
            <template v-slot:suffix>
              <i style="font-style: normal">{{ form.unit ? form.unit : "" }}</i>
              <i style="font-style: normal">吨</i>
            </template>
          </el-input> </el-form-item
        ><el-form-item label="税率" prop="taxRate">
          </el-input>
        </el-form-item
        >
        <el-form-item label="税率" prop="taxRate">
          <el-input
            v-model.number="form.taxRate"
            placeholder="请输入税率"
            @blur="handleTaxRateBlur"
              v-model.number="form.taxRate"
              placeholder="请输入税率"
              @blur="handleTaxRateBlur"
          >
            <template v-slot:suffix>
              <i style="font-style: normal">%</i>
@@ -60,9 +51,9 @@
        </el-form-item>
        <el-form-item label="单价(不含税)" prop="priceExcludingTax">
          <el-input
            v-model.number="form.priceExcludingTax"
            placeholder="请输入"
            @blur="handlePriceBlur"
              v-model.number="form.priceExcludingTax"
              placeholder="请输入"
              @blur="handlePriceBlur"
          >
            <template v-slot:suffix>
              <i style="font-style: normal">元</i>
@@ -71,8 +62,8 @@
        </el-form-item>
        <el-form-item label="单价(含税)" prop="priceIncludingTax">
          <el-input
            v-model.number="form.priceIncludingTax"
            placeholder="自动计算"
              v-model.number="form.priceIncludingTax"
              placeholder="自动计算"
          >
            <template v-slot:suffix>
              <i style="font-style: normal">元</i>
@@ -81,8 +72,8 @@
        </el-form-item>
        <el-form-item label="总价(不含税)" prop="totalPriceExcludingTax">
          <el-input
            v-model.number="form.totalPriceExcludingTax"
            placeholder="自动计算"
              v-model.number="form.totalPriceExcludingTax"
              placeholder="自动计算"
          >
            <template v-slot:suffix>
              <i style="font-style: normal">元</i>
@@ -91,8 +82,8 @@
        </el-form-item>
        <el-form-item label="总价(含税)" prop="totalPriceIncludingTax">
          <el-input
            v-model.number="form.totalPriceIncludingTax"
            placeholder="自动计算"
              v-model.number="form.totalPriceIncludingTax"
              placeholder="自动计算"
          >
            <template v-slot:suffix>
              <i style="font-style: normal">元</i>
@@ -100,16 +91,16 @@
          </el-input>
        </el-form-item>
        <el-form-item label="登记人" prop="registrantId">
          <el-input v-model="form.registrantId" disabled placeholder="请输入" />
          <el-input v-model="form.registrantId" disabled placeholder="请输入"/>
        </el-form-item>
        <el-form-item label="登记日期" prop="registrationDate">
          <el-date-picker
            disabled
            v-model="form.registrationDate"
            type="date"
            placeholder="YYYY-MM-DD"
            style="width: 100%"
            value-format="YYYY-MM-DD"
              disabled
              v-model="form.registrationDate"
              type="date"
              placeholder="YYYY-MM-DD"
              style="width: 100%"
              value-format="YYYY-MM-DD"
          />
        </el-form-item>
      </el-form>
@@ -117,10 +108,12 @@
        <div class="dialog-footer">
          <!-- 重置和取消 -->
          <el-button @click="handleClose" v-if="title.includes('新增')"
            >取消</el-button
          >取消
          </el-button
          >
          <el-button @click="handleReset" v-if="title.includes('编辑')"
            >重置</el-button
          >重置
          </el-button
          >
          <el-button type="primary" @click="handleSubmit">确认</el-button>
        </div>
@@ -130,12 +123,11 @@
</template>
<script setup name="ProductionDialog">
import { ref, defineProps, watch, onMounted, nextTick, computed } from "vue";
import { ElMessage } from "element-plus";
import {ref, defineProps, watch, onMounted, nextTick, computed} from "vue";
import {ElMessage} from "element-plus";
import useUserStore from "@/store/modules/user";
import { addOrEditPR,getSupplyList, getCoalInfoList } from "@/api/procureMent";
import { getSupply } from "@/api/basicInformation/supplier";
import { getCoalInfo } from "@/api/basicInformation/coal";
import {addOrEditPR, getSupplyList, getCoalInfoList} from "@/api/procureMent";
const props = defineProps({
  title: {
    type: String,
@@ -164,7 +156,6 @@
      getSupplyList(),
      getCoalInfoList(),
    ]);
    console.log(supplyRes, coalRes);
    let supplyData = supplyRes.data;
    let coalData = coalRes.data;
    supplyList.value = supplyData.map((item) => ({
@@ -188,8 +179,8 @@
    return 0;
  }
  return (
    Math.floor(parseFloat(num) * Math.pow(10, precision)) /
    Math.pow(10, precision)
      Math.floor(parseFloat(num) * Math.pow(10, precision)) /
      Math.pow(10, precision)
  );
};
// 含税单价计算
@@ -250,9 +241,9 @@
// 处理税率输入框失焦,确保精度
const handleTaxRateBlur = () => {
  if (
    form.value.taxRate !== null &&
    form.value.taxRate !== undefined &&
    form.value.taxRate !== ""
      form.value.taxRate !== null &&
      form.value.taxRate !== undefined &&
      form.value.taxRate !== ""
  ) {
    form.value.taxRate = toFixed(parseFloat(form.value.taxRate), 2);
  }
@@ -261,13 +252,13 @@
// 处理不含税单价输入框失焦,确保精度
const handlePriceBlur = () => {
  if (
    form.value.priceExcludingTax !== null &&
    form.value.priceExcludingTax !== undefined &&
    form.value.priceExcludingTax !== ""
      form.value.priceExcludingTax !== null &&
      form.value.priceExcludingTax !== undefined &&
      form.value.priceExcludingTax !== ""
  ) {
    form.value.priceExcludingTax = toFixed(
      parseFloat(form.value.priceExcludingTax),
      2
        parseFloat(form.value.priceExcludingTax),
        2
    );
  }
};
@@ -275,13 +266,13 @@
// 处理采购数量输入框失焦,确保精度
const handleQuantityBlur = () => {
  if (
    form.value.purchaseQuantity !== null &&
    form.value.purchaseQuantity !== undefined &&
    form.value.purchaseQuantity !== ""
      form.value.purchaseQuantity !== null &&
      form.value.purchaseQuantity !== undefined &&
      form.value.purchaseQuantity !== ""
  ) {
    form.value.purchaseQuantity = toFixed(
      parseFloat(form.value.purchaseQuantity),
      3
        parseFloat(form.value.purchaseQuantity),
        3
    ); // 数量保留3位小数
  }
};
@@ -293,30 +284,29 @@
});
const rules = {
  supplierName: [
    { required: true, message: "请输入供应商名称", trigger: "blur" },
    {required: true, message: "请输入供应商名称", trigger: "blur"},
  ],
  coal: [{ required: true, message: "请输入煤种", trigger: "blur" }],
  unit: [{ required: true, message: "请输入单位", trigger: "blur" }],
  coal: [{required: true, message: "请输入煤种", trigger: "blur"}],
  purchaseQuantity: [
    { required: true, message: "请输入采购数量", trigger: "blur" },
    { type: "number", message: "采购数量必须为数字", trigger: "blur" },
    {required: true, message: "请输入采购数量", trigger: "blur"},
    {type: "number", message: "采购数量必须为数字", trigger: "blur"},
  ],
  priceExcludingTax: [
    { required: true, message: "请输入单价", trigger: "blur" },
    {required: true, message: "请输入单价", trigger: "blur"},
  ],
  totalPriceExcludingTax: [
    { required: true, message: "请输入总价", trigger: "blur" },
    {required: true, message: "请输入总价", trigger: "blur"},
  ],
  priceIncludingTax: [
    { required: true, message: "请输入含税单价", trigger: "blur" },
    {required: true, message: "请输入含税单价", trigger: "blur"},
  ],
  totalPriceIncludingTax: [
    { required: true, message: "请输入含税总价", trigger: "blur" },
    {required: true, message: "请输入含税总价", trigger: "blur"},
  ],
  taxRate: [{ required: true, message: "请输入税率", trigger: "blur" }],
  registrantId: [{ required: true, message: "请输入登记人", trigger: "blur" }],
  taxRate: [{required: true, message: "请输入税率", trigger: "blur"}],
  registrantId: [{required: true, message: "请输入登记人", trigger: "blur"}],
  registrationDate: [
    { required: true, message: "请选择登记日期", trigger: "change" },
    {required: true, message: "请选择登记日期", trigger: "change"},
  ],
};
// 关闭弹窗
src/views/procureMent/index.vue
@@ -18,12 +18,11 @@
      <!-- 操作按钮区 -->
      <el-row :gutter="24" class="table-toolbar">
        <el-button type="primary" :icon="Plus" @click="handleAdd"
          >新建</el-button
        >
          >新建
        </el-button>
        <el-button type="danger" :icon="Delete" @click="handleDelete"
          >删除</el-button
        >
        <!-- <el-button type="info" :icon="Download" @click="handleExport">导出</el-button> -->
          >删除
        </el-button>
      </el-row>
      <!-- 表格组件 -->
      <data-table
@@ -66,7 +65,12 @@
import DataTable from "@/components/Table/ETable.vue";
import Pagination from "@/components/Pagination";
import ProductionDialog from "./components/ProductionDialog.vue";
import { purchaseRegistration } from "@/api/procureMent";
import {
  purchaseRegistration,
  getSupplyList,
  getCoalInfoList,
} from "@/api/procureMent";
import useUserStore from "@/store/modules/user";
// 引入字典数据
const { proxy } = getCurrentInstance();
@@ -117,9 +121,22 @@
// supplier 供应商数据
const columns = ref([
  { prop: "supplierName", label: "供应商名称", minWidth: 200 },
  { prop: "coal", label: "煤种类型", minWidth: 120 },
  { prop: "unit", label: "单位", minWidth: 150 },
  {
    prop: "supplierId",
    label: "供应商名称",
    minWidth: 200,
    formatter: (row) => {
      return MatchQuery(row.supplierId, "supplyRes") || "未知供应商";
    },
  },
  {
    prop: "coalId",
    label: "煤种类型",
    minWidth: 120,
    formatter: (row) => {
      return MatchQuery(row.coalId, "coalRes") || "未知煤种";
    },
  },
  { prop: "purchaseQuantity", label: "采购数量", minWidth: 100 },
  { prop: "priceIncludingTax", label: "单价(含税)", minWidth: 150 },
  { prop: "totalPriceIncludingTax", label: "总价(含税)", minWidth: 100 },
@@ -128,6 +145,16 @@
  { prop: "registrantId", label: "登记人", minWidth: 100 },
  { prop: "registrationDate", label: "登记日期", minWidth: 100 },
]);
// 匹配查询字段
const MatchQuery = (data, name) => {
  const list = name === "supplyRes" ? supplyRes.value.data : coalRes.value.data;
  const item = list.find((items) => items.id == data);
  return item ? item.coal || item.supplierName  : "";
};
// 获取供应商列表
const supplyRes = ref([]);
const coalRes = ref([]);
// 重置查询
const resetQuery = () => {
@@ -167,7 +194,7 @@
  form.value = {
    supplierName: "",
    coal: "",
    unit: "",
    unit: "吨",
    purchaseQuantity: "",
    priceExcludingTax: "",
    totalPriceExcludingTax: "",
@@ -227,17 +254,6 @@
const handleDeleteSuccess = (row) => {
  ElMessage.success("删除成功:" + row.supplierName);
};
// 导出
const handleExport = (row) => {
  proxy.download(
    "system/post/export",
    {
      ...queryParams.value,
    },
    `post_${new Date().getTime()}.xlsx`
  );
  ElMessage.success("导出数据:" + row.supplierName);
};
// 成功
const handleSuccess = (val) => {
  tableData.value.push(val);
@@ -248,6 +264,10 @@
const getList = async () => {
  loading.value = true;
  try {
    [supplyRes.value, coalRes.value] = await Promise.all([
      getSupplyList(),
      getCoalInfoList(),
    ]);
    // 传递分页参数
    let res = await purchaseRegistration({
      current: current.value,
@@ -271,6 +291,7 @@
.app-container {
  box-sizing: border-box;
}
.search-form {
  background-color: #fff;
  padding: 20px 20px 0 20px;
@@ -278,6 +299,7 @@
  border-radius: 4px;
  box-shadow: var(--el-box-shadow-light);
}
.search-form :deep(.el-form-item) {
  margin-bottom: 16px;
  width: 100%;
@@ -289,11 +311,13 @@
    width: 50%;
  }
}
@media screen and (min-width: 1200px) {
  .search-form :deep(.el-form-item) {
    width: 18%;
  }
}
.table-toolbar {
  margin-bottom: 20px;
  display: flex;
@@ -306,15 +330,18 @@
  .table-toolbar {
    flex-direction: column;
  }
  .table-toolbar .el-button {
    width: 100%;
  }
}
/* 表格工具栏 */
.table-toolbar,
.table-toolbar > * {
  margin: 0 0 0 0 !important;
}
.table-toolbar {
  margin-bottom: 20px !important;
}
src/views/production/components/ProductionDetailsTable.vue
@@ -1,20 +1,20 @@
<template>
  <el-table :data="tableData" :border="border" style="width: 100%">
  <el-table :data="tableData" :border="border" style="width: 100%">
    <el-table-column label="煤种" min-width="120">
      <template #default="{ row, $index }">
        <el-select
          clearable
          :model-value="getCoalNameById(row.coalId) || row.coalId"
          placeholder="请选择煤种"
          @change="(value) => handleCoalSelectChange(row, value)"
          filterable
          :key="`coalId-select-${$index}-${weekList.length}`"
        <el-select
            clearable
            :model-value="getCoalNameById(row.coalId) || row.coalId"
            placeholder="请选择煤种"
            @change="(value) => handleCoalSelectChange(row, value)"
            filterable
            :key="`coalId-select-${$index}-${weekList.length}`"
        >
          <el-option
            v-for="(item, index) of weekList"
            :key="`option-${index}-${item.key}`"
            :label="item.value"
            :value="item.value"
              v-for="(item, index) of weekList"
              :key="`option-${index}-${item.key}`"
              :label="item.value"
              :value="item.value"
          />
        </el-select>
      </template>
@@ -22,10 +22,10 @@
    <el-table-column label="生产数量" min-width="120">
      <template #default="{ row, $index }">
        <el-input
          v-model="row.productionQuantity"
          placeholder="请输入生产数量"
          type="number"
          @input="handleInput('productionQuantity', $index, $event)"
            v-model="row.productionQuantity"
            placeholder="请输入生产数量"
            type="number"
            @input="handleInput('productionQuantity', $index, $event)"
        />
      </template>
    </el-table-column>
@@ -33,10 +33,10 @@
    <el-table-column label="人工成本" min-width="120">
      <template #default="{ row, $index }">
        <el-input
          v-model="row.laborCost"
          placeholder="请输入人工成本"
          type="number"
          @input="handleInput('laborCost', $index, $event)"
            v-model="row.laborCost"
            placeholder="请输入人工成本"
            type="number"
            @input="handleInput('laborCost', $index, $event)"
        >
          <template #suffix>
            <i style="font-style: normal">元</i>
@@ -48,10 +48,10 @@
    <el-table-column label="能耗成本" min-width="120">
      <template #default="{ row, $index }">
        <el-input
          v-model="row.energyConsumptionCost"
          placeholder="请输入能耗成本"
          type="number"
          @input="handleInput('energyConsumptionCost', $index, $event)"
            v-model="row.energyConsumptionCost"
            placeholder="请输入能耗成本"
            type="number"
            @input="handleInput('energyConsumptionCost', $index, $event)"
        >
          <template #suffix>
            <i style="font-style: normal">元</i>
@@ -63,10 +63,10 @@
    <el-table-column label="设备折旧" min-width="120">
      <template #default="{ row, $index }">
        <el-input
          v-model="row.equipmentDepreciation"
          placeholder="请输入设备折旧"
          type="number"
          @input="handleInput('equipmentDepreciation', $index, $event)"
            v-model="row.equipmentDepreciation"
            placeholder="请输入设备折旧"
            type="number"
            @input="handleInput('equipmentDepreciation', $index, $event)"
        >
          <template #suffix>
            <i style="font-style: normal">元</i>
@@ -78,10 +78,10 @@
    <el-table-column label="采购单价" min-width="120">
      <template #default="{ row, $index }">
        <el-input
          v-model="row.purchasePrice"
          placeholder="请输入采购单价"
          type="number"
          @input="handleInput('purchasePrice', $index, $event)"
            v-model="row.purchasePrice"
            placeholder="请输入采购单价"
            type="number"
            @input="handleInput('purchasePrice', $index, $event)"
        >
          <template #suffix>
            <i style="font-style: normal">元</i>
@@ -93,12 +93,12 @@
    <el-table-column label="总成本" min-width="120">
      <template #default="{ row, $index }">
        <el-input
          disabled
          v-model="row.totalCost"
          placeholder="总成本"
          type="number"
          :readonly="autoCalculate"
          @input="handleInput('totalCost', $index, $event)"
            disabled
            v-model="row.totalCost"
            placeholder="总成本"
            type="number"
            :readonly="autoCalculate"
            @input="handleInput('totalCost', $index, $event)"
        >
          <template #suffix>
            <i style="font-style: normal">元</i>
@@ -109,34 +109,34 @@
    <el-table-column label="生产人" min-width="120">
      <template #default="{ row, $index }">
        <el-select
          clearable
          :model-value="getUserNameById(row.producer) || row.producer"
          placeholder="请选择生产人"
          @change="(value) => handleUserSelectChange(row, value)"
          filterable
          :key="`producer-select-${$index}-${userList.length}`"
            clearable
            :model-value="getUserNameById(row.producer) || row.producer"
            placeholder="请选择生产人"
            @change="(value) => handleUserSelectChange(row, value)"
            filterable
            :key="`producer-select-${$index}-${userList.length}`"
        >
          <el-option
            v-for="(item, index) of userList"
            :key="`option-${index}-${item.key}`"
            :label="item.value"
            :value="item.value"
              v-for="(item, index) of userList"
              :key="`option-${index}-${item.key}`"
              :label="item.value"
              :value="item.value"
          />
        </el-select>
      </template>
    </el-table-column>
    <el-table-column
      v-if="showOperations"
      label="操作"
      width="120"
      fixed="right"
        v-if="showOperations"
        label="操作"
        width="120"
        fixed="right"
    >
      <template #default="{ $index }">
        <el-button
          type="danger"
          size="small"
          @click="handleDelete($index)"
          :icon="Delete"
            type="danger"
            size="small"
            @click="handleDelete($index)"
            :icon="Delete"
        >
          删除
        </el-button>
@@ -146,11 +146,12 @@
</template>
<script setup name="ProductionDetailsTable">
import { ref, computed, watch, onMounted, nextTick } from "vue";
import { Delete } from "@element-plus/icons-vue";
import { getCoalFieldList } from "@/api/basicInformation/coalQualityMaintenance";
import { getCoalInfoList } from "@/api/production";
import { userListAll } from "@/api/publicApi";
import {ref, computed, watch, onMounted, nextTick} from "vue";
import {Delete} from "@element-plus/icons-vue";
import {getCoalFieldList} from "@/api/basicInformation/coalQualityMaintenance";
import {getCoalInfoList} from "@/api/production";
import {userListAll} from "@/api/publicApi";
const props = defineProps({
  modelValue: {
    type: Array,
@@ -189,19 +190,19 @@
  // 如果开启自动计算总成本
  if (
    props.autoCalculate &&
    [
      "laborCost",
      "energyCost",
      "equipmentDepreciation",
      "purchasePrice",
    ].includes(field)
      props.autoCalculate &&
      [
        "laborCost",
        "energyCost",
        "equipmentDepreciation",
        "purchasePrice",
      ].includes(field)
  ) {
    calculateTotalCost(newData[index]);
  }
  tableData.value = newData;
  emit("input-change", { field, index, value, row: newData[index] });
  emit("input-change", {field, index, value, row: newData[index]});
};
// 计算总成本
@@ -212,10 +213,10 @@
  const purchasePrice = parseFloat(row.purchasePrice) || 0;
  row.totalCost = (
    laborCost +
    energyCost +
    equipmentDepreciation +
    purchasePrice
      laborCost +
      energyCost +
      equipmentDepreciation +
      purchasePrice
  ).toFixed(2);
};
@@ -254,7 +255,7 @@
  if (newValue && weekList.value.length > 0) {
    // 当数据加载完成且weekList已获取时,确保显示正确
  }
}, { deep: true });
}, {deep: true});
// 监听weekList变化,当下拉数据加载完成后处理显示
watch(weekList, (newList) => {
@@ -266,23 +267,23 @@
      tableData.value = tempData;
    });
  }
}, { deep: true });
}, {deep: true});
onMounted(async()=>{
onMounted(async () => {
  let res = await getCoalInfoList()
  console.log(res);
  res.data.forEach(item => {
    let obj = {};
    obj.value = item.coal;
    obj.key = item.id;
    weekList.value.push(obj);
    weekList.value.push(obj);
  });
  let ress = await userListAll();
  ress.data.forEach(item => {
    let obj = {};
    obj.value = item.nickName;
    obj.key = item.userId;
    userList.value.push(obj);
    userList.value.push(obj);
  });
  // 通知父组件weekList已加载完成
  nextTick(() => {
@@ -302,7 +303,7 @@
  }
};
const userList = ref([]);
const getUserList = (async()=>{
const getUserList = (async () => {
  let res = await userListAll();
  if (res.code === 200) {
    userList.value = res.data.map((item) => ({
@@ -318,7 +319,7 @@
  if (newValue && userList.value.length > 0) {
    // 当数据加载完成且weekList已获取时,确保显示正确
  }
}, { deep: true });
}, {deep: true});
// 监听userList变化,当下拉数据加载完成后处理显示
watch(userList, (newList) => {
@@ -330,7 +331,7 @@
      tableData.value = tempData;
    });
  }
}, { deep: true });
}, {deep: true});
const getUserNameById = (id) => {
  const producer = userList.value.find(item => item.key == id);
src/views/production/components/ProductionDialog.vue
@@ -1,25 +1,30 @@
<template>
  <el-dialog
    v-model="dialogVisible"
    :title="dialogType === 'add' ? '新增生产加工' : '编辑生产加工'"
    width="1200px"
    :close-on-click-modal="false"
    @close="handleClose"
      v-model="dialogVisible"
      :title="dialogType === 'add' ? '新增生产加工' : '编辑生产加工'"
      width="1200px"
      :close-on-click-modal="false"
      @close="handleClose"
  >
    <el-row :gutter="10" style="margin-bottom: 10px">
      <el-col :span="3">
        <el-button type="primary" @click="handlData"
          ><el-icon> <Plus /> </el-icon>选择数据</el-button
        >
          <el-icon>
            <Plus/>
          </el-icon>
          选择数据
        </el-button
        >
      </el-col>
      <el-col :span="4">
        <el-button
          type="danger"
          @click="removeSelectedData"
          :disabled="tableData.length === 0"
            type="danger"
            @click="removeSelectedData"
            :disabled="tableData.length === 0"
        >
          <el-icon>
            <Delete />
            <Delete/>
          </el-icon>
          清空已选
        </el-button>
@@ -34,15 +39,15 @@
      </el-col>
    </el-row>
    <ETableModify
      :columns="columns"
      :showOperations="false"
      height="200"
      @cell-edit="handleCellEdit"
      :tableData="tableData"
      :showOverflowTooltip="false"
      @row-click="handleRowClick"
      :editableColumns="['usedQuantity']"
      @delete="handleRemoveItem"
        :columns="columns"
        :showOperations="false"
        height="200"
        @cell-edit="handleCellEdit"
        :tableData="tableData"
        :showOverflowTooltip="false"
        @row-click="handleRowClick"
        :editableColumns="['usedQuantity']"
        @delete="handleRemoveItem"
    />
    <div class="empty-table">
      <h1>生产明细</h1>
@@ -50,7 +55,7 @@
        <el-col :span="2">
          <el-button type="primary" @click="addNewRow">
            <el-icon>
              <Plus />
              <Plus/>
            </el-icon>
            新增
          </el-button>
@@ -65,60 +70,62 @@
        </el-col> -->
      </el-row>
      <ProductionDetailsTable
        v-model="detailsTableData"
        :border="false"
        :show-operations="true"
        :auto-calculate="true"
        @input-change="handleDetailsChange"
        @delete-row="handleDeleteRow"
          v-model="detailsTableData"
          :border="false"
          :show-operations="true"
          :auto-calculate="true"
          @input-change="handleDetailsChange"
          @delete-row="handleDeleteRow"
      />
    </div>
    <template #footer>
      <div class="dialog-footer">
        <el-button
          @click="handleClose"
          v-if="dialogType === 'add' || dialogType === 'edit'"
          >取 消</el-button
            @click="handleClose"
            v-if="dialogType === 'add' || dialogType === 'edit'"
        >取 消
        </el-button
        >
        <!-- <el-button @click="handleReset" v-if="dialogType === 'edit'"
          >重 置</el-button
        > -->
        <el-button type="primary" :loading="loading" @click="handleSubmit"
          >确 定</el-button
        >确 定
        </el-button
        >
      </div>
    </template>
  </el-dialog>
  <el-dialog
    v-model="innerVisible"
    width="1000"
    title="选择配置数据"
    center
    append-to-body
      v-model="innerVisible"
      width="1000"
      title="选择配置数据"
      center
      append-to-body
  >
    <div style="margin-bottom: 10px">
      <el-alert
        v-if="tableData.length > 0"
        :title="`当前已选择 ${tableData.length} 条数据`"
        type="info"
        :closable="false"
        show-icon
          v-if="tableData.length > 0"
          :title="`当前已选择 ${tableData.length} 条数据`"
          type="info"
          :closable="false"
          show-icon
      />
    </div>
    <ETable
      :showIndex="false"
      :showOverflowTooltip="false"
      @selection-change="handleSelectionChange"
      :showOperations="false"
      ref="etableRef"
      :columns="formalDatabaseColumns"
      :tableData="formalDatabaseData"
      :defaultSelectedIds="selectedIds"
      :rowKey="'id'"
      height="400"
      @cell-edit="handleCellEdit"
      :show-selection="true"
        :showIndex="false"
        :showOverflowTooltip="false"
        @selection-change="handleSelectionChange"
        :showOperations="false"
        ref="etableRef"
        :columns="formalDatabaseColumns"
        :tableData="formalDatabaseData"
        :defaultSelectedIds="selectedIds"
        :rowKey="'id'"
        height="400"
        @cell-edit="handleCellEdit"
        :show-selection="true"
    />
    <el-row :gutter="24" style="margin-top: 15px">
      <el-col :span="12">
@@ -129,9 +136,9 @@
      <el-col :span="12" style="text-align: right">
        <el-button @click="innerVisible = false">取消</el-button>
        <el-button
          type="primary"
          @click="handleSelectData"
          :disabled="formalDatabaseSelectedData.length === 0"
            type="primary"
            @click="handleSelectData"
            :disabled="formalDatabaseSelectedData.length === 0"
        >
          确定添加
        </el-button>
@@ -141,34 +148,35 @@
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick, computed } from "vue";
import {ref, reactive, watch, onMounted, nextTick, computed} from "vue";
import ETable from "@/components/Table/ETable.vue";
import ETableModify from "@/components/Table/EtableModify.vue";
import ProductionDetailsTable from "./ProductionDetailsTable.vue";
import { ElMessage, ElMessageBox, ElAlert, ElText } from "element-plus";
import { Delete, Warning, Plus } from "@element-plus/icons-vue";
import {ElMessage, ElMessageBox, ElAlert, ElText} from "element-plus";
import {Delete, Warning, Plus} from "@element-plus/icons-vue";
import {
  getOfficialAll,
  addOrEditPM,
  getCoalInfoList,
  deleteProductionInventory,
} from "@/api/production/index.js";
import { validateFormData, validateNumber, deepClone, createDefaultProductionRow } from "@/utils/production";
import { useCoalData } from "./useCoalData";
import {validateFormData, validateNumber, deepClone, createDefaultProductionRow} from "@/utils/production";
import {useCoalData} from "./useCoalData";
import useUserStore from "@/store/modules/user";
// Props 和 Emits
const props = defineProps({
  visible: { type: Boolean, default: false },
  type: { type: String, default: "add" },
  rowData: { type: Object, default: () => ({}) },
  visible: {type: Boolean, default: false},
  type: {type: String, default: "add"},
  rowData: {type: Object, default: () => ({})},
});
const dialogVisible = defineModel("visible", { type: Boolean, default: false });
const dialogVisible = defineModel("visible", {type: Boolean, default: false});
const emit = defineEmits(["update:visible", "success", "update:productionAndProcessing"]);
// 用户信息和煤种数据
const userStore = useUserStore();
const { getCoalNameById } = useCoalData();
const {getCoalNameById} = useCoalData();
let userInfo;
// 对话框状态
@@ -185,10 +193,16 @@
const selectedIds = ref([]);
const currentRow = ref(null);
const copyForm = ref(null);
const coalList = ref([])
const supplierList = ref({});
// 表格列配置
const columns = [
  { label: "煤种", prop: "coal", minwidth: 120 },
  { label: "库存数量", prop: "inventoryQuantity", minwidth: 100 },
  {prop: "coalId", label: "煤种", minwidth: 60,slot:false,
    formatter: (row) => {
      return coalList.value.find(coal => coal.id === row.coalId)?.coal || "--";
    }
  },
  {label: "库存数量", prop: "inventoryQuantity", minwidth: 100},
  {
    label: "使用数量",
    prop: "usedQuantity",
@@ -199,18 +213,28 @@
];
const formalDatabaseColumns = ref([
  { prop: "supplierName", label: "供应商名称", minwidth: 150 },
  { prop: "coal", label: "煤种类型", minwidth: 60 },
  { prop: "inventoryQuantity", label: "库存数量", minwidth: 80 },
  { prop: "unit", label: "单位", minwidth: 20 },
  { prop: "priceExcludingTax", label: "单价(不含税)", minwidth: 80 },
  { prop: "createTime", label: "登记日期", width: 200 },
  {prop: "supplierName", label: "供应商名称", minwidth: 150
  // ,formatter: (row) => {
  //     console.log(row);
  //     return supplierList.value[row.supplierId] || "--";
  //   }
  },
  {prop: "coalId", label: "煤种", minwidth: 60,
    formatter: (row) => {
      // return coalList.value[row.coalId].coal || "--";
      return coalList.value.find(coal => coal.id === row.coalId)?.coal || "--";
    }
  },
  {prop: "inventoryQuantity", label: "库存数量", minwidth: 80},
  {prop: "unit", label: "单位", minwidth: 20},
  {prop: "priceExcludingTax", label: "单价(不含税)", minwidth: 80},
  {prop: "createTime", label: "登记日期", width: 200},
]);
// 工具函数
const debugIdMatching = () => {
  if (formalDatabaseData.value.length > 0 && selectedIds.value.length > 0) {
    const matchedRows = formalDatabaseData.value.filter((row) =>
      selectedIds.value.includes(row.id)
        selectedIds.value.includes(row.id)
    );
  }
};
@@ -222,12 +246,15 @@
// 获取配置数据
const handlData = async () => {
  innerVisible.value = true;
  let res = await getOfficialAll();
  if (res.code === 200) {
    formalDatabaseData.value = res.data;
  let getSupplier = await getOfficialAll();
  let getCoalName = await getCoalInfoList();
  coalList.value = getCoalName.data || [];
  supplierList.value = getSupplier.data || [];
  if (getSupplier.code === 200) {
    formalDatabaseData.value = getSupplier.data;
    const existingOfficialIds = tableData.value
      .map((item) => item.officialId)
      .filter((id) => id);
        .map((item) => item.officialId)
        .filter((id) => id);
    selectedIds.value = existingOfficialIds;
    debugIdMatching();
    nextTick(() => {
@@ -252,7 +279,7 @@
      try {
        etableRef.value.clearSelection();
        const rowsToSelect = formalDatabaseData.value.filter((row) =>
          ids.includes(row.id)
            ids.includes(row.id)
        );
        if (rowsToSelect.length > 0) {
          etableRef.value.setRowsSelection(rowsToSelect, true);
@@ -277,8 +304,8 @@
  detailsTableData.value = data.productionList || [];
  dialogType.value = "edit";
  const existingOfficialIds = tableData.value
    .map((item) => item.officialId)
    .filter((id) => id);
      .map((item) => item.officialId)
      .filter((id) => id);
  selectedIds.value = existingOfficialIds;
};
// 监听对话框状态,在打开时设置选中状态
@@ -319,8 +346,8 @@
  // 更新selectedIds,确保包含所有当前tableData中的officialId
  const allOfficialIds = tableData.value
    .map((item) => item.officialId)
    .filter((id) => id);
      .map((item) => item.officialId)
      .filter((id) => id);
  selectedIds.value = allOfficialIds;
  // 关闭选择对话框
@@ -348,13 +375,13 @@
  // 验证生产明细数据
  const detailsValidation = validateFormData(detailsTableData.value, [
    "coalId",
    "productionQuantity",
    "productionQuantity",
    "laborCost",
    "energyConsumptionCost",
    "equipmentDepreciation",
    "purchasePrice"
  ]);
  if (!detailsValidation.isValid) {
    ElMessage.warning(detailsValidation.message);
    return;
@@ -400,13 +427,13 @@
const handleCellEdit = (row, prop, value) => {
  if (prop === "usedQuantity") {
    const validation = validateNumber(value, 0, Number(row.inventoryQuantity));
    if (!validation.isValid) {
      ElMessage.warning(validation.message);
      row.usedQuantity = validation.value;
      return;
    }
    row.usedQuantity = validation.value;
  }
};
@@ -445,15 +472,15 @@
// 删除单个已选数据项
const handleRemoveItem = (row) => {
  const index = tableData.value.findIndex(
    (item) => item.officialId === row.officialId
      (item) => item.officialId === row.officialId
  );
  if (index > -1) {
    tableData.value.splice(index, 1);
    // 更新selectedIds
    const updatedOfficialIds = tableData.value
      .map((item) => item.officialId)
      .filter((id) => id);
        .map((item) => item.officialId)
        .filter((id) => id);
    selectedIds.value = updatedOfficialIds;
    ElMessage.success("已删除选中项");
  }
@@ -470,21 +497,22 @@
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(async () => {
      if (dialogType.value === "edit") {
        let res = await deleteProductionInventory({
          productionInventoryList: tableData.value,
        });
        emit("update:productionAndProcessing", tableData.value, copyForm.value);
      }
      // [Vue warn]: Component emitted event "update:productionAndProcessing" but it is neither declared in the emits option nor as an "onUpdate:productionAndProcessing" prop.
      .then(async () => {
        if (dialogType.value === "edit") {
          let res = await deleteProductionInventory({
            productionInventoryList: tableData.value,
          });
          emit("update:productionAndProcessing", tableData.value, copyForm.value);
        }
        // [Vue warn]: Component emitted event "update:productionAndProcessing" but it is neither declared in the emits option nor as an "onUpdate:productionAndProcessing" prop.
      formalDatabaseSelectedData.value = [];
      tableData.value = [];
      selectedIds.value = [];
      ElMessage.success("已清空所有数据");
    })
    .catch(() => {});
        formalDatabaseSelectedData.value = [];
        tableData.value = [];
        selectedIds.value = [];
        ElMessage.success("已清空所有数据");
      })
      .catch(() => {
      });
};
// 计算总使用量
@@ -507,6 +535,7 @@
.el-row > .el-col > h1 {
  font-weight: bolder;
}
.empty-table > .el-row {
  margin-bottom: 12px;
}
src/views/production/components/useCoalData.js
@@ -2,9 +2,9 @@
 * 煤种数据管理组合式函数
 * 提供煤种数据的获取、缓存、转换等功能
 */
import { ref, computed, watch } from 'vue';
import { getCoalInfoList } from "@/api/production";
import { ElMessage } from 'element-plus';
import {ref, computed, watch} from 'vue';
import {getCoalInfoList} from "@/api/production";
import {ElMessage} from 'element-plus';
// 全局煤种数据缓存
const coalData = ref([]);
@@ -12,85 +12,85 @@
const isLoaded = ref(false);
export function useCoalData() {
  // 获取煤种数据
  const getCoalData = async (forceRefresh = false) => {
    if (isLoaded.value && !forceRefresh) {
      return coalData.value;
    }
    if (isLoading.value) {
      // 如果正在加载,等待加载完成
      return new Promise((resolve) => {
        const unwatch = watch(isLoading, (loading) => {
          if (!loading) {
            unwatch();
            resolve(coalData.value);
          }
        });
      });
    }
    // 获取煤种数据
    const getCoalData = async (forceRefresh = false) => {
        if (isLoaded.value && !forceRefresh) {
            return coalData.value;
        }
    isLoading.value = true;
    try {
      const res = await getCoalInfoList();
      if (res.code === 200) {
        coalData.value = res.data;
        isLoaded.value = true;
        return coalData.value;
      } else {
        ElMessage.error('获取煤种数据失败');
        return [];
      }
    } catch (error) {
      ElMessage.error('获取煤种数据失败');
      console.error('煤种数据获取错误:', error);
      return [];
    } finally {
      isLoading.value = false;
    }
  };
        if (isLoading.value) {
            // 如果正在加载,等待加载完成
            return new Promise((resolve) => {
                const unwatch = watch(isLoading, (loading) => {
                    if (!loading) {
                        unwatch();
                        resolve(coalData.value);
                    }
                });
            });
        }
  // 根据ID获取煤种名称
  const getCoalNameById = (id) => {
    if (!id || coalData.value.length === 0) return id;
    const coal = coalData.value.find(item => item.id == id);
    return coal ? coal.coal : id;
  };
        isLoading.value = true;
        try {
            const res = await getCoalInfoList();
            if (res.code === 200) {
                coalData.value = res.data;
                isLoaded.value = true;
                return coalData.value;
            } else {
                ElMessage.error('获取煤种数据失败');
                return [];
            }
        } catch (error) {
            ElMessage.error('获取煤种数据失败');
            console.error('煤种数据获取错误:', error);
            return [];
        } finally {
            isLoading.value = false;
        }
    };
  // 根据名称获取煤种ID
  const getCoalIdByName = (name) => {
    if (!name || coalData.value.length === 0) return '';
    const coal = coalData.value.find(item => item.coal === name);
    return coal ? coal.id : '';
  };
    // 根据ID获取煤种名称
    const getCoalNameById = (id) => {
        if (!id || coalData.value.length === 0) return id;
        const coal = coalData.value.find(item => item.id == id);
        return coal ? coal.coal : id;
    };
  // 生成下拉选项
  const coalOptions = computed(() => {
    return coalData.value.map(item => ({
      label: item.coal,
      value: item.coal,
      key: item.id
    }));
  });
    // 根据名称获取煤种ID
    const getCoalIdByName = (name) => {
        if (!name || coalData.value.length === 0) return '';
        const coal = coalData.value.find(item => item.coal === name);
        return coal ? coal.id : '';
    };
  // 生成key-value映射
  const coalMap = computed(() => {
    const map = {};
    coalData.value.forEach(item => {
      map[item.id] = item.coal;
    // 生成下拉选项
    const coalOptions = computed(() => {
        return coalData.value.map(item => ({
            label: item.coal,
            value: item.coal,
            key: item.id
        }));
    });
    return map;
  });
  return {
    coalData: computed(() => coalData.value),
    coalOptions,
    coalMap,
    isLoading: computed(() => isLoading.value),
    isLoaded: computed(() => isLoaded.value),
    getCoalData,
    getCoalNameById,
    getCoalIdByName
  };
    // 生成key-value映射
    const coalMap = computed(() => {
        const map = {};
        coalData.value.forEach(item => {
            map[item.id] = item.coal;
        });
        return map;
    });
    return {
        coalData: computed(() => coalData.value),
        coalOptions,
        coalMap,
        isLoading: computed(() => isLoading.value),
        isLoaded: computed(() => isLoaded.value),
        getCoalData,
        getCoalNameById,
        getCoalIdByName
    };
}
src/views/production/components/useDialog.js
@@ -2,55 +2,55 @@
 * 对话框管理组合式函数
 * 提供对话框的打开、关闭、数据处理等功能
 */
import { ref } from 'vue';
import {ref} from 'vue';
export function useDialog() {
  const dialogVisible = ref(false);
  const dialogType = ref('add');
  const dialogRef = ref(null);
  const currentRowData = ref(null);
    const dialogVisible = ref(false);
    const dialogType = ref('add');
    const dialogRef = ref(null);
    const currentRowData = ref(null);
  // 打开对话框
  const openDialog = (type = 'add', rowData = null) => {
    dialogType.value = type;
    currentRowData.value = rowData;
    dialogVisible.value = true;
    // 调用对话框组件的初始化方法
    if (dialogRef.value) {
      if (type === 'add') {
        dialogRef.value.Initialization?.();
      } else if (type === 'edit' && rowData) {
        dialogRef.value.editInitialization?.(rowData);
      }
    }
  };
    // 打开对话框
    const openDialog = (type = 'add', rowData = null) => {
        dialogType.value = type;
        currentRowData.value = rowData;
        dialogVisible.value = true;
  // 关闭对话框
  const closeDialog = () => {
    dialogVisible.value = false;
    dialogType.value = 'add';
    currentRowData.value = null;
  };
        // 调用对话框组件的初始化方法
        if (dialogRef.value) {
            if (type === 'add') {
                dialogRef.value.Initialization?.();
            } else if (type === 'edit' && rowData) {
                dialogRef.value.editInitialization?.(rowData);
            }
        }
    };
  // 对话框成功回调
  const handleDialogSuccess = (callback) => {
    closeDialog();
    if (typeof callback === 'function') {
      callback();
    }
  };
    // 关闭对话框
    const closeDialog = () => {
        dialogVisible.value = false;
        dialogType.value = 'add';
        currentRowData.value = null;
    };
  return {
    // 状态
    dialogVisible,
    dialogType,
    dialogRef,
    currentRowData,
    // 方法
    openDialog,
    closeDialog,
    handleDialogSuccess
  };
    // 对话框成功回调
    const handleDialogSuccess = (callback) => {
        closeDialog();
        if (typeof callback === 'function') {
            callback();
        }
    };
    return {
        // 状态
        dialogVisible,
        dialogType,
        dialogRef,
        currentRowData,
        // 方法
        openDialog,
        closeDialog,
        handleDialogSuccess
    };
}
src/views/production/components/useTableData.js
@@ -2,131 +2,131 @@
 * 表格数据管理组合式函数
 * 提供分页、搜索、选择等通用功能
 */
import { ref, reactive } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import {ref, reactive} from 'vue';
import {ElMessage, ElMessageBox} from 'element-plus';
export function useTableData(apiFunction, options = {}) {
  const {
    pageSize = 10,
    searchField = 'searchAll'
  } = options;
    const {
        pageSize = 10,
        searchField = 'searchAll'
    } = options;
  // 响应式数据
  const tableData = ref([]);
  const loading = ref(false);
  const total = ref(0);
  const selectedRows = ref([]);
    // 响应式数据
    const tableData = ref([]);
    const loading = ref(false);
    const total = ref(0);
    const selectedRows = ref([]);
  // 查询参数
  const queryParams = reactive({
    [searchField]: '',
    current: 1,
    size: pageSize,
  });
    // 查询参数
    const queryParams = reactive({
        [searchField]: '',
        current: 1,
        size: pageSize,
    });
  // 获取列表数据
  const getList = async () => {
    loading.value = true;
    try {
      const params = {
        [searchField]: queryParams[searchField],
        current: queryParams.current,
        size: queryParams.size,
      };
      console.log('查询参数:', params);
      const res = await apiFunction(params);
      tableData.value = res.data.records || [];
      total.value = res.data.total || 0;
    } catch (error) {
      ElMessage.error('获取数据失败');
      console.error('API错误:', error);
    } finally {
      loading.value = false;
    }
  };
  // 搜索
  const handleSearch = () => {
    queryParams.current = 1;
    getList();
  };
  // 重置搜索
  const handleReset = () => {
    queryParams[searchField] = '';
    console.log('重置搜索参数:', queryParams);
    handleSearch();
  };
  // 分页处理
  const handlePageChange = ({ page, limit }) => {
    if (page && page !== queryParams.current) {
      queryParams.current = page;
    }
    if (limit && limit !== queryParams.size) {
      queryParams.size = limit;
      queryParams.current = 1; // 改变每页大小时回到第一页
    }
    getList();
  };
  // 表格选择处理
  const handleSelectionChange = (selection) => {
    selectedRows.value = selection;
  };
  // 批量删除
  const deleteSelected = async (deleteFunction) => {
    if (selectedRows.value.length === 0) {
      ElMessage.warning('请选择要删除的数据');
      return;
    }
    try {
      await ElMessageBox.confirm(
        `确认删除选中的 ${selectedRows.value.length} 条数据吗?`,
        '删除确认',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
    // 获取列表数据
    const getList = async () => {
        loading.value = true;
        try {
            const params = {
                [searchField]: queryParams[searchField],
                current: queryParams.current,
                size: queryParams.size,
            };
            console.log('查询参数:', params);
            const res = await apiFunction(params);
            tableData.value = res.data.records || [];
            total.value = res.data.total || 0;
        } catch (error) {
            ElMessage.error('获取数据失败');
            console.error('API错误:', error);
        } finally {
            loading.value = false;
        }
      );
    };
      const ids = selectedRows.value.map(row => row.id);
      await deleteFunction(ids);
      ElMessage.success('删除成功');
      selectedRows.value = [];
      getList();
    } catch (error) {
      if (error !== 'cancel') {
        ElMessage.error('删除失败');
        console.error('删除错误:', error);
      }
    }
  };
    // 搜索
    const handleSearch = () => {
        queryParams.current = 1;
        getList();
    };
  // 刷新数据
  const refresh = () => {
    getList();
  };
    // 重置搜索
    const handleReset = () => {
        queryParams[searchField] = '';
        console.log('重置搜索参数:', queryParams);
        handleSearch();
    };
  return {
    // 数据
    tableData,
    loading,
    total,
    selectedRows,
    queryParams,
    // 方法
    getList,
    handleSearch,
    handleReset,
    handlePageChange,
    handleSelectionChange,
    deleteSelected,
    refresh
  };
    // 分页处理
    const handlePageChange = ({page, limit}) => {
        if (page && page !== queryParams.current) {
            queryParams.current = page;
        }
        if (limit && limit !== queryParams.size) {
            queryParams.size = limit;
            queryParams.current = 1; // 改变每页大小时回到第一页
        }
        getList();
    };
    // 表格选择处理
    const handleSelectionChange = (selection) => {
        selectedRows.value = selection;
    };
    // 批量删除
    const deleteSelected = async (deleteFunction) => {
        if (selectedRows.value.length === 0) {
            ElMessage.warning('请选择要删除的数据');
            return;
        }
        try {
            await ElMessageBox.confirm(
                `确认删除选中的 ${selectedRows.value.length} 条数据吗?`,
                '删除确认',
                {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }
            );
            const ids = selectedRows.value.map(row => row.id);
            await deleteFunction(ids);
            ElMessage.success('删除成功');
            selectedRows.value = [];
            getList();
        } catch (error) {
            if (error !== 'cancel') {
                ElMessage.error('删除失败');
                console.error('删除错误:', error);
            }
        }
    };
    // 刷新数据
    const refresh = () => {
        getList();
    };
    return {
        // 数据
        tableData,
        loading,
        total,
        selectedRows,
        queryParams,
        // 方法
        getList,
        handleSearch,
        handleReset,
        handlePageChange,
        handleSelectionChange,
        deleteSelected,
        refresh
    };
}
src/views/production/index.vue
@@ -4,9 +4,9 @@
    <el-form :inline="true" :model="queryParams" class="search-form">
      <el-form-item label="搜索">
        <el-input
          v-model="queryParams.searchAll"
          placeholder="请输入关键词"
          clearable
            v-model="queryParams.searchAll"
            placeholder="请输入关键词"
            clearable
        />
      </el-form-item>
      <el-form-item>
@@ -27,18 +27,19 @@
        </el-button>
      </div>      <!-- 数据表格 -->
      <ETable
        :showOverflowTooltip="false"
        :loading="loading"
        :table-data="tableData"
        :columns="columns"
        :current-page="queryParams.current"
        :page-size="queryParams.size"
        @selection-change="handleSelectionChange"
        @edit="row => openDialog('edit', row)"
        :show-selection="true"
        :border="true"
        :maxHeight="480"
      >        <template #coal="{ row }">
          :showOverflowTooltip="false"
          :loading="loading"
          :table-data="tableData"
          :columns="columns"
          :current-page="queryParams.current"
          :page-size="queryParams.size"
          @selection-change="handleSelectionChange"
          @edit="row => openDialog('edit', row)"
          :show-selection="true"
          :border="true"
          :maxHeight="480"
      >
        <template #coal="{ row }">
          <div class="coal-tags">
            <el-tag v-for="coal in parseCoalArray(row.coal)" :key="coal" size="small">
              {{ getCoalNameById(coal) }}
@@ -48,47 +49,47 @@
        </template>
      </ETable>      <!-- 分页组件 -->
      <Pagination
        :layout="'total, prev, pager, next, jumper'"
        :total="total"
        v-model:page="queryParams.current"
        :limit="queryParams.size"
        @pagination="handlePageChange"
          :layout="'total, prev, pager, next, jumper'"
          :total="total"
          v-model:page="queryParams.current"
          :limit="queryParams.size"
          @pagination="handlePageChange"
      />
    </el-card>
    <!-- 生产对话框 -->
    <!-- handleProductionAndProcessing -->
    <ProductionDialog
      v-model:visible="dialogVisible"
      ref="dialogRef"
      :type="dialogType"
      @update:productionAndProcessing="handleProductionAndProcessing"
      @success="handleDialogSuccess"
        v-model:visible="dialogVisible"
        ref="dialogRef"
        :type="dialogType"
        @update:productionAndProcessing="handleProductionAndProcessing"
        @success="handleDialogSuccess"
    />
  </div>
</template>
<script setup>
import { onMounted } from "vue";
import { ElMessage } from "element-plus";
import { Plus, Delete } from "@element-plus/icons-vue";
import {onMounted} from "vue";
import {ElMessage} from "element-plus";
import {Plus, Delete} from "@element-plus/icons-vue";
import ProductionDialog from "./components/ProductionDialog.vue";
import ETable from "@/components/Table/ETable.vue";
import Pagination from "@/components/Pagination/index.vue";
import { getProductionMasterList, delPM } from "@/api/production";
import { parseCoalArray } from "@/utils/production";
import { useTableData } from "./components/useTableData.js";
import { useDialog } from "./components/useDialog.js";
import { useCoalData } from "./components/useCoalData.js";
import {getProductionMasterList, delPM} from "@/api/production";
import {parseCoalArray} from "@/utils/production";
import {useTableData} from "./components/useTableData.js";
import {useDialog} from "./components/useDialog.js";
import {useCoalData} from "./components/useCoalData.js";
// 表格列配置
const columns = [
  { prop: "coal", label: "煤种", minWidth: 150, slot: 'coal' },
  { prop: "productionQuantity", label: "生产数量", minWidth: 120 },
  { prop: "laborCost", label: "人工成本", minWidth: 150 },
  { prop: "energyConsumptionCost", label: "能耗成本", minWidth: 120 },
  { prop: "equipmentDepreciation", label: "设备折旧", minWidth: 143 },
  { prop: "totalCost", label: "总成本", minWidth: 150 },
  {prop: "coal", label: "煤种", minWidth: 150, slot: 'coal'},
  {prop: "productionQuantity", label: "生产数量", minWidth: 120},
  {prop: "laborCost", label: "人工成本", minWidth: 150},
  {prop: "energyConsumptionCost", label: "能耗成本", minWidth: 120},
  {prop: "equipmentDepreciation", label: "设备折旧", minWidth: 143},
  {prop: "totalCost", label: "总成本", minWidth: 150},
];
// 使用表格数据组合式函数
@@ -104,7 +105,7 @@
  handlePageChange,
  handleSelectionChange,
  deleteSelected
} = useTableData(getProductionMasterList, { pageSize: 10 });
} = useTableData(getProductionMasterList, {pageSize: 10});
// 使用对话框组合式函数
const {
@@ -116,13 +117,13 @@
} = useDialog();
// 使用煤种数据组合式函数
const { getCoalNameById, getCoalData } = useCoalData();
const {getCoalNameById, getCoalData} = useCoalData();
// 处理生产数据更新
const handleProductionAndProcessing = (row, rows) => {
  const index = tableData.value.findIndex(item => item.id === rows.id);
  if (index !== -1) {
    tableData.value[index] = { ...tableData.value[index], ...row };
    tableData.value[index] = {...tableData.value[index], ...row};
  }
};
@@ -158,6 +159,7 @@
    width: 20%;
  }
}
.search-form {
  display: flex;
  justify-content: space-between;
@@ -172,15 +174,16 @@
    margin-left: 10px;
  }
}
.coal-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  .el-tag {
    margin-right: 4px;
    margin-bottom: 4px;
    &:last-child {
      margin-right: 0;
    }