已添加16个文件
已修改12个文件
3752 ■■■■■ 文件已修改
src/api/consumablesLogistics/consumablesIn.js 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/consumablesLogistics/consumablesInRecord.js 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/consumablesLogistics/consumablesOutRecord.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/consumablesLogistics/consumablesUninventory.js 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/qualityManagement/rawMaterial.js 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/dispatchLog/Record.vue 733 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/dispatchLog/index.vue 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/receiptManagement/Record.vue 281 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/receiptManagement/index.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/stockManagement/FrozenAndThaw.vue 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/stockManagement/Import.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/stockManagement/New.vue 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/stockManagement/Qualified.vue 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/stockManagement/Subtract.vue 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/stockManagement/Unqualified.vue 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/stockManagement/index.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/consumablesLogistics/stockReport/index.vue 675 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Modal/RepairModal.vue 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/index.vue 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/Record.vue 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/Record.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Qualified.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/InspectItem/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/index.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterial/components/formDia.vue 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterial/index.vue 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/consumablesLogistics/consumablesIn.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
import request from "@/utils/request.js";
// åˆ†é¡µæŸ¥è¯¢è€—材库存记录列表
export const getConsumablesInListPage = (params) => {
  return request({
    url: "/consumablesInventory/pageConsumablesInventory",
    method: "get",
    params,
  });
};
// åˆ›å»ºè€—材库存记录(新增库存)
export const createConsumablesIn = (params) => {
  return request({
    url: "/consumablesInventory/addConsumablesInventory",
    method: "post",
    data: params,
  });
};
// å‡å°‘耗材库存记录(扣减库存)
export const subtractConsumablesIn = (params) => {
  return request({
    url: "/consumablesInventory/subtractConsumablesInventory",
    method: "post",
    data: params,
  });
};
// è€—材库存报表分页
export const getConsumablesInReportList = (params) => {
  return request({
    url: "/consumablesInventory/ConsumablesInventoryPage",
    method: "get",
    params,
  });
};
// è€—材进出存报表(统计各个产品的入库和出库记录)
export const getConsumablesInInAndOutReportList = (params) => {
  return request({
    url: "/consumablesInventory/ConsumablesInAndOutRecord",
    method: "get",
    params,
  });
};
// å†»ç»“耗材库存记录
export const frozenConsumablesIn = (params) => {
  return request({
    url: "/consumablesInventory/frozenConsumables",
    method: "post",
    data: params,
  });
};
// è§£å†»è€—材库存记录
export const thawConsumablesIn = (params) => {
  return request({
    url: "/consumablesInventory/thawConsumables",
    method: "post",
    data: params,
  });
};
src/api/consumablesLogistics/consumablesInRecord.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
import request from "@/utils/request";
// è€—材入库管理-查询入库记录列表
export const getConsumablesInRecordListPage = (params) => {
    return request({
        url: "/consumablesInRecord/listPage",
        method: "get",
        params,
    });
};
export const updateConsumablesInRecord = (id, data) => {
    return request({
        url: "/consumablesInRecord/" + id,
        method: "put",
        data: data,
    });
};
export const batchDeleteConsumablesInRecords = (ids) => {
    return request({
        url: "/consumablesInRecord",
        method: "delete",
        data: ids,
    });
};
src/api/consumablesLogistics/consumablesOutRecord.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
import request from "@/utils/request";
// è€—材出库台账-查询出库列表
export const getConsumablesOutRecordPage = (params) => {
    return request({
        url: "/consumablesOutRecord/listPage",
        method: "get",
        params,
    });
};
// åˆ é™¤è€—材出库信息
export const delConsumablesOutRecord = (ids) => {
    return request({
        url: "/consumablesOutRecord",
        method: "delete",
        data: ids,
    });
};
src/api/consumablesLogistics/consumablesUninventory.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
import request from "@/utils/request.js";
// åˆ†é¡µæŸ¥è¯¢è€—材不合格库存记录列表
export const getConsumablesUninventoryListPage = (params) => {
  return request({
    url: "/consumablesUnInventory/pageConsumablesUnInventory",
    method: "get",
    params,
  });
};
// åˆ›å»ºè€—材不合格库存记录
export const createConsumablesUnInventory = (params) => {
  return request({
    url: "/consumablesUninventory/addstockUninventory",
    method: "post",
    data: params,
  });
};
// å‡å°‘耗材不合格库存记录
export const subtractConsumablesUnInventory = (params) => {
  return request({
    url: "/consumablesUninventory/subtractstockUninventory",
    method: "post",
    data: params,
  });
};
// å†»ç»“耗材不合格库存记录
export const frozenConsumablesUninventory = (params) => {
  return request({
    url: "/consumablesUninventory/frozenStock",
    method: "post",
    data: params,
  });
};
// è§£å†»è€—材不合格库存记录
export const thawConsumablesUninventory = (params) => {
  return request({
    url: "/consumablesUninventory/thawStock",
    method: "post",
    data: params,
  });
};
src/api/qualityManagement/rawMaterial.js
@@ -9,11 +9,36 @@
    })
}
// æŸ¥è¯¢åŽŸæ£€è¯¦æƒ…
export function findRawMaterialDetail(id) {
    return request({
        url: '/quality/rawMaterial/detail/' + id,
        method: 'get',
    })
}
// æ–°å¢žåŽŸæ£€
export function createRawMaterial(data) {
    return request({
        url: '/quality/rawMaterial',
        method: 'post',
        data: data,
    })
}
// æäº¤
export function submitRawMaterial(id) {
    return request({
        url: '/quality/rawMaterial/submit/' + id,
        method: 'patch',
    })
}
// ä¿®æ”¹æ£€éªŒäºº
export function updateCheckUserName(data) {
    return request({
        url: '/quality/rawMaterial/updateCheckUserName',
        method: 'patch',
        data: data,
    })
}
@@ -35,3 +60,12 @@
        data: query,
    })
}
export function downloadRawMaterial(data) {
    return request({
        url: '/quality/rawMaterial/down',
        method: 'post',
        data: data,
        responseType: "blob",
    })
}
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -168,6 +168,43 @@
        </el-skeleton>
      </div>
      <div v-if="isRawMaterialApproval" style="margin: 10px 0 18px;">
        <el-divider content-position="left">原料详情</el-divider>
        <el-skeleton :loading="rawMaterialLoading" animated>
          <template #template>
            <el-skeleton-item variant="h3" style="width: 30%" />
            <el-skeleton-item variant="text" style="width: 100%" />
            <el-skeleton-item variant="text" style="width: 100%" />
          </template>
          <template #default>
            <el-empty v-if="!currentRawMaterial" description="未查询到对应原料详情" />
            <template v-else>
              <el-descriptions :column="2" border>
                <el-descriptions-item label="产品名称">{{ currentRawMaterial.productName }}</el-descriptions-item>
                <el-descriptions-item label="规格">{{ currentRawMaterial.model }}</el-descriptions-item>
                <el-descriptions-item label="单位">{{ currentRawMaterial.unit }}</el-descriptions-item>
                <el-descriptions-item label="批号">{{ currentRawMaterial.batchNo }}</el-descriptions-item>
                <el-descriptions-item label="检验类型">{{ currentRawMaterial.checkTypeText }}</el-descriptions-item>
                <el-descriptions-item label="检测结果">{{ currentRawMaterial.checkResultText }}</el-descriptions-item>
                <el-descriptions-item label="检验员">{{ currentRawMaterial.checkUserName }}</el-descriptions-item>
                <el-descriptions-item label="检测日期">{{ currentRawMaterial.checkTime }}</el-descriptions-item>
              </el-descriptions>
              <div style="margin-top: 20px;">
                <h4>检测项目明细</h4>
                <el-table :data="currentRawMaterial.qualityInspectItem || []" border style="width: 100%">
                  <el-table-column prop="name" label="检测项目" />
                  <el-table-column prop="unit" label="单位" />
                  <el-table-column prop="standardValue" label="标准值" />
                  <el-table-column prop="internalControl" label="内控值" />
                  <el-table-column prop="testValue" label="化验值" />
                </el-table>
              </div>
            </template>
          </template>
        </el-skeleton>
      </div>
      <el-form :model="{ activities }" ref="formRef" label-position="top">
        <el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical">
          <el-step
@@ -232,6 +269,7 @@
import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
import {findRawMaterialDetail} from "@/api/qualityManagement/rawMaterial.js";
const emit = defineEmits(['close'])
const { proxy } = getCurrentInstance()
@@ -252,9 +290,12 @@
const quotationLoading = ref(false)
const currentQuotation = ref({})
const purchaseLoading = ref(false)
const rawMaterialLoading = ref(false)
const currentPurchase = ref({})
const currentRawMaterial = ref({})
const isQuotationApproval = computed(() => Number(props.approveType) === 6)
const isPurchaseApproval = computed(() => Number(props.approveType) === 5)
const isRawMaterialApproval = computed(() => Number(props.approveType) === 9)
const data = reactive({
    form: {
@@ -291,10 +332,12 @@
// æ‰“开弹框
const openDialog = (type, row) => {
  console.log(type, row);
  operationType.value = type;
  dialogFormVisible.value = true;
  currentQuotation.value = {}
  currentPurchase.value = {}
  currentRawMaterial.value = {}
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
    });
@@ -355,6 +398,15 @@
    }
  }
  if (isRawMaterialApproval.value) {
    rawMaterialLoading.value = true
    findRawMaterialDetail(row.approveRecordId).then(res => {
      currentRawMaterial.value = res.data;
    }).finally(() => {
      rawMaterialLoading.value = false
    })
  }
  approveProcessDetails(row.approveId).then((res) => {
    activities.value = res.data
    // å¢žåŠ isApproval字段
src/views/collaborativeApproval/approvalProcess/index.vue
@@ -6,6 +6,7 @@
      <el-tab-pane label="请假管理" name="2"></el-tab-pane>
      <el-tab-pane label="出差管理" name="3"></el-tab-pane>
      <el-tab-pane label="报销管理" name="4"></el-tab-pane>
      <el-tab-pane label="原料管理" name="9"></el-tab-pane>
    </el-tabs>
    
    <div class="search_form">
@@ -35,7 +36,7 @@
        <el-button
          type="primary"
          @click="openForm('add')"
          v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
          v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7 && currentApproveType !== 9"
        >新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button
@@ -216,6 +217,7 @@
          currentApproveType.value === 5 ||
          currentApproveType.value === 6 ||
          currentApproveType.value === 7 ||
          currentApproveType.value === 9 ||
          row.approveStatus == 2 ||
          row.approveStatus == 1 ||
          row.approveStatus == 4
@@ -297,6 +299,7 @@
    5: "/approveProcess/exportFive",
    6: "/approveProcess/exportSix",
    7: "/approveProcess/exportSeven",
    9: "/approveProcess/exportNine",
  }
  const url = urlMap[type] || urlMap[0]
  const nameMap = {
@@ -308,6 +311,7 @@
    5: "采购申请审批表",
    6: "报价审批表",
    7: "发货审批表",
    9: "原料审批表",
  }
  const fileName = nameMap[type] || nameMap[0]
  proxy.download(url, {}, `${fileName}.xlsx`)
src/views/consumablesLogistics/dispatchLog/Record.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,733 @@
<template>
    <div class="app-container">
        <div class="search_form">
            <div>
                <span class="search_title ml10">出库日期:</span>
                <el-date-picker
                    v-model="searchForm.timeStr"
                    type="date"
                    placeholder="请选择日期"
                    value-format="YYYY-MM-DD"
                    format="YYYY-MM-DD"
                    clearable
                    @change="handleQuery"
                />
        <span class="search_title ml10">来源:</span>
        <el-select v-model="searchForm.recordType"
                   style="width: 240px"
                   placeholder="请选择"
                   clearable>
          <el-option v-for="item in stockRecordTypeOptions"
                     :key="item.value"
                     :label="item.label"
                     :value="item.value"/>
        </el-select>
                <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
                >搜索</el-button
                >
            </div>
            <div>
                <el-button @click="handleOut">导出</el-button>
                <el-button type="danger" plain @click="handleDelete">删除</el-button>
                <el-button type="primary" plain @click="handlePrint">打印</el-button>
            </div>
        </div>
        <div class="table_list">
            <el-table
                :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
                :expand-row-keys="expandedRowKeys"
                :row-key="(row) => row.id"
                style="width: 100%"
                height="calc(100vh - 18.5em)"
            >
                <el-table-column align="center" type="selection" width="55" />
                <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column
            label="出库批次"
            prop="outboundBatches"
            min-width="100"
            show-overflow-tooltip
        />
                <el-table-column
                    label="出库日期"
                    prop="createTime"
                    show-overflow-tooltip
                />
                <el-table-column
                    label="产品大类"
                    prop="productName"
                    show-overflow-tooltip
                />
                <el-table-column
                    label="规格型号"
                    prop="model"
                    show-overflow-tooltip
                />
                <el-table-column
                    label="单位"
                    prop="unit"
                    show-overflow-tooltip
                />
                <el-table-column
                    label="出库数量"
                    prop="stockOutNum"
                    show-overflow-tooltip
                />
                <el-table-column
                    label="净重(吨)"
                    prop="netWeight"
                    show-overflow-tooltip
                />
                <el-table-column
                    label="出库人"
                    prop="createBy"
                    show-overflow-tooltip
                />
        <el-table-column label="来源"
                         prop="recordType"
                         show-overflow-tooltip>
          <template #default="scope">
            {{ getRecordType(scope.row.recordType) }}
          </template>
        </el-table-column>
        <el-table-column
            label="车牌"
            prop="licensePlateNo"
            show-overflow-tooltip
        />
        <el-table-column label="操作"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button type="primary"
                       size="mini"
                       @click="handlePreview(scope.row)">导出过磅单</el-button>
          </template>
        </el-table-column>
            </el-table>
            <pagination
                v-show="total > 0"
                :total="total"
                layout="total, sizes, prev, pager, next, jumper"
                :page="page.current"
                :limit="page.size"
                @pagination="paginationChange"
            />
        </div>
    </div>
</template>
<script setup>
import pagination from "@/components/PIMTable/Pagination.vue";
import { ref, reactive, toRefs, getCurrentInstance } from "vue";
import { ElMessageBox } from "element-plus";
import useUserStore from "@/store/modules/user";
import { getCurrentDate } from "@/utils/index.js";
import {
    getConsumablesOutRecordPage,
    delConsumablesOutRecord,
} from "@/api/consumablesLogistics/consumablesOutRecord.js";
import {
  findAllQualifiedStockOutRecordTypeOptions, findAllUnQualifiedStockOutRecordTypeOptions,
} from "@/api/basicData/enum.js";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
// æ¥æºç±»åž‹é€‰é¡¹
const stockRecordTypeOptions = ref([]);
const page = reactive({
    current: 1,
    size: 100,
});
const total = ref(0);
const props = defineProps({
  type: {
    type: String,
    required: true,
    default: '0'
  }
})
// æ‰“印相关
const printPreviewVisible = ref(false);
const printData = ref([]);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const data = reactive({
    searchForm: {
        supplierName: "",
        timeStr: "",
    recordType: "",
    }
});
const { searchForm } = toRefs(data);
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
    page.current = 1;
    getList();
};
const paginationChange = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
    tableLoading.value = true;
    getConsumablesOutRecordPage({ ...searchForm.value, ...page, type: props.type })
        .then((res) => {
            tableLoading.value = false;
            tableData.value = res.data.records;
            tableData.value.map((item) => {
                item.children = [];
            });
            total.value = res.data.total;
        })
        .catch(() => {
            tableLoading.value = false;
        });
};
const getRecordType = (recordType) => {
  return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
}
// èŽ·å–æ¥æºç±»åž‹é€‰é¡¹
const fetchStockRecordTypeOptions = () => {
  if (props.type === '0') {
    findAllQualifiedStockOutRecordTypeOptions()
        .then(res => {
          stockRecordTypeOptions.value = res.data;
        })
    return
  }
  findAllUnQualifiedStockOutRecordTypeOptions()
      .then(res => {
        stockRecordTypeOptions.value = res.data;
      })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
    // è¿‡æ»¤æŽ‰å­æ•°æ®
    selectedRows.value = selection.filter((item) => item.id);
    console.log("selection", selectedRows.value);
};
const expandedRowKeys = ref([]);
// å¯¼å‡ºè¿‡ç£…单
const handlePreview = (row) => {
  proxy.$download.name(row.weighbridgeDocPath);
}
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/consumablesOutRecord/exportConsumablesOutRecord", {type: props.type}, props.type === '0' ? "耗材合格出库台账.xlsx" : "耗材不合格出库台账.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
// åˆ é™¤
const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            delConsumablesOutRecord(ids).then((res) => {
                proxy.$modal.msgSuccess("删除成功");
                getList();
            });
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
// æ‰“印功能
const handlePrint = () => {
    if (selectedRows.value.length === 0) {
        proxy.$modal.msgWarning("请选择要打印的数据");
        return;
    }
    printData.value = [...selectedRows.value];
    console.log('打印数据:', printData.value);
    printPreviewVisible.value = true;
};
// æ‰§è¡Œæ‰“印
const executePrint = () => {
    console.log('开始执行打印,数据条数:', printData.value.length);
    console.log('打印数据:', printData.value);
    // åˆ›å»ºä¸€ä¸ªæ–°çš„æ‰“印窗口
    const printWindow = window.open('', '_blank', 'width=800,height=600');
    // æž„建打印内容
    let printContent = `
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>打印预览</title>
      <style>
        body {
          margin: 0;
          padding: 0;
          font-family: "SimSun", serif;
          background: white;
        }
                                                     .print-page {
            width: 200mm;
            height: 75mm;
            padding: 10mm;
            padding-left: 20mm;
            background: white;
            box-sizing: border-box;
            page-break-after: always;
            page-break-inside: avoid;
          }
         .print-page:last-child {
           page-break-after: avoid;
         }
        .delivery-note {
          width: 100%;
          height: 100%;
          font-size: 12px;
          line-height: 1.2;
          display: flex;
          flex-direction: column;
          color: #000;
        }
        .header {
          text-align: center;
          margin-bottom: 8px;
        }
        .company-name {
          font-size: 18px;
          font-weight: bold;
          margin-bottom: 4px;
        }
        .document-title {
          font-size: 16px;
          font-weight: bold;
        }
        .info-section {
          margin-bottom: 8px;
          display: flex;
          justify-content: space-between;
          align-items: center;
        }
        .info-row {
          line-height: 20px;
        }
        .label {
          font-weight: bold;
          width: 60px;
          font-size: 12px;
        }
        .value {
          margin-right: 20px;
          min-width: 80px;
          font-size: 12px;
        }
                 .table-section {
                 margin-bottom: 40px;
          //  flex: 0.6;
         }
        .product-table {
          width: 100%;
          border-collapse: collapse;
          border: 1px solid #000;
        }
                 .product-table th, .product-table td {
           border: 1px solid #000;
           padding: 6px;
           text-align: center;
           font-size: 12px;
           line-height: 1.4;
         }
        .product-table th {
          font-weight: bold;
        }
        .total-value {
          font-weight: bold;
        }
        .footer-section {
          margin-top: auto;
        }
        .footer-row {
          display: flex;
          margin-bottom: 3px;
          line-height: 22px;
          justify-content: space-between;
        }
        .footer-item {
          display: flex;
          margin-right: 20px;
        }
        .footer-item .label {
          font-weight: bold;
          width: 80px;
          font-size: 12px;
        }
        .footer-item .value {
          min-width: 80px;
          font-size: 12px;
        }
        .address-item .address-value {
          min-width: 200px;
        }
        @media print {
          body {
            margin: 0;
            padding: 0;
          }
                     .print-page {
             margin: 0;
             padding: 10mm;
             /* padding-left: 20mm; */
             page-break-inside: avoid;
             page-break-after: always;
           }
           .print-page:last-child {
             page-break-after: avoid;
           }
        }
      </style>
    </head>
    <body>
  `;
    // ä¸ºæ¯æ¡æ•°æ®ç”Ÿæˆæ‰“印页面
    printData.value.forEach((item, index) => {
        printContent += `
      <div class="print-page">
        <div class="delivery-note">
          <div class="header">
            <div class="company-name">鼎诚瑞实业有限责任公司</div>
            <div class="document-title">零售发货单</div>
          </div>
          <div class="info-section">
            <div class="info-row">
              <div>
                <span class="label">发货日期:</span>
                <span class="value">${formatDate(item.createTime)}</span>
              </div>
              <div>
                <span class="label">客户名称:</span>
                <span class="value">${item.supplierName || '张爱有'}</span>
              </div>
            </div>
            <div class="info-row">
              <span class="label">单号:</span>
              <span class="value">${item.code || ''}</span>
            </div>
          </div>
          <div class="table-section">
            <table class="product-table">
              <thead>
                <tr>
                  <th>产品名称</th>
                  <th>规格型号</th>
                  <th>单位</th>
                  <th>单价</th>
                  <th>零售数量</th>
                  <th>零售金额</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>${item.productName || '砂灰砖'}</td>
                  <td>${item.model || '标准'}</td>
                  <td>${item.unit || '块'}</td>
                  <td>${item.taxInclusiveUnitPrice || '0'}</td>
                  <td>${item.inboundNum || '2000'}</td>
                  <td>${item.taxInclusiveTotalPrice || '0'}</td>
                </tr>
              </tbody>
              <tfoot>
                <tr>
                  <td class="label">合计</td>
                  <td class="total-value"></td>
                  <td class="total-value"></td>
                  <td class="total-value"></td>
                  <td class="total-value">${item.inboundNum || '2000'}</td>
                  <td class="total-value">${item.taxInclusiveTotalPrice || '0'}</td>
                </tr>
              </tfoot>
            </table>
          </div>
          <div class="footer-section">
            <div class="footer-row">
              <div class="footer-item">
                <span class="label">收货电话:</span>
                <span class="value"></span>
              </div>
              <div class="footer-item">
                <span class="label">收货人:</span>
                <span class="value"></span>
              </div>
              <div class="footer-item address-item">
                <span class="label">收货地址:</span>
                <span class="value address-value"></span>
              </div>
            </div>
            <div class="footer-row">
              <div class="footer-item">
                <span class="label">操作员:</span>
                <span class="value">${userStore.nickName || '撕开前'}</span>
              </div>
              <div class="footer-item">
                <span class="label">打印日期:</span>
                <span class="value">${formatDateTime(new Date())}</span>
              </div>
            </div>
          </div>
        </div>
      </div>
    `;
    });
    printContent += `
    </body>
    </html>
  `;
    // å†™å…¥å†…容到新窗口
    printWindow.document.write(printContent);
    printWindow.document.close();
    // ç­‰å¾…内容加载完成后打印
    printWindow.onload = () => {
        setTimeout(() => {
            printWindow.print();
            printWindow.close();
            printPreviewVisible.value = false;
        }, 500);
    };
};
// æ ¼å¼åŒ–日期
const formatDate = (dateString) => {
    if (!dateString) return getCurrentDate();
    const date = new Date(dateString);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}/${month}/${day}`;
};
// æ ¼å¼åŒ–日期时间
const formatDateTime = (date) => {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    const hours = String(date.getHours()).padStart(2, "0");
    const minutes = String(date.getMinutes()).padStart(2, "0");
    const seconds = String(date.getSeconds()).padStart(2, "0");
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
};
onMounted(() => {
    getList();
  fetchStockRecordTypeOptions();
});
</script>
<style scoped lang="scss">
.print-preview-dialog {
    .el-dialog__body {
        padding: 0;
        max-height: 80vh;
        overflow-y: auto;
    }
}
.print-preview-container {
    .print-preview-header {
        padding: 15px;
        border-bottom: 1px solid #e4e7ed;
        text-align: center;
        .el-button {
            margin: 0 10px;
        }
    }
    .print-preview-content {
        padding: 20px;
        background-color: #f5f5f5;
        min-height: 400px;
    }
}
.print-page {
    width: 220mm;
    height: 90mm;
    padding: 10mm;
    margin: 0 auto;
    background: white;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    margin-bottom: 10px;
    box-sizing: border-box;
}
.delivery-note {
    width: 100%;
    height: 100%;
    font-family: "SimSun", serif;
    font-size: 10px;
    line-height: 1.2;
    display: flex;
    flex-direction: column;
}
.header {
    text-align: center;
    margin-bottom: 8px;
    .company-name {
        font-size: 18px;
        font-weight: bold;
        margin-bottom: 4px;
    }
    .document-title {
        font-size: 16px;
        font-weight: bold;
    }
}
.info-section {
    margin-bottom: 8px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .info-row {
        line-height: 20px;
        .label {
            font-weight: bold;
            width: 60px;
            font-size: 14px;
        }
        .value {
            margin-right: 20px;
            min-width: 80px;
            font-size: 14px;
        }
    }
}
.table-section {
    margin-bottom: 4px;
    flex: 1;
    .product-table {
        width: 100%;
        border-collapse: collapse;
        border: 1px solid #000;
        th, td {
            border: 1px solid #000;
            padding: 6px;
            text-align: center;
            font-size: 14px;
            line-height: 1.4;
        }
        th {
            font-weight: bold;
        }
        .total-label {
            text-align: right;
            font-weight: bold;
        }
        .total-value {
            font-weight: bold;
        }
    }
}
.footer-section {
    .footer-row {
        display: flex;
        margin-bottom: 3px;
        line-height: 20px;
        justify-content: space-between;
        .footer-item {
            display: flex;
            margin-right: 20px;
            .label {
                font-weight: bold;
                width: 80px;
                font-size: 14px;
            }
            .value {
                min-width: 80px;
                font-size: 14px;
            }
            &.address-item {
                .address-value {
                    min-width: 200px;
                }
            }
        }
    }
}
@media print {
    .app-container {
        display: none;
    }
    .print-page {
        box-shadow: none;
        margin: 0;
        padding: 10mm;
        padding-left: 20mm;
        page-break-inside: avoid;
        page-break-after: always;
    }
    .print-page:last-child {
        page-break-after: avoid;
    }
}
</style>
src/views/consumablesLogistics/dispatchLog/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane v-for="tab in tabs"
                   :label="tab.label"
                   :name="tab.name"
                   :key="tab.name">
        <record :type="tab.type" v-if="activeTab === tab.name" />
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script setup>
import { ref, computed } from "vue";
import Record from "@/views/consumablesLogistics/dispatchLog/Record.vue";
const activeTab = ref('qualified')
const type = ref(0)
const tabs = computed(() => {
  return [
    {
      label: '合格出库',
      name: 'qualified',
      type: '0'
    },
    {
      label: '不合格出库',
      name: 'unqualified',
      type: '1'
    }
  ]
})
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
  type.value = tabName === 'qualified' ? 0 : 1
}
</script>
src/views/consumablesLogistics/receiptManagement/Record.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,281 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title ml10">入库日期:</span>
        <el-date-picker v-model="searchForm.timeStr"
                        type="date"
                        placeholder="请选择日期"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        clearable
                        @change="handleQuery"/>
        <span class="search_title ml10">产品大类:</span>
        <el-input v-model="searchForm.productName"
                  style="width: 240px"
                  placeholder="请输入"
                  clearable/>
        <span class="search_title ml10">来源:</span>
        <el-select v-model="searchForm.recordType"
                  style="width: 240px"
                  placeholder="请选择"
                  clearable>
          <el-option v-for="item in stockRecordTypeOptions"
                     :key="item.value"
                     :label="item.label"
                     :value="item.value"/>
        </el-select>
        <el-button type="primary"
                   @click="handleQuery"
                   style="margin-left: 10px">搜索
        </el-button>
      </div>
      <div>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除
        </el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
                :expand-row-keys="expandedRowKeys"
                :row-key="row => row.id"
                style="width: 100%"
                height="calc(100vh - 18.5em)">
        <el-table-column align="center"
                         type="selection"
                         width="55"/>
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60"/>
        <el-table-column label="入库批次"
                         prop="inboundBatches"
                         width="280"
                         show-overflow-tooltip/>
        <el-table-column label="入库时间"
                         prop="createTime"
                         show-overflow-tooltip/>
        <el-table-column label="产品大类"
                         prop="productName"
                         show-overflow-tooltip/>
        <el-table-column label="规格型号"
                         prop="model"
                         show-overflow-tooltip/>
        <el-table-column label="单位"
                         prop="unit"
                         show-overflow-tooltip/>
        <el-table-column label="入库数量"
                         prop="stockInNum"
                         show-overflow-tooltip/>
        <el-table-column label="车牌号"
                         prop="licensePlateNo"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="毛重(吨)"
                         prop="grossWeight"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="皮重(吨)"
                         prop="tareWeight"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="净重(吨)"
                         prop="netWeight"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="入库人"
                         prop="createBy"
                         show-overflow-tooltip/>
        <el-table-column label="来源"
                         prop="recordType"
                         show-overflow-tooltip>
          <template #default="scope">
            {{ getRecordType(scope.row.recordType) }}
          </template>
        </el-table-column>
        <el-table-column label="过磅日期"
                         prop="weighingDate"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="过磅员"
                         prop="weighingOperator"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="操作"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button type="primary"
                       size="mini"
                       @click="handlePreview(scope.row)">导出过磅单</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="pageProductChange"/>
    </div>
  </div>
</template>
<script setup>
import pagination from "@/components/PIMTable/Pagination.vue";
import {
  ref,
  reactive,
  toRefs,
  onMounted,
  getCurrentInstance,
} from "vue";
import {ElMessageBox} from "element-plus";
import {
  getConsumablesInRecordListPage,
  batchDeleteConsumablesInRecords,
} from "@/api/consumablesLogistics/consumablesInRecord.js";
import {
  findAllQualifiedStockInRecordTypeOptions, findAllUnQualifiedStockInRecordTypeOptions,
} from "@/api/basicData/enum.js";
const {proxy} = getCurrentInstance();
const props = defineProps({
  type: {
    type: String,
    required: true,
    default: '0'
  }
})
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const stockRecordTypeOptions = ref([]);
const page = reactive({
  current: 1,
  size: 100,
});
const total = ref(0);
const data = reactive({
  searchForm: {
    productName: "",
    timeStr: "",
    recordType: "",
  },
});
const {searchForm} = toRefs(data);
const handleQuery = () => {
  page.current = 1;
  getList();
};
const getRecordType = (recordType) => {
  return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
}
// å¯¼å‡ºè¿‡ç£…单
const handlePreview = (row) => {
  console.log(row);
  console.log(row.weighbridgeDocPath);
  proxy.$download.name(row.weighbridgeDocPath);
}
const pageProductChange = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  const params = {...page, type: props.type};
  params.timeStr = searchForm.value.timeStr;
  params.productName = searchForm.value.productName;
  params.recordType = searchForm.value.recordType;
  getConsumablesInRecordListPage(params)
      .then(res => {
        tableData.value = res.data.records || [];
        total.value = res.data.total ?? 0;
      }).finally(() => {
    tableLoading.value = false;
  })
};
const fetchStockRecordTypeOptions = () => {
  if (props.type === '0') {
    findAllQualifiedStockInRecordTypeOptions()
        .then(res => {
          stockRecordTypeOptions.value = res.data;
        })
    return
  }
  findAllUnQualifiedStockInRecordTypeOptions()
      .then(res => {
        stockRecordTypeOptions.value = res.data;
      })
}
const handleSelectionChange = selection => {
  selectedRows.value = selection.filter(item => item.id);
};
const expandedRowKeys = ref([]);
const handleOut = () => {
  ElMessageBox.confirm("是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/consumablesInRecord/exportConsumablesInRecord", {type: props.type}, props.type === '0' ? "耗材合格入库.xlsx" : "耗材不合格入库.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
const handleDelete = () => {
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  const ids = selectedRows.value.map(item => item.id);
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        batchDeleteConsumablesInRecords(ids)
            .then(() => {
              proxy.$modal.msgSuccess("删除成功");
              getList();
            })
            .catch(() => {
              proxy.$modal.msgError("删除失败");
            });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
onMounted(() => {
  getList();
  fetchStockRecordTypeOptions();
});
</script>
<style scoped lang="scss"></style>
src/views/consumablesLogistics/receiptManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane v-for="tab in tabs"
                   :label="tab.label"
                   :name="tab.name"
                   :key="tab.name">
        <record :type="tab.type" v-if="activeTab === tab.name" />
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script setup>
import Record from "@/views/consumablesLogistics/receiptManagement/Record.vue";
const activeTab = ref('qualified')
const type = ref(0)
const tabs = ref([
  {
    label: '合格入库',
    name: 'qualified',
    type: '0'
  },
  {
    label: '不合格入库',
    name: 'unqualified',
    type: '1'
  }
])
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
  type.value = tabName === 'qualified' ? 0 : 1
}
</script>
src/views/consumablesLogistics/stockManagement/FrozenAndThaw.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,164 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        :title="operationType === 'frozen' ? '冻结库存' : '解冻库存'"
        width="800"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" ref="formRef">
        <el-form-item
            :label="operationType === 'frozen' ? '冻结数量:' : '解冻数量:'"
            prop="lockedQuantity"
        >
          <el-input-number v-model="formState.lockedQuantity" :step="1" :min="1" precision="0" style="width: 100%" :max="maxCount" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import {frozenConsumablesIn, thawConsumablesIn} from "@/api/consumablesLogistics/consumablesIn.js";
import {frozenConsumablesUninventory, thawConsumablesUninventory} from "@/api/consumablesLogistics/consumablesUninventory.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  operationType: {
    type: String,
    required: true,
    default: 'frozen',
  },
  type: {
    type: String,
    required: true,
    default: 'qualified',
  },
  record: {
    type: Object,
    default: () => {},
  }
});
const emit = defineEmits(['update:visible', 'completed']);
// å“åº”式数据(替代选项式的 data)
const formState = ref({
  lockedQuantity: 0,
});
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
let { proxy } = getCurrentInstance()
const closeModal = () => {
  // é‡ç½®è¡¨å•数据
  formState.value = {
    lockedQuantity: undefined
  };
  isShow.value = false;
};
const maxCount = computed(() => {
  // å†»ç»“库存最大数量为未解冻数量
  if (props.operationType === 'frozen') {
    return props.record.unLockedQuantity
  }
  // è§£å†»åº“存最大数量为已冻结数量
  return props.record.lockedQuantity
})
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      const data = Object.assign({id: props.record.id}, formState.value);
      if (props.type === 'qualified') {
        // å†»ç»“
        if (props.operationType === 'frozen') {
          frozenConsumablesIn(data).then(res => {
            if (res.code === 200) {
              // å…³é—­æ¨¡æ€æ¡†
              isShow.value = false;
              // å‘ŠçŸ¥çˆ¶ç»„件已完成
              emit('completed');
              proxy.$modal.msgSuccess("提交成功");
            } else {
              proxy.$modal.msgError(res.msg);
            }
          })
        } else {
          thawConsumablesIn(data).then(res => {
            if (res.code === 200) {
              // å…³é—­æ¨¡æ€æ¡†
              isShow.value = false;
              // å‘ŠçŸ¥çˆ¶ç»„件已完成
              emit('completed');
              proxy.$modal.msgSuccess("提交成功");
            } else {
              proxy.$modal.msgError(res.msg);
            }
          })
        }
      } else {
        if (props.operationType === 'frozen') {
          frozenConsumablesUninventory(data).then(res => {
            if (res.code === 200) {
              // å…³é—­æ¨¡æ€æ¡†
              isShow.value = false;
              // å‘ŠçŸ¥çˆ¶ç»„件已完成
              emit('completed');
              proxy.$modal.msgSuccess("提交成功");
            } else {
              proxy.$modal.msgError(res.msg);
            }
          })
        } else {
          thawConsumablesUninventory(data).then(res => {
            if (res.code === 200) {
              // å…³é—­æ¨¡æ€æ¡†
              isShow.value = false;
              // å‘ŠçŸ¥çˆ¶ç»„件已完成
              emit('completed');
              proxy.$modal.msgSuccess("提交成功");
            } else {
              proxy.$modal.msgError(res.msg);
            }
          })
        }
      }
    }
  })
};
onMounted(() => {
  formState.value.lockedQuantity = maxCount.value;
})
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
});
</script>
src/views/consumablesLogistics/stockManagement/Import.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
<template>
  <el-dialog  v-model="isShow" title="导入库存" @close="closeModal">
    <FileUpload
      ref="fileUploadRef"
      accept=".xlsx, .xls"
      :headers="upload.headers"
      :action="upload.url"
      :disabled="upload.isUploading"
      :showTip="true"
      @success="handleFileSuccess"
      :downloadTemplate="downloadTemplate"
    />
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitFileForm">ç¡® å®š</el-button>
        <el-button @click="closeModal">取 æ¶ˆ</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import {computed, ref, getCurrentInstance, reactive} from "vue";
import { getToken } from "@/utils/auth.js";
import { FileUpload } from "@/components/Upload";
import { ElMessage } from "element-plus";
defineOptions({
  name: "导入库存",
});
const { proxy } = getCurrentInstance()
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  type: {
    type: String,
    required: true,
    default: 'qualified',
  },
});
const emit = defineEmits(['update:visible', 'uploadSuccess']);
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
const fileUploadRef = ref();
const upload = reactive({
  // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(库存导入)
  open: false,
  // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
  isUploading: false,
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/consumablesInventory/importConsumablesInventory",
});
const submitFileForm = () => {
  fileUploadRef.value.uploadApi();
};
const handleFileSuccess = (response) => {
  const { code, msg } = response;
  if (code == 200) {
    ElMessage({ message: "导入成功", type: "success" });
    emit('uploadSuccess');
    closeModal();
  } else {
    ElMessage({ message: msg, type: "error" });
  }
};
const downloadTemplate = () => {
  proxy.download("/consumablesInventory/downloadConsumablesInventory", {}, "耗材库存导入模板.xlsx");
}
const closeModal = () => {
  isShow.value = false;
};
</script>
src/views/consumablesLogistics/stockManagement/New.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,273 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="新增库存"
        width="800"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
        <el-form-item
            label="产品名称"
            prop="productModelId"
            :rules="[
                {
                required: true,
                message: '请选择产品',
                trigger: 'change',
              }
            ]"
        >
          <el-button type="primary" @click="showProductSelectDialog = true">
            {{ formState.productName ? formState.productName : '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item
            label="规格"
            prop="productModelName"
        >
          <el-input v-model="formState.productModelName"  disabled />
        </el-form-item>
        <el-form-item
            label="单位"
            prop="unit"
        >
          <el-input v-model="formState.unit"  disabled />
        </el-form-item>
        <!-- productType === 0:原材料 -->
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="车牌号"
            prop="licensePlateNo"
        >
          <el-input v-model="formState.licensePlateNo" />
        </el-form-item>
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="毛重(吨)"
            prop="grossWeight"
        >
          <el-input-number
              v-model="formState.grossWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              @change="computeNetWeight"
          />
        </el-form-item>
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="皮重(吨)"
            prop="tareWeight"
        >
          <el-input-number
              v-model="formState.tareWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              @change="computeNetWeight"
          />
        </el-form-item>
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="净重(吨)"
            prop="netWeight"
        >
          <el-input-number
              v-model="formState.netWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              disabled
          />
        </el-form-item>
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="过磅日期"
            prop="weighingDate"
        >
          <el-date-picker
              style="width: 100%"
              v-model="formState.weighingDate"
              value-format="YYYY-MM-DD HH:mm:ss"
              format="YYYY-MM-DD HH:mm:ss"
              type="datetime"
              placeholder="请选择过磅日期"
              clearable
          />
        </el-form-item>
        <el-form-item
            v-if="type === 'qualified' && formState.productType === 0"
            label="过磅员"
            prop="weighingOperator"
        >
          <el-input v-model="formState.weighingOperator" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="formState.remark" type="textarea" />
        </el-form-item>
      </el-form>
      <!-- äº§å“é€‰æ‹©å¼¹çª— -->
      <ProductSelectDialog
          v-model="showProductSelectDialog"
          @confirm="handleProductSelect"
          single
      />
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {createConsumablesIn} from "@/api/consumablesLogistics/consumablesIn.js";
import {createConsumablesUnInventory} from "@/api/consumablesLogistics/consumablesUninventory.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  type: {
    type: String,
    required: true,
    default: 'qualified',
  },
});
const emit = defineEmits(['update:visible', 'completed']);
// å“åº”式数据(替代选项式的 data)
const formState = ref({
  productId: undefined,
  productModelId: undefined,
  productName: "",
  productModelName: "",
  unit: "",
  productType: undefined,
  // è¿‡ç£…相关字段(仅原材料合格品使用)
  licensePlateNo: "",
  grossWeight: undefined,
  tareWeight: undefined,
  netWeight: undefined,
  weighingDate: undefined,
  weighingOperator: "",
  remark: '',
});
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
const showProductSelectDialog = ref(false);
let { proxy } = getCurrentInstance()
const closeModal = () => {
  // é‡ç½®è¡¨å•数据
  formState.value = {
    productId: undefined,
    productModelId: undefined,
    productName: "",
    productModelName: "",
    description: '',
  };
  isShow.value = false;
};
// äº§å“é€‰æ‹©å¤„理
const handleProductSelect = async (products) => {
  formState.value.weighingDate = undefined;
  formState.value.grossWeight = undefined;
  formState.value.tareWeight = undefined;
  formState.value.netWeight = undefined;
  if (products && products.length > 0) {
    const product = products[0];
    formState.value.productId = product.productId;
    formState.value.productName = product.productName;
    formState.value.productModelName = product.model;
    formState.value.productModelId = product.id;
    formState.value.unit = product.unit;
    formState.value.productType = product.productType;
    showProductSelectDialog.value = false;
    // è§¦å‘表单验证更新
    proxy.$refs["formRef"]?.validateField('productModelId');
  }
};
// å‡€é‡ = æ¯›é‡ - çš®é‡
const computeNetWeight = () => {
  const { grossWeight, tareWeight } = formState.value;
  if (grossWeight != null && tareWeight != null) {
    const net = Number(grossWeight) - Number(tareWeight);
    // ä¿ç•™ä¸¤ä½å°æ•°ï¼Œä¸”不为负
    const safeNet = Number(net.toFixed(2));
    formState.value.netWeight = safeNet > 0 ? safeNet : 0;
  } else {
    formState.value.netWeight = undefined;
  }
};
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      // éªŒè¯æ˜¯å¦é€‰æ‹©äº†äº§å“å’Œè§„æ ¼
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择产品");
        return;
      }
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择规格");
        return;
      }
      if (props.type === 'qualified') {
        createConsumablesIn(formState.value).then(res => {
          // å…³é—­æ¨¡æ€æ¡†
          isShow.value = false;
          // å‘ŠçŸ¥çˆ¶ç»„件已完成
          emit('completed');
          proxy.$modal.msgSuccess("提交成功");
        })
      } else {
        createConsumablesUnInventory(formState.value).then(res => {
          // å…³é—­æ¨¡æ€æ¡†
          isShow.value = false;
          // å‘ŠçŸ¥çˆ¶ç»„件已完成
          emit('completed');
          proxy.$modal.msgSuccess("提交成功");
        })
      }
    }
  })
};
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
});
</script>
src/views/consumablesLogistics/stockManagement/Qualified.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,211 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title ml10">产品大类:</span>
        <el-input v-model="searchForm.productName"
                  style="width: 240px"
                  placeholder="请输入"
                  clearable/>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
         <el-button type="primary" @click="isShowNewModal = true">新增库存</el-button>
        <el-button type="info" plain icon="Upload" @click="isShowImportModal = true">
          å¯¼å…¥åº“å­˜
        </el-button>
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="row => row.id" style="width: 100%"
        :row-class-name="tableRowClassName" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="产品类型" prop="parentName" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productName" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="model" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip />
        <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip />
        <!-- <el-table-column label="库存预警数量" prop="warnNum"  show-overflow-tooltip /> -->
        <el-table-column label="净重(吨)" prop="netWeight"  show-overflow-tooltip />
        <el-table-column label="备注" prop="remark"  show-overflow-tooltip />
        <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="60" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">出库</el-button>
            <el-button link type="primary" size="small" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">冻结</el-button>
            <el-button link type="primary" size="small" v-if="scope.row.lockedQuantity > 0" @click="showThawModal(scope.row)">解冻</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <new-stock-inventory v-if="isShowNewModal"
                 v-model:visible="isShowNewModal"
                 type="qualified"
                 @completed="handleQuery" />
    <subtract-stock-inventory v-if="isShowSubtractModal"
                 v-model:visible="isShowSubtractModal"
                 :record="record"
                 type="qualified"
                 @completed="handleQuery" />
    <!-- å¯¼å…¥åº“å­˜-->
    <import-stock-inventory v-if="isShowImportModal"
                 v-model:visible="isShowImportModal"
                 type="qualified"
                 @uploadSuccess="handleQuery" />
    <!-- å†»ç»“/解冻库存-->
    <frozen-and-thaw-stock-inventory v-if="isShowFrozenAndThawModal"
                 v-model:visible="isShowFrozenAndThawModal"
                 :record="record"
                 :operation-type="operationType"
                 type="qualified"
                 @completed="handleQuery" />
  </div>
</template>
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import {ElMessage, ElMessageBox} from "element-plus";
import { getConsumablesInListPage } from "@/api/consumablesLogistics/consumablesIn.js";
const NewStockInventory = defineAsyncComponent(() => import("@/views/consumablesLogistics/stockManagement/New.vue"));
const SubtractStockInventory = defineAsyncComponent(() => import("@/views/consumablesLogistics/stockManagement/Subtract.vue"));
const ImportStockInventory = defineAsyncComponent(() => import("@/views/consumablesLogistics/stockManagement/Import.vue"));
const FrozenAndThawStockInventory = defineAsyncComponent(() => import("@/views/consumablesLogistics/stockManagement/FrozenAndThaw.vue"));
const { proxy } = getCurrentInstance()
const tableData = ref([])
const selectedRows = ref([])
const record = ref({})
const tableLoading = ref(false)
const page = reactive({
  current: 1,
  size: 100,
})
const total = ref(0)
// æ˜¯å¦æ˜¾ç¤ºæ–°å¢žå¼¹æ¡†
const isShowNewModal = ref(false)
// æ˜¯å¦æ˜¾ç¤ºé¢†ç”¨å¼¹æ¡†
const isShowSubtractModal = ref(false)
// æ˜¯å¦æ˜¾ç¤ºå†»ç»“/解冻弹框
const isShowFrozenAndThawModal = ref(false)
// æ“ä½œç±»åž‹
const operationType = ref('frozen')
// æ˜¯å¦æ˜¾ç¤ºå¯¼å…¥å¼¹æ¡†
const isShowImportModal = ref(false)
const data = reactive({
  searchForm: {
    productName: '',
  }
})
const { searchForm } = toRefs(data)
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1
  getList()
}
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList()
}
const getList = () => {
  tableLoading.value = true
  getConsumablesInListPage({ ...searchForm.value, ...page }).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
    // æ•°æ®åŠ è½½å®ŒæˆåŽæ£€æŸ¥åº“å­˜
    // checkStockAndCreatePurchase();
  }).catch(() => {
    tableLoading.value = false
  })
}
const handleFileSuccess = (response) => {
  const { code, msg } = response;
  if (code == 200) {
    ElMessage({ message: "导入成功", type: "success" });
    upload.open = false;
    emits("uploadSuccess");
  } else {
    ElMessage({ message: msg, type: "error" });
  }
};
// ç‚¹å‡»é¢†ç”¨
const showSubtractModal = (row) => {
  record.value = row
  isShowSubtractModal.value = true
}
// ç‚¹å‡»å†»ç»“
const showFrozenModal = (row) => {
  record.value = row
  isShowFrozenAndThawModal.value = true
  operationType.value = 'frozen'
}
// ç‚¹å‡»è§£å†»
const showThawModal = (row) => {
  record.value = row
  isShowFrozenAndThawModal.value = true
  operationType.value = 'thaw'
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  // è¿‡æ»¤æŽ‰å­æ•°æ®
  selectedRows.value = selection.filter(item => item.id);
  console.log('selection', selectedRows.value)
}
const expandedRowKeys = ref([])
// è¡¨æ ¼è¡Œç±»å
const tableRowClassName = ({ row }) => {
  const stock = Number(row?.unLockedQuantity ?? 0);
  const warn = Number(row?.warnNum ?? 0);
  if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
    return '';
  }
  return stock < warn ? 'row-low-stock' : '';
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm(
    '是否确认导出?',
    '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }
  ).then(() => {
    proxy.download("/consumablesInventory/exportConsumablesInventory", {}, '耗材合格库存信息.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
onMounted(() => {
  getList()
})
</script>
<style scoped lang="scss">
:deep(.row-low-stock td) {
  background-color: #fde2e2;
  color: #c45656;
}
:deep(.row-low-stock:hover > td) {
  background-color: #fcd4d4;
}
</style>
src/views/consumablesLogistics/stockManagement/Subtract.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,285 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="领用"
        width="800"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
        <el-form-item
            label="产品名称"
            prop="productModelId"
            :rules="[
                {
                required: true,
                message: '请选择产品',
                trigger: 'change',
              }
            ]"
        >
          <el-button type="primary" @click="showProductSelectDialog = true" disabled>
            {{ formState.productName ? formState.productName : '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item
            label="规格"
            prop="productModelName"
        >
          <el-input v-model="formState.model"  disabled />
        </el-form-item>
        <el-form-item
            label="单位"
            prop="unit"
        >
          <el-input v-model="formState.unit"  disabled />
        </el-form-item>
        <el-form-item
            label="车牌号"
            prop="licensePlateNo"
        >
          <el-input v-model="formState.licensePlateNo" />
        </el-form-item>
        <el-form-item
            label="毛重(吨)"
            prop="grossWeight"
        >
          <el-input-number
              v-model="formState.grossWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              @change="computeNetWeight"
          />
        </el-form-item>
        <el-form-item
            label="皮重(吨)"
            prop="tareWeight"
        >
          <el-input-number
              v-model="formState.tareWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              @change="computeNetWeight"
          />
        </el-form-item>
        <el-form-item
            label="净重(吨)"
            prop="netWeight"
        >
          <el-input-number
              v-model="formState.netWeight"
              :step="0.01"
              :min="0"
              style="width: 100%"
              disabled
          />
        </el-form-item>
        <el-form-item
            label="过磅日期"
            prop="weighingDate"
        >
          <el-date-picker
              style="width: 100%"
              v-model="formState.weighingDate"
              value-format="YYYY-MM-DD HH:mm:ss"
              format="YYYY-MM-DD HH:mm:ss"
              type="datetime"
              placeholder="请选择过磅日期"
              clearable
          />
        </el-form-item>
        <el-form-item
            label="过磅员"
            prop="weighingOperator"
        >
          <el-input v-model="formState.weighingOperator" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="formState.remark" type="textarea" />
        </el-form-item>
      </el-form>
      <!-- äº§å“é€‰æ‹©å¼¹çª— -->
      <ProductSelectDialog
          v-model="showProductSelectDialog"
          @confirm="handleProductSelect"
          single
      />
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref, computed, getCurrentInstance, onMounted} from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {subtractConsumablesIn} from "@/api/consumablesLogistics/consumablesIn.js";
import {subtractConsumablesUnInventory} from "@/api/consumablesLogistics/consumablesUninventory.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  record: {
    type: Object,
    default: () => {},
  },
  type: {
    type: String,
    required: true,
    default: 'qualified',
  },
});
const emit = defineEmits(['update:visible', 'completed']);
onMounted(() => {
  initFormData()
})
const isRawMaterial = computed(() => {
  return props.record.parentName === '原材料';
})
const initFormData = () => {
  if (props.record) {
    formState.value = {
      ...props.record,
    }
  }
}
// å“åº”式数据(替代选项式的 data)
const formState = ref({
  productId: undefined,
  productModelId: undefined,
  productName: "",
  model: "",
  unit: "",
  // è¿‡ç£…相关字段
  licensePlateNo: "",
  grossWeight: undefined,
  tareWeight: undefined,
  netWeight: undefined,
  weighingDate: undefined,
  weighingOperator: "",
  remark: '',
});
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
const showProductSelectDialog = ref(false);
let { proxy } = getCurrentInstance()
const closeModal = () => {
  // é‡ç½®è¡¨å•数据
  formState.value = {
    productId: undefined,
    productModelId: undefined,
    productName: "",
    model: "",
    unit: "",
    licensePlateNo: "",
    grossWeight: undefined,
    tareWeight: undefined,
    netWeight: undefined,
    weighingDate: undefined,
    weighingOperator: "",
    remark: '',
  };
  isShow.value = false;
};
// å‡€é‡ = æ¯›é‡ - çš®é‡
const computeNetWeight = () => {
  const { grossWeight, tareWeight } = formState.value;
  if (grossWeight != null && tareWeight != null) {
    const net = Number(grossWeight) - Number(tareWeight);
    const safeNet = Number(net.toFixed(2));
    formState.value.netWeight = safeNet > 0 ? safeNet : 0;
  } else {
    formState.value.netWeight = undefined;
  }
};
// äº§å“é€‰æ‹©å¤„理
const handleProductSelect = async (products) => {
  if (products && products.length > 0) {
    const product = products[0];
    console.log(product)
    formState.value.productId = product.productId;
    formState.value.productName = product.productName;
    formState.value.productModelName = product.model;
    formState.value.productModelId = product.id;
    formState.value.unit = product.unit;
    showProductSelectDialog.value = false;
    // è§¦å‘表单验证更新
    proxy.$refs["formRef"]?.validateField('productModelId');
  }
};
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      // éªŒè¯æ˜¯å¦é€‰æ‹©äº†äº§å“å’Œè§„æ ¼
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择产品");
        return;
      }
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择规格");
        return;
      }
      if (props.type === 'qualified') {
        subtractConsumablesIn(formState.value).then(res => {
          // å…³é—­æ¨¡æ€æ¡†
          isShow.value = false;
          // å‘ŠçŸ¥çˆ¶ç»„件已完成
          emit('completed');
          proxy.$modal.msgSuccess("提交成功");
        })
      } else {
        subtractConsumablesUnInventory(formState.value).then(res => {
          // å…³é—­æ¨¡æ€æ¡†
          isShow.value = false;
          // å‘ŠçŸ¥çˆ¶ç»„件已完成
          emit('completed');
          proxy.$modal.msgSuccess("提交成功");
        })
      }
    }
  })
};
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
});
</script>
src/views/consumablesLogistics/stockManagement/Unqualified.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,187 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title ml10">产品大类:</span>
        <el-input v-model="searchForm.productName"
                  style="width: 240px"
                  placeholder="请输入"
                  clearable/>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
         <el-button type="primary" @click="isShowNewModal = true">新增库存</el-button>
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="row => row.id" style="width: 100%"
        :row-class-name="tableRowClassName" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="产品大类" prop="productName" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="model" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip />
        <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip />
        <el-table-column label="备注" prop="remark"  show-overflow-tooltip />
        <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="60" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">领用</el-button>
            <el-button link type="primary" size="small" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">冻结</el-button>
            <el-button link type="primary" size="small" v-if="scope.row.lockedQuantity > 0" @click="showThawModal(scope.row)">解冻</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <new-stock-inventory v-if="isShowNewModal"
                 v-model:visible="isShowNewModal"
                 type="unqualified"
                 @completed="handleQuery" />
    <subtract-stock-inventory v-if="isShowSubtractModal"
                 v-model:visible="isShowSubtractModal"
                 :record="record"
                 type="unqualified"
                 @completed="handleQuery" />
    <!-- å†»ç»“/解冻库存-->
    <frozen-and-thaw-stock-inventory v-if="isShowFrozenAndThawModal"
                                     v-model:visible="isShowFrozenAndThawModal"
                                     :record="record"
                                     :operation-type="operationType"
                                     type="unqualified"
                                     @completed="handleQuery" />
  </div>
</template>
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
import { ElMessageBox } from "element-plus";
import { getConsumablesUninventoryListPage } from "@/api/consumablesLogistics/consumablesUninventory.js";
const NewStockInventory = defineAsyncComponent(() => import("@/views/consumablesLogistics/stockManagement/New.vue"));
const SubtractStockInventory = defineAsyncComponent(() => import("@/views/consumablesLogistics/stockManagement/Subtract.vue"));
const FrozenAndThawStockInventory = defineAsyncComponent(() => import("@/views/consumablesLogistics/stockManagement/FrozenAndThaw.vue"));
const { proxy } = getCurrentInstance()
const tableData = ref([])
const selectedRows = ref([])
const record = ref({})
const tableLoading = ref(false)
const page = reactive({
  current: 1,
  size: 100,
})
const total = ref(0)
// æ˜¯å¦æ˜¾ç¤ºæ–°å¢žå¼¹æ¡†
const isShowNewModal = ref(false)
// æ˜¯å¦æ˜¾ç¤ºé¢†ç”¨å¼¹æ¡†
const isShowSubtractModal = ref(false)
// æ˜¯å¦æ˜¾ç¤ºå†»ç»“/解冻弹框
const isShowFrozenAndThawModal = ref(false)
// æ“ä½œç±»åž‹
const operationType = ref('frozen')
const data = reactive({
  searchForm: {
    productName: '',
  }
})
const { searchForm } = toRefs(data)
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1
  getList()
}
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList()
}
const getList = () => {
  tableLoading.value = true
  getConsumablesUninventoryListPage({ ...searchForm.value, ...page }).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
    // æ•°æ®åŠ è½½å®ŒæˆåŽæ£€æŸ¥åº“å­˜
    // checkStockAndCreatePurchase();
  }).catch(() => {
    tableLoading.value = false
  })
}
// ç‚¹å‡»é¢†ç”¨
const showSubtractModal = (row) => {
  record.value = row
  isShowSubtractModal.value = true
}
// ç‚¹å‡»å†»ç»“
const showFrozenModal = (row) => {
  record.value = row
  isShowFrozenAndThawModal.value = true
  operationType.value = 'frozen'
}
// ç‚¹å‡»è§£å†»
const showThawModal = (row) => {
  record.value = row
  isShowFrozenAndThawModal.value = true
  operationType.value = 'thaw'
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  // è¿‡æ»¤æŽ‰å­æ•°æ®
  selectedRows.value = selection.filter(item => item.id);
  console.log('selection', selectedRows.value)
}
const expandedRowKeys = ref([])
// è¡¨æ ¼è¡Œç±»å
const tableRowClassName = ({ row }) => {
  // const stock = Number(row?.unLockedQuantity ?? 0);
  // const warn = Number(row?.warnNum ?? 0);
  // if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
  //   return '';
  // }
  // return stock < warn ? 'row-low-stock' : '';
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm(
    '是否确认导出?',
    '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }
  ).then(() => {
    proxy.download("/consumablesUninventory/exportStockUninventory", {}, '耗材不合格库存信息.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
onMounted(() => {
  getList()
})
</script>
<style scoped lang="scss">
:deep(.row-low-stock td) {
  background-color: #fde2e2;
  color: #c45656;
}
:deep(.row-low-stock:hover > td) {
  background-color: #fcd4d4;
}
</style>
src/views/consumablesLogistics/stockManagement/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane v-for="tab in tabs"
                   :label="tab.label"
                   :name="tab.name"
                   :key="tab.name">
        <component :is="tab.name === 'qualified' ? QualifiedRecord : UnqualifiedRecord" />
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script setup>
import QualifiedRecord from "@/views/consumablesLogistics/stockManagement/Qualified.vue";
import UnqualifiedRecord from "@/views/consumablesLogistics/stockManagement/Unqualified.vue";
const activeTab = ref('qualified')
const tabs = ref([
  {
    label: '合格库存',
    name: 'qualified'
  },
  {
    label: '不合格库存',
    name: 'unqualified'
  }
])
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
}
</script>
src/views/consumablesLogistics/stockReport/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,675 @@
<template>
  <div class="app-container">
    <!-- æœç´¢è¡¨å• -->
    <div class="search_form">
      <div class="search_left">
        <span class="search_title">报表类型:</span>
        <el-select
          v-model="searchForm.reportType"
          style="width: 150px;"
          placeholder="请选择"
          @change="handleReportTypeChange"
        >
          <el-option label="日报" value="daily" />
          <el-option label="月报" value="monthly" />
          <el-option label="进出存报表" value="inout" />
        </el-select>
        <span class="search_title ml10">时间范围:</span>
         <el-date-picker
           v-if="searchForm.reportType === 'daily'"
           v-model="searchForm.singleDate"
           type="date"
           placeholder="请选择日期"
           format="YYYY-MM-DD"
           value-format="YYYY-MM-DD"
           style="width: 200px;"
         />
        <el-date-picker
          v-else-if="searchForm.reportType === 'monthly'"
          v-model="searchForm.monthRange"
          type="monthrange"
          range-separator="至"
          start-placeholder="开始月份"
          end-placeholder="结束月份"
          format="YYYY-MM-DD"
          value-format="YYYY-MM-DD"
          style="width: 240px;"
        />
        <el-date-picker
          v-else
          v-model="searchForm.dateRange"
          type="daterange"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          format="YYYY-MM-DD"
          value-format="YYYY-MM-DD"
          style="width: 240px;"
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">
          æŸ¥è¯¢
        </el-button>
        <el-button @click="handleReset">重置</el-button>
      </div>
      <div class="search_right">
<!--        <el-button type="success" @click="handleExport" icon="Download">-->
<!--          å¯¼å‡ºæŠ¥è¡¨-->
<!--        </el-button>-->
      </div>
    </div>
<!--    &lt;!&ndash; ç»Ÿè®¡å¡ç‰‡ &ndash;&gt;-->
<!--    <div class="stats_cards" v-if="reportData.summary">-->
<!--      <el-row :gutter="20">-->
<!--        <el-col :span="6">-->
<!--          <el-card class="stats_card">-->
<!--            <div class="stats_content">-->
<!--              <div class="stats_icon in">-->
<!--                <el-icon><TrendCharts /></el-icon>-->
<!--              </div>-->
<!--              <div class="stats_info">-->
<!--                <div class="stats_value">{{ reportData.summary.totalIn || 0 }}</div>-->
<!--                <div class="stats_label">总入库量</div>-->
<!--              </div>-->
<!--            </div>-->
<!--          </el-card>-->
<!--        </el-col>-->
<!--        <el-col :span="6">-->
<!--          <el-card class="stats_card">-->
<!--            <div class="stats_content">-->
<!--              <div class="stats_icon out">-->
<!--                <el-icon><TrendCharts /></el-icon>-->
<!--              </div>-->
<!--              <div class="stats_info">-->
<!--                <div class="stats_value">{{ reportData.summary.totalOut || 0 }}</div>-->
<!--                <div class="stats_label">总出库量</div>-->
<!--              </div>-->
<!--            </div>-->
<!--          </el-card>-->
<!--        </el-col>-->
<!--        <el-col :span="6">-->
<!--          <el-card class="stats_card">-->
<!--            <div class="stats_content">-->
<!--              <div class="stats_icon stock">-->
<!--                <el-icon><Box /></el-icon>-->
<!--              </div>-->
<!--              <div class="stats_info">-->
<!--                <div class="stats_value">{{ reportData.summary.currentStock || 0 }}</div>-->
<!--                <div class="stats_label">当前库存</div>-->
<!--              </div>-->
<!--            </div>-->
<!--          </el-card>-->
<!--        </el-col>-->
<!--        <el-col :span="6">-->
<!--          <el-card class="stats_card">-->
<!--            <div class="stats_content">-->
<!--              <div class="stats_icon turnover">-->
<!--                <el-icon><Refresh /></el-icon>-->
<!--              </div>-->
<!--              <div class="stats_info">-->
<!--                <div class="stats_value">{{ reportData.summary.turnoverRate || 0 }}%</div>-->
<!--                <div class="stats_label">周转率</div>-->
<!--              </div>-->
<!--            </div>-->
<!--          </el-card>-->
<!--        </el-col>-->
<!--      </el-row>-->
<!--    </div>-->
<!--    &lt;!&ndash; å›¾è¡¨åŒºåŸŸ &ndash;&gt;-->
<!--    <div class="chart_section" v-if="reportData.chartData">-->
<!--      <el-row :gutter="20">-->
<!--        <el-col :span="12">-->
<!--          <el-card>-->
<!--            <template #header>-->
<!--              <span>库存趋势图</span>-->
<!--            </template>-->
<!--            <div ref="trendChart" style="height: 300px;"></div>-->
<!--          </el-card>-->
<!--        </el-col>-->
<!--        <el-col :span="12">-->
<!--          <el-card>-->
<!--            <template #header>-->
<!--              <span>进出库对比</span>-->
<!--            </template>-->
<!--            <div ref="comparisonChart" style="height: 300px;"></div>-->
<!--          </el-card>-->
<!--        </el-col>-->
<!--      </el-row>-->
<!--    </div>-->
    <!-- è¯¦ç»†æ•°æ®è¡¨æ ¼ -->
    <div class="table_section">
      <el-card>
        <template #header>
          <span>{{ getTableTitle() }}</span>
        </template>
         <el-table
           v-loading="tableLoading"
           :data="reportData.tableData"
           border
           height="400"
           style="width: 100%"
           :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
         >
          <el-table-column
            align="center"
            label="序号"
            type="index"
            width="60"
          />
           <el-table-column
             label="入库时间"
             prop="createTime"
             width="200"
             show-overflow-tooltip
             v-if="searchForm.reportType !== 'inout'"
           />
           <el-table-column
             label="入库批次"
             prop="inboundBatches"
             width="240"
             show-overflow-tooltip
             v-if="searchForm.reportType !== 'inout'"
           />
           <el-table-column
             label="产品大类"
             prop="productName"
             show-overflow-tooltip
           />
           <el-table-column
             label="规格型号"
             prop="model"
             show-overflow-tooltip
           />
           <el-table-column
             label="单位"
             prop="unit"
             show-overflow-tooltip
           />
           <el-table-column
             label="入库数量"
             prop="totalStockIn"
             align="center"
             v-if="searchForm.reportType === 'inout'"
           />
           <el-table-column
               label="入库数量"
               prop="stockInNum"
               align="center"
               v-else
           />
           <el-table-column
             label="出库数量"
             prop="totalStockOut"
             width="100"
             align="center"
             v-if="searchForm.reportType === 'inout'"
           />
           <el-table-column
             label="现在库存"
             prop="currentStock"
             align="center"
           />
            <el-table-column
             label="现净重(吨)"
             prop="currentWeight"
             align="center"
           />
           <el-table-column label="来源"
                            prop="recordType"
                            v-if="searchForm.reportType !== 'inout'"
                            show-overflow-tooltip>
             <template #default="scope">
               {{ getRecordType(scope.row.recordType) }}
             </template>
           </el-table-column>
           <el-table-column
             label="入库人"
             prop="createBy"
             width="80"
             v-if="searchForm.reportType !== 'inout'"
             show-overflow-tooltip
           />
        </el-table>
      </el-card>
    </div>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus'
import * as echarts from 'echarts'
import {
  getConsumablesInInAndOutReportList,
  getConsumablesInReportList
} from "@/api/consumablesLogistics/consumablesIn.js";
import {
  findAllQualifiedStockInRecordTypeOptions,
} from "@/api/basicData/enum.js";
const { proxy } = getCurrentInstance()
// å“åº”式数据
const tableLoading = ref(false)
const trendChart = ref(null)
const comparisonChart = ref(null)
const searchForm = reactive({
  reportType: 'daily',
  singleDate: '',
  dateRange: [],
  monthRange: []
})
const reportData = ref({
  summary: null,
  chartData: null,
  tableData: []
})
const stockRecordTypeOptions = ref([])
const getRecordType = (recordType) => {
  return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
}
// èŽ·å–æ¥æºç±»åž‹é€‰é¡¹
const fetchStockRecordTypeOptions = () => {
  findAllQualifiedStockInRecordTypeOptions()
      .then(res => {
        stockRecordTypeOptions.value = res.data;
      })
}
// èŽ·å–è¡¨æ ¼æ ‡é¢˜
const getTableTitle = () => {
  const typeMap = {
    daily: '日报详细数据',
    monthly: '月报详细数据',
    inout: '进出存报表详细数据'
  }
  return typeMap[searchForm.reportType] || '报表详细数据'
}
// æŠ¥è¡¨ç±»åž‹æ”¹å˜
const handleReportTypeChange = () => {
  reportData.value = {
    summary: null,
    chartData: null,
    tableData: []
  }
}
// æŸ¥è¯¢æ•°æ®
const handleQuery = async () => {
  if (!validateSearchForm()) {
    return
  }
  tableLoading.value = true
  try {
    const params = getQueryParams()
    let response
    if (searchForm.reportType === 'inout') {
      response = await getConsumablesInInAndOutReportList(params)
    } else {
      response = await getConsumablesInReportList(params)
    }
    if (response.code === 200) {
      reportData.value.tableData = response.data.records
      // reportData.value.summary = response.data.summary
      // reportData.value.chartData = response.data.chartData
      // nextTick(() => {
      //   initCharts()
      // })
    }
  } catch (error) {
    ElMessage.error('查询失败:' + error.message)
  } finally {
    tableLoading.value = false
  }
}
// // ç”Ÿæˆå‡æ•°æ®
// const generateMockData = () => {
//   // ç”Ÿæˆç»Ÿè®¡å¡ç‰‡å‡æ•°æ®
//   const summary = {
//     totalIn: 1000,
//     totalOut: 600,
//     currentStock: 400,
//     turnoverRate: 30
//   }
//   // ç”Ÿæˆå›¾è¡¨å‡æ•°æ®
//   const trendDates = ['2025-09-15', '2025-09-16', '2025-09-17', '2025-09-18', '2025-09-19']
//   const trendValues = [300, 350, 400, 380, 420]
//   const comparisonDates = ['2025-09-15', '2025-09-16', '2025-09-17']
//   const inValues = [100, 150, 200]
//   const outValues = [80, 120, 100]
//   const chartData = {
//     trendDates,
//     trendValues,
//     comparisonDates,
//     inValues,
//     outValues
//   }
//   reportData.value = {
//     summary,
//     chartData,
//     tableData: []
//   }
// }
// éªŒè¯æœç´¢è¡¨å•
const validateSearchForm = () => {
  if (searchForm.reportType === 'daily') {
    if (!searchForm.singleDate) {
      ElMessage.warning('请选择日期')
      return false
    }
  } else if (searchForm.reportType === 'inout') {
    if (!searchForm.dateRange || searchForm.dateRange.length !== 2) {
      ElMessage.warning('请选择日期范围')
      return false
    }
  } else if (searchForm.reportType === 'monthly') {
    if (!searchForm.monthRange || searchForm.monthRange.length !== 2) {
      ElMessage.warning('请选择月份范围')
      return false
    }
  }
  return true
}
// èŽ·å–æŸ¥è¯¢å‚æ•°
const getQueryParams = () => {
  const params = {
    reportType: searchForm.reportType,
    reportDate: "",
    startMonth: "",
    endMonth: "",
    startDate: "",
    endDate: ""
  }
  if (searchForm.reportType === 'daily') {
    params.reportDate = searchForm.singleDate
  } else if (searchForm.reportType === 'monthly') {
    params.startMonth = searchForm.monthRange[0]
    params.endMonth = searchForm.monthRange[1]
  } else {
    params.startDate = searchForm.dateRange[0]
    params.endDate = searchForm.dateRange[1]
  }
  return params
}
// é‡ç½®æœç´¢
const handleReset = () => {
  searchForm.reportType = 'daily'
  searchForm.singleDate = ''
  searchForm.dateRange = []
  searchForm.monthRange = []
  reportData.value = {
    summary: null,
    chartData: null,
    tableData: []
  }
}
// å¯¼å‡ºæŠ¥è¡¨
const handleExport = async () => {
  if (!validateSearchForm()) {
    return
  }
  try {
    const params = getQueryParams()
    // const response = await exportStockReport(params)
    proxy.download("/consumablesInventory/exportConsumablesInventory", params, '耗材库存报表.xlsx')
    // åˆ›å»ºä¸‹è½½é“¾æŽ¥
    // const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
    // const url = window.URL.createObjectURL(blob)
    // const link = document.createElement('a')
    // link.href = url
    // link.download = `${getTableTitle()}_${new Date().getTime()}.xlsx`
    // document.body.appendChild(link)
    // link.click()
    // document.body.removeChild(link)
    // window.URL.revokeObjectURL(url)
    // ElMessage.success('导出成功')
  } catch (error) {
    ElMessage.error('导出失败:' + error.message)
  }
}
// åˆå§‹åŒ–图表
const initCharts = () => {
  if (!reportData.value.chartData) return
  initTrendChart()
  initComparisonChart()
}
// åˆå§‹åŒ–趋势图
const initTrendChart = () => {
  if (!trendChart.value) return
  const chart = echarts.init(trendChart.value)
  const option = {
    title: {
      text: '库存变化趋势',
      left: 'center'
    },
    tooltip: {
      trigger: 'axis'
    },
    legend: {
      data: ['库存量'],
      top: 30
    },
    xAxis: {
      type: 'category',
      data: reportData.value.chartData.trendDates || []
    },
    yAxis: {
      type: 'value'
    },
    series: [{
      name: '库存量',
      type: 'line',
      data: reportData.value.chartData.trendValues || [],
      smooth: true,
      itemStyle: {
        color: '#409EFF'
      }
    }]
  }
  chart.setOption(option)
}
// åˆå§‹åŒ–对比图
const initComparisonChart = () => {
  if (!comparisonChart.value) return
  const chart = echarts.init(comparisonChart.value)
  const option = {
    title: {
      text: '进出库对比',
      left: 'center'
    },
    tooltip: {
      trigger: 'axis'
    },
    legend: {
      data: ['入库', '出库'],
      top: 30
    },
    xAxis: {
      type: 'category',
      data: reportData.value.chartData.comparisonDates || []
    },
    yAxis: {
      type: 'value'
    },
    series: [
      {
        name: '入库',
        type: 'bar',
        data: reportData.value.chartData.inValues || [],
        itemStyle: {
          color: '#67C23A'
        }
      },
      {
        name: '出库',
        type: 'bar',
        data: reportData.value.chartData.outValues || [],
        itemStyle: {
          color: '#F56C6C'
        }
      }
    ]
  }
  chart.setOption(option)
}
// ç»„件挂载时设置默认时间
onMounted(() => {
  const today = new Date()
  searchForm.singleDate = today.toISOString().split('T')[0]
  const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
  searchForm.dateRange = [
    yesterday.toISOString().split('T')[0],
    today.toISOString().split('T')[0]
  ]
  fetchStockRecordTypeOptions()
})
</script>
<style scoped>
.app-container {
  padding: 20px;
}
.search_form {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding: 20px;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.search_left {
  display: flex;
  align-items: center;
}
.search_title {
  font-weight: 500;
  color: #333;
  margin-right: 8px;
}
.ml10 {
  margin-left: 10px;
}
.stats_cards {
  margin-bottom: 20px;
}
.stats_card {
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.stats_content {
  display: flex;
  align-items: center;
  padding: 10px 0;
}
.stats_icon {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: 15px;
  font-size: 24px;
  color: #fff;
}
.stats_icon.in {
  background: linear-gradient(135deg, #67C23A, #85CE61);
}
.stats_icon.out {
  background: linear-gradient(135deg, #F56C6C, #F78989);
}
.stats_icon.stock {
  background: linear-gradient(135deg, #409EFF, #66B1FF);
}
.stats_icon.turnover {
  background: linear-gradient(135deg, #E6A23C, #EEBE77);
}
.stats_info {
  flex: 1;
}
.stats_value {
  font-size: 24px;
  font-weight: bold;
  color: #333;
  line-height: 1;
  margin-bottom: 5px;
}
.stats_label {
  font-size: 14px;
  color: #666;
}
.chart_section {
  margin-bottom: 20px;
}
.table_section {
  margin-bottom: 20px;
}
:deep(.el-card__header) {
  background: #f8f9fa;
  border-bottom: 1px solid #e9ecef;
  font-weight: 500;
}
:deep(.el-table .el-table__header-wrapper th) {
  background-color: #F0F1F5 !important;
  color: #333333;
  font-weight: 600;
}
:deep(.el-table .el-table__body-wrapper td) {
  padding: 8px 0;
}
</style>
src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -72,6 +72,28 @@
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="24">
          <el-form-item label="现场照片">
            <div class="repair-upload-area">
              <el-upload
                v-model:file-list="uploadFileList"
                :action="uploadUrl"
                :headers="uploadHeaders"
                accept="image/*"
                list-type="picture-card"
                :limit="uploadLimit"
                :on-success="handleUploadSuccess"
                :on-remove="handleRemoveFile"
                :before-upload="beforeUpload"
              >
                <el-icon><Plus /></el-icon>
              </el-upload>
              <div v-if="repairFileList.length === 0" class="upload-tip">请上传现场照片,最多 {{ uploadLimit }} å¼ </div>
            </div>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </FormDialog>
</template>
@@ -84,10 +106,12 @@
  getRepairById,
} from "@/api/equipmentManagement/repair";
import { ElMessage } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import dayjs from "dayjs";
import useFormData from "@/hooks/useFormData";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth";
defineOptions({
  name: "设备报修弹窗",
@@ -102,24 +126,83 @@
const userStore = useUserStore();
const deviceOptions = ref([]);
// çŽ°åœºç…§ç‰‡ä¸Šä¼ ï¼ˆä¸Ž APP å­—段一致:fileList / commonFileList)
const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/file/upload";
const uploadLimit = 10;
const uploadFileList = ref([]);
const repairFileList = ref([]);
const uploadHeaders = { Authorization: "Bearer " + getToken() };
const baseApi = import.meta.env.VITE_APP_BASE_API || "";
const formatFileUrl = (url) => {
  if (!url) return "";
  if (url.startsWith("http://") || url.startsWith("https://")) return url;
  const path = url.replace(/^\/+/, "");
  return path ? baseApi + "/" + path : baseApi;
};
const loadDeviceName = async () => {
  const { data } = await getDeviceLedger();
  deviceOptions.value = data;
};
const { form, resetForm } = useFormData({
  deviceLedgerId: undefined, // è®¾å¤‡Id
  deviceName: undefined, // è®¾å¤‡åç§°
  deviceModel: undefined, // è§„格型号
  repairTime: dayjs().format("YYYY-MM-DD"), // æŠ¥ä¿®æ—¥æœŸï¼Œé»˜è®¤å½“天
  repairName: userStore.nickName, // æŠ¥ä¿®äºº
  remark: undefined, // æ•…障现象
  status: 0, // æŠ¥ä¿®çŠ¶æ€
  deviceLedgerId: undefined,
  deviceName: undefined,
  deviceModel: undefined,
  repairTime: dayjs().format("YYYY-MM-DD"),
  repairName: userStore.nickName,
  remark: undefined,
  status: 0,
});
const setDeviceModel = (deviceId) => {
  const option = deviceOptions.value.find((item) => item.id === deviceId);
  form.deviceModel = option.deviceModel;
};
const beforeUpload = (file) => {
  const isImage = file.type.startsWith("image/");
  if (!isImage) {
    ElMessage.warning("只能上传图片");
    return false;
  }
  if (repairFileList.value.length >= uploadLimit) {
    ElMessage.warning(`最多上传 ${uploadLimit} å¼ å›¾ç‰‡`);
    return false;
  }
  return true;
};
const handleUploadSuccess = (res, file) => {
  if (res?.code !== 200 && res?.code !== 0) {
    ElMessage.error(res?.msg || "上传失败");
    const idx = uploadFileList.value.findIndex((f) => f.uid === file.uid);
    if (idx > -1) uploadFileList.value.splice(idx, 1);
    return;
  }
  const d = res?.data !== undefined ? res.data : res;
  if (!d) return;
  const item = {
    uid: file.uid,
    id: d.id,
    url: d.url || d.downloadUrl || d.tempPath,
    downloadUrl: d.downloadUrl || d.url,
    bucketFilename: d.bucketFilename || d.originalFilename || file.name,
    originalFilename: d.originalFilename || d.bucketFilename || file.name,
    name: d.bucketFilename || d.originalFilename || file.name,
    size: d.size ?? d.byteSize ?? file.size,
    byteSize: d.byteSize ?? d.size ?? file.size,
    uploadResponse: res,
  };
  repairFileList.value.push(item);
};
const handleRemoveFile = (file) => {
  const targetUrl = file.url || file.response?.data?.url || file.response?.data?.downloadUrl;
  repairFileList.value = repairFileList.value.filter(
    (f) => f.uid !== file.uid && (f.url || f.downloadUrl) !== targetUrl
  );
};
const setForm = (data) => {
@@ -130,14 +213,39 @@
  form.repairName = data.repairName;
  form.remark = data.remark;
  form.status = data.status;
  const list = data.fileList || data.commonFileList || [];
  repairFileList.value = (Array.isArray(list) ? list : []).map((f, i) => ({
    uid: f.id || "existing-" + i,
    id: f.id,
    url: f.url || f.downloadUrl,
    downloadUrl: f.downloadUrl || f.url,
    bucketFilename: f.bucketFilename || f.originalFilename || f.name,
    originalFilename: f.originalFilename || f.bucketFilename || f.name,
    name: f.bucketFilename || f.originalFilename || f.name || "图片",
    size: f.size ?? f.byteSize,
    byteSize: f.byteSize ?? f.size,
    ...f,
  }));
  uploadFileList.value = repairFileList.value.map((f, i) => ({
    uid: f.uid || "existing-" + i,
    name: f.name || f.bucketFilename || "图片",
    url: formatFileUrl(f.url || f.downloadUrl),
    status: "success",
  }));
};
const sendForm = async () => {
  loading.value = true;
  try {
    const fileList = repairFileList.value.map((f) => {
      const d = f.uploadResponse?.data !== undefined ? f.uploadResponse.data : f.uploadResponse;
      return d ? { ...d } : (f.id || f.url ? { ...f } : null);
    }).filter(Boolean);
    const submitData = { ...form };
    if (fileList.length) submitData.fileList = fileList;
    const { code } = id.value
      ? await editRepair({ id: unref(id), ...form })
      : await addRepair(form);
      ? await editRepair({ id: unref(id), ...submitData })
      : await addRepair(submitData);
    if (code == 200) {
      ElMessage.success(`${id.value ? "编辑" : "新增"}报修成功`);
      visible.value = false;
@@ -150,16 +258,22 @@
const handleCancel = () => {
  resetForm();
  repairFileList.value = [];
  uploadFileList.value = [];
  visible.value = false;
};
const handleClose = () => {
  resetForm();
  repairFileList.value = [];
  uploadFileList.value = [];
  visible.value = false;
};
const openAdd = async () => {
  id.value = undefined;
  repairFileList.value = [];
  uploadFileList.value = [];
  visible.value = true;
  await nextTick();
  await loadDeviceName();
@@ -180,4 +294,15 @@
});
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.repair-upload-area {
  :deep(.el-upload-list--picture-card) {
    --el-upload-list-picture-card-size: 100px;
  }
}
.upload-tip {
  font-size: 12px;
  color: var(--el-text-color-secondary);
  margin-top: 8px;
}
</style>
src/views/equipmentManagement/repair/index.vue
@@ -112,6 +112,13 @@
            ç¼–辑
          </el-button>
          <el-button
            type="info"
            link
            @click="viewAttachments(row)"
          >
            æŸ¥çœ‹é™„ä»¶
          </el-button>
          <el-button
            type="success"
            link
            :disabled="row.status === 1"
@@ -132,17 +139,26 @@
    </div>
    <RepairModal ref="repairModalRef" @ok="getTableData"/>
    <MaintainModal ref="maintainModalRef" @ok="getTableData"/>
    <FileListDialog
      ref="fileListDialogRef"
      v-model="fileDialogVisible"
      title="查看附件"
      :show-upload-button="false"
      :show-delete-button="false"
      name-column-label="附件名称"
    />
  </div>
</template>
<script setup>
import { onMounted, getCurrentInstance, computed } from "vue";
import {usePaginationApi} from "@/hooks/usePaginationApi";
import {getRepairPage, delRepair} from "@/api/equipmentManagement/repair";
import {getRepairPage, delRepair, getRepairById} from "@/api/equipmentManagement/repair";
import RepairModal from "./Modal/RepairModal.vue";
import {ElMessageBox, ElMessage} from "element-plus";
import dayjs from "dayjs";
import MaintainModal from "./Modal/MaintainModal.vue";
import FileListDialog from "@/components/Dialog/FileListDialog.vue";
defineOptions({
  name: "设备报修",
@@ -153,6 +169,37 @@
// æ¨¡æ€æ¡†å®žä¾‹
const repairModalRef = ref();
const maintainModalRef = ref();
const fileListDialogRef = ref();
const fileDialogVisible = ref(false);
const baseApi = import.meta.env.VITE_APP_BASE_API || "";
const formatFileUrl = (url) => {
  if (!url) return "";
  if (url.startsWith("http://") || url.startsWith("https://")) return url;
  const path = String(url).replace(/^\/+/, "");
  return path ? baseApi + "/" + path : baseApi;
};
// æŸ¥çœ‹é™„件(与 APP å­—段一致:fileList / commonFileList)
const viewAttachments = async (row) => {
  try {
    const { code, data } = await getRepairById(row.id);
    if (code === 200 && data) {
      const list = data.fileList || data.commonFileList || [];
      const mapped = (Array.isArray(list) ? list : []).map((f) => ({
        id: f.id,
        name: f.originalFilename || f.bucketFilename || f.name || "附件",
        url: formatFileUrl(f.url || f.downloadUrl),
        raw: f,
      }));
      fileListDialogRef.value?.open(mapped);
    } else {
      ElMessage.warning("获取附件失败");
    }
  } catch (e) {
    ElMessage.error("获取附件失败");
  }
};
// è¡¨æ ¼å¤šé€‰æ¡†é€‰ä¸­é¡¹
const multipleList = ref([]);
@@ -232,7 +279,7 @@
        dataType: "slot",
        slot: "operation",
        align: "center",
        width: "300px",
        width: "360px",
      },
    ]
);
src/views/inventoryManagement/dispatchLog/Record.vue
@@ -77,6 +77,21 @@
                    show-overflow-tooltip
                />
                <el-table-column
                    label="车牌号"
                    prop="licensePlateNo"
                    show-overflow-tooltip
                />
                <el-table-column
                    label="毛重(吨)"
                    prop="grossWeight"
                    show-overflow-tooltip
                />
                <el-table-column
                    label="皮重(吨)"
                    prop="tareWeight"
                    show-overflow-tooltip
                />
                <el-table-column
                    label="净重(吨)"
                    prop="netWeight"
                    show-overflow-tooltip
@@ -94,10 +109,24 @@
          </template>
        </el-table-column>
        <el-table-column
            label="车牌"
            prop="licensePlateNo"
            label="过磅日期"
            prop="weighingDate"
            show-overflow-tooltip
        />
        <el-table-column
            label="过磅员"
            prop="weighingOperator"
            show-overflow-tooltip
        />
        <el-table-column label="操作"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button type="primary"
                       size="mini"
                       @click="handlePreview(scope.row)">导出过磅单</el-button>
          </template>
        </el-table-column>
            </el-table>
            <pagination
                v-show="total > 0"
@@ -214,6 +243,11 @@
};
const expandedRowKeys = ref([]);
// å¯¼å‡ºè¿‡ç£…单
const handlePreview = (row) => {
  proxy.$download.name(row.weighbridgeDocPath);
}
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("是否确认导出?", "导出", {
src/views/inventoryManagement/receiptManagement/Record.vue
@@ -107,6 +107,15 @@
                         prop="weighingOperator"
                         v-if="type === '0'"
                         show-overflow-tooltip/>
        <el-table-column label="操作"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button type="primary"
                       size="mini"
                       @click="handlePreview(scope.row)">导出过磅单</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0"
                  :total="total"
@@ -218,6 +227,11 @@
const expandedRowKeys = ref([]);
// å¯¼å‡ºè¿‡ç£…单
const handlePreview = (row) => {
  proxy.$download.name(row.weighbridgeDocPath);
}
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("是否确认导出?", "导出", {
src/views/inventoryManagement/stockManagement/Qualified.vue
@@ -142,6 +142,7 @@
// ç‚¹å‡»é¢†ç”¨
const showSubtractModal = (row) => {
  console.log('row', row)
  record.value = row
  isShowSubtractModal.value = true
}
src/views/qualityManagement/InspectItem/index.vue
@@ -47,9 +47,9 @@
        <el-form-item label="内控值" prop="internalControl">
          <el-input v-model="form.internalControl" placeholder="请输入内控值" clearable style="width: 280px" />
        </el-form-item>
        <el-form-item label="化验值" prop="testValue">
        <!-- <el-form-item label="化验值" prop="testValue">
          <el-input v-model="form.testValue" placeholder="请输入化验值" clearable style="width: 280px" />
        </el-form-item>
        </el-form-item> -->
      </el-form>
      <template #footer>
        <div class="dialog-footer">
@@ -98,7 +98,7 @@
  { label: "单位", prop: "unit", width: 120 },
  { label: "标准值", prop: "standardValue", width: 160 },
  { label: "内控值", prop: "internalControl", width: 160 },
  { label: "化验值", prop: "testValue", width: 160 },
  // { label: "化验值", prop: "testValue", width: 160 },
  {
    dataType: "action",
    label: "操作",
src/views/qualityManagement/nonconformingManagement/index.vue
@@ -5,9 +5,9 @@
        <div>
          <span class="search_title">类型:</span>
          <el-select v-model="searchForm.inspectType" clearable style="width: 200px" @change="handleQuery">
            <el-option label="原材料检验" :value="0" />
            <el-option label="过程检验" :value="1" />
            <el-option label="出厂检验" :value="2" />
            <el-option label="入厂检" :value="0" />
            <el-option label="车间检" :value="1" />
            <el-option label="出厂检" :value="2" />
          </el-select>
        </div>
        <div style="margin-left: 10px">
@@ -114,11 +114,11 @@
    width: 120,
    formatData: (params) => {
      if (params == 0) {
        return "原材料检验";
        return "入厂检";
      } else if (params == 1) {
        return "过程检验";
        return "车间检";
      } else {
        return '出厂检验';
        return '出厂检';
      }
    },
    formatType: (params) => {
src/views/qualityManagement/rawMaterial/components/formDia.vue
@@ -27,9 +27,10 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select v-model="form.productModelId" placeholder="请选择" clearable :disabled="operationType === 'edit'"
              <el-select v-model="form.productModelId" placeholder="请选择" clearable
                         :disabled="operationType === 'edit'"
                         filterable readonly @change="handleChangeModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id"/>
              </el-select>
            </el-form-item>
          </el-col>
@@ -67,8 +68,8 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-select v-model="form.checkName" placeholder="请选择" clearable filterable style="width: 100%">
            <el-form-item label="检验员:" prop="checkUserName">
              <el-select v-model="form.checkUserName" placeholder="请选择" clearable filterable style="width: 100%">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                           :value="item.nickName"/>
              </el-select>
@@ -98,6 +99,7 @@
          :column="tableColumn"
          :tableData="tableData"
          :tableLoading="tableLoading"
          :is-show-pagination="false"
          height="400"
      >
        <template #slot="{ row }">
@@ -112,7 +114,7 @@
      </template>
    </el-dialog>
    <item-select v-model="isShowItems" @confirm="handleItemSelect" />
    <item-select v-model="isShowItems" @confirm="handleItemSelect"/>
  </div>
</template>
@@ -122,7 +124,7 @@
import {qualityInspectParamInfo} from "@/api/qualityManagement/qualityInspectParam.js";
import {qualityInspectDetailByProductId} from "@/api/qualityManagement/metricMaintenance.js";
import {userListNoPage} from "@/api/system/user.js";
import {createRawMaterial, updateRawMaterial} from "@/api/qualityManagement/rawMaterial.js";
import {createRawMaterial, findRawMaterialDetail, updateRawMaterial} from "@/api/qualityManagement/rawMaterial.js";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
const {proxy} = getCurrentInstance()
@@ -136,34 +138,30 @@
  form: {
    checkTime: "",
    supplier: "",
    checkName: "",
    productName: "",
    productId: "",
    productModelId: "",
    model: "",
    batchNo: "",
    unit: "",
    quantity: "",
    checkCompany: "",
    checkType: undefined,
    checkResult: "",
    unit: "",
    checkUserName: "",
  },
  rules: {
    checkTime: [{required: true, message: "请输入", trigger: "blur"},],
    supplier: [{required: true, message: "请输入", trigger: "blur"}],
    checkName: [{required: false, message: "请输入", trigger: "blur"}],
    checkUserName: [{required: false, message: "请选择检验员", trigger: "blur"}],
    productId: [{required: true, message: "请输入", trigger: "blur"}],
    productModelId: [{required: true, message: "请选择产品型号", trigger: "change"}],
    batchNo: [{required: false, message: "请输入批次", trigger: "blur"}],
    unit: [{required: false, message: "请输入", trigger: "blur"}],
    quantity: [{required: true, message: "请输入", trigger: "blur"}],
    checkCompany: [{required: false, message: "请输入", trigger: "blur"}],
    batchNo: [{required: true, message: "请输入批次", trigger: "blur"}],
    checkType: [{required: true, message: "请选择检验类型", trigger: "change"}],
    checkResult: [{required: true, message: "请选择检测结果", trigger: "change"}],
  },
});
const tableColumn = ref([
  {
    label: "指标",
    prop: "parameterItem",
    label: "检测项目",
    prop: "name",
  },
  {
    label: "单位",
@@ -175,7 +173,7 @@
  },
  {
    label: "内控值",
    prop: "controlValue",
    prop: "internalControl",
  },
  {
    label: "化验值",
@@ -183,6 +181,20 @@
    dataType: 'slot',
    slot: 'slot',
  },
  {
    dataType: 'action',
    label: '操作',
    align: 'center',
    fixed: 'right',
    width: 140,
    operation: [
      {
        name: '删除',
        type: 'text',
        clickFun: (row) => handleDelete(row.id),
      }
    ]
  }
]);
const tableData = ref([]);
const tableLoading = ref(false);
@@ -191,7 +203,6 @@
const userList = ref([]);
const productOptions = ref([]);
const currentProductId = ref(0);
const testStandardOptions = ref([]); // æŒ‡æ ‡é€‰æ‹©ä¸‹æ‹‰æ¡†æ•°æ®
const modelOptions = ref([]);
// æ‰“开弹框
@@ -201,31 +212,29 @@
    userList.value = res.data || [];
  })
  // å…ˆé‡ç½®è¡¨å•数据(保持字段完整,避免弹窗首次渲染时触发必填红框“闪一下”)
    form.value = {
  form.value = {
    checkTime: "",
    supplier: "",
    checkName: "",
    productName: "",
    productId: "",
    productModelId: "",
    model: "",
    batchNo: "",
    unit: "",
    quantity: "",
    checkCompany: "",
    checkType: "",
    checkResult: "",
    unit: "",
    checkUserName: "",
  }
  testStandardOptions.value = [];
  tableData.value = [];
  // å…ˆç¡®ä¿äº§å“æ ‘已加载,否则编辑时产品/规格型号无法反显
  await getProductOptions();
  if (operationType.value === 'edit') {
    form.value = {...row}
    await fetchData(row.id);
    currentProductId.value = row.productId || 0
    // å…³é”®ï¼šç¼–辑时加载规格型号下拉选项,才能反显 productModelId
    if (currentProductId.value) {
      try {
        const res = await modelList({ id: currentProductId.value });
        const res = await modelList({id: currentProductId.value});
        modelOptions.value = res || [];
        // åŒæ­¥å›žå¡« model / unit(有些接口返回的 row é‡Œå¯èƒ½æ²¡å¸¦å…¨ï¼‰
        if (form.value.productModelId) {
@@ -261,7 +270,7 @@
  modelOptions.value = [];
  currentProductId.value = value
  form.value.productName = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
  modelList({id: value}).then((res) => {
    modelOptions.value = res;
  })
  if (currentProductId.value) {
@@ -270,6 +279,8 @@
};
const handleItemSelect = (value) => {
  // è¿‡æ»¤å·²å­˜åœ¨çš„æŒ‡æ ‡
  value = value.filter(item => !tableData.value.some(existingItem => existingItem.id === item.id));
  tableData.value.push(...value)
}
@@ -312,13 +323,7 @@
const submitForm = () => {
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      form.value.inspectType = 0
            if (operationType.value === "add") {
                tableData.value.forEach((item) => {
                    delete item.id
                })
            }
      const data = {...form.value, qualityInspectParams: tableData.value}
      const data = {...form.value, qualityInspectItem: tableData.value}
      if (operationType.value === "add") {
        createRawMaterial(data).then(res => {
          proxy.$modal.msgSuccess("提交成功");
@@ -336,7 +341,6 @@
const getList = () => {
  if (!currentProductId.value) {
    testStandardOptions.value = [];
    tableData.value = [];
    return;
  }
@@ -358,10 +362,23 @@
const closeDia = () => {
  proxy.resetForm("formRef");
  tableData.value = [];
  testStandardOptions.value = [];
  dialogFormVisible.value = false;
  emit('close')
};
const handleDelete = (id) => {
  tableData.value = tableData.value.filter(item => item.id !== id);
}
const fetchData = (id) => {
  tableLoading.value = true;
  findRawMaterialDetail(id).then(res => {
    form.value = res.data;
    tableData.value = res.data.qualityInspectItem;
  }).finally(() => {
    tableLoading.value = false;
  })
}
defineExpose({
  openDialog,
});
@@ -369,4 +386,4 @@
<style scoped>
</style>
</style>
src/views/qualityManagement/rawMaterial/index.vue
@@ -2,15 +2,38 @@
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">供应商:</span>
        <span class="search_title">批号:</span>
        <el-input
            v-model="searchForm.supplier"
            style="width: 240px"
            placeholder="请输入供应商搜索"
            v-model="searchForm.batchNo"
            style="width: 200px"
            placeholder="请输入"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
            prefix-icon="Search"
        />
        <span class="search_title">检验类型:</span>
        <el-select
            v-model="searchForm.checkType"
            style="width: 200px"
            placeholder="请选择"
            @change="handleQuery"
            clearable
        >
          <el-option label="入厂检" :value="0" />
          <el-option label="车间检" :value="1" />
          <el-option label="出厂检" :value="2" />
        </el-select>
        <span class="search_title">提交状态:</span>
        <el-select
            v-model="searchForm.inspectState"
            style="width: 200px"
            placeholder="请选择"
            @change="handleQuery"
            clearable
        >
          <el-option label="未提交" :value="0" />
          <el-option label="已提交" :value="1" />
        </el-select>
        <span style="margin-left: 10px" class="search_title">检测日期:</span>
        <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                        placeholder="请选择" clearable @change="changeDaterange"/>
@@ -44,8 +67,8 @@
    <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="30%"
               @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-form-item label="检验员:" prop="checkName">
          <el-select v-model="form.checkName" placeholder="请选择" clearable>
        <el-form-item label="检验员:" prop="checkUserName">
          <el-select v-model="form.checkUserName" placeholder="请选择" clearable>
            <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                       :value="item.nickName"/>
          </el-select>
@@ -63,31 +86,31 @@
</template>
<script setup>
import {Search} from "@element-plus/icons-vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import InspectionFormDia from "@/views/qualityManagement/rawMaterial/components/inspectionFormDia.vue";
import FormDia from "@/views/qualityManagement/rawMaterial/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {
  downloadQualityInspect,
  qualityInspectUpdate,
  submitQualityInspect
} from "@/api/qualityManagement/rawMaterialInspection.js";
import FilesDia from "@/views/qualityManagement/rawMaterial/components/filesDia.vue";
import dayjs from "dayjs";
import {userListNoPage} from "@/api/system/user.js";
import useUserStore from "@/store/modules/user";
import {deleteRawMaterial, findRawMaterialListPage} from "@/api/qualityManagement/rawMaterial.js";
import {
  deleteRawMaterial,
  findRawMaterialListPage,
  submitRawMaterial, updateCheckUserName,downloadRawMaterial
} from "@/api/qualityManagement/rawMaterial.js";
const data = reactive({
  searchForm: {
    supplier: "",
    batchNo: "",
    checkType: undefined,
    inspectState: undefined,
    entryDate: undefined, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  rules: {
    checkName: [{required: true, message: "请选择", trigger: "change"}],
    checkUserName: [{required: true, message: "请选择", trigger: "change"}],
  },
});
const {searchForm, rules} = toRefs(data);
@@ -99,7 +122,7 @@
  },
  {
    label: "检验员",
    prop: "checkName",
    prop: "checkUserName",
  },
  {
    label: "产品名称",
@@ -110,12 +133,8 @@
    prop: "model",
  },
  {
    label: "单位",
    prop: "unit",
  },
  {
    label: "数量",
    prop: "quantity",
    label: "批次号",
    prop: "batchNo",
    width: 120
  },
  {
@@ -128,12 +147,19 @@
    prop: "checkResult",
    dataType: "tag",
    formatType: (params) => {
      if (params === 1) {
      if (params === 0) {
        return "danger";
      } else if (params === 0) {
      } else if (params === 1) {
        return "success";
      } else {
        return null;
      }
    },
    formatData: (params) => {
      if (params === 0) {
        return "不合格";
      } else if (params === 1) {
        return "合格";
      }
    },
  },
@@ -161,15 +187,15 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
                disabled: (row) => {
                    // å·²æäº¤åˆ™ç¦ç”¨
                    if (row.inspectState == 1) return true;
                    // å¦‚果检验员有值,只有当前登录用户能编辑
                    if (row.checkName) {
                        return row.checkName !== userStore.nickName;
                    }
                    return false;
                }
        disabled: (row) => {
          // å·²æäº¤åˆ™ç¦ç”¨
          if (row.inspectState == 1) return true;
          // å¦‚果检验员有值,只有当前登录用户能编辑
          if (row.checkUserName) {
            return row.checkUserName !== userStore.nickName;
          }
          return false;
        }
      },
      {
        name: "附件",
@@ -184,29 +210,25 @@
        clickFun: (row) => {
          submit(row.id);
        },
                disabled: (row) => {
                    // å·²æäº¤åˆ™ç¦ç”¨
                    if (row.inspectState == 1) return true;
                    // å¦‚果检验员有值,只有当前登录用户能提交
                    if (row.checkName) {
                        return row.checkName !== userStore.nickName;
                    }
                    return false;
                }
        disabled: (row) => {
          // å·²æäº¤åˆ™ç¦ç”¨
          if (row.inspectState == 1) return true;
          return false;
        }
      },
      {
        name: "分配检验员",
        type: "text",
        clickFun: (row) => {
          if (!row.checkName) {
          if (!row.checkUserName) {
            open(row)
          } else {
            proxy.$modal.msgError("检验员已存在");
          }
        },
                disabled: (row) => {
                    return row.inspectState == 1 || row.checkName;
                }
        disabled: (row) => {
          return row.inspectState === 1 || row.checkUserName || row.checkUserName !== '';
        }
      },
      {
        name: "下载",
@@ -224,7 +246,7 @@
const userList = ref([]);
const dialogFormVisible = ref(false);
const form = ref({
  checkName: ""
  checkUserName: ""
});
const page = reactive({
  current: 1,
@@ -319,7 +341,7 @@
    type: "warning",
  })
      .then(() => {
        proxy.download("/quality/qualityInspect/export", {inspectType: 0}, "原材料检验.xlsx");
        proxy.download("/quality/rawMaterial/export", {}, "原料检.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
@@ -328,7 +350,7 @@
// æä»·
const submit = async (id) => {
  const res = await submitQualityInspect({id: id})
  const res = await submitRawMaterial(id)
  if (res.code === 200) {
    proxy.$modal.msgSuccess("提交成功");
    getList();
@@ -347,7 +369,7 @@
      ...form.value,
      id: currentRow.value.id
    }
    qualityInspectUpdate(data).then(res => {
    updateCheckUserName(data).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
@@ -363,7 +385,7 @@
}
const downLoadFile = (row) => {
  downloadQualityInspect({ id: row.id }).then((blobData) => {
  downloadRawMaterial({id: row.id}).then((blobData) => {
    const blob = new Blob([blobData], {
      type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    })
@@ -371,7 +393,7 @@
    const link = document.createElement('a')
    link.href = downloadUrl
    link.download = '原材料检验报告.docx'
    link.download = '原料检验报告.docx'
    document.body.appendChild(link)
    link.click()
@@ -385,4 +407,8 @@
});
</script>
<style scoped></style>
<style scoped>
.search_title {
  margin: 0 10px;
}
</style>