| | |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">采购合同号:</span> |
| | | <el-input |
| | | v-model="searchForm.purchaseContractNumber" |
| | | style="width: 240px" |
| | | placeholder="请输入" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >搜索</el-button |
| | | > |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="供应商名称:"> |
| | | <el-input v-model="searchForm.supplierName" placeholder="请输入" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="采购合同号:"> |
| | | <el-input |
| | | v-model="searchForm.purchaseContractNumber" |
| | | style="width: 240px" |
| | | placeholder="请输入" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="销售合同号:"> |
| | | <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="项目名称:"> |
| | | <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="录入日期:"> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="请选择" clearable @change="changeDaterange" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery"> 搜索 </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div> |
| | | |
| | | </div> |
| | | <div class="table_list"> |
| | | <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;"> |
| | | <el-button type="primary" @click="openForm('add')">新增台账</el-button> |
| | | <el-button type="success" @click="openScanAddDialog">扫码新增</el-button> |
| | | <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 |
| | |
| | | <el-table-column |
| | | label="采购合同号" |
| | | prop="purchaseContractNumber" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="销售合同号" |
| | | prop="salesContractNo" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="供应商名称" |
| | | width="240" |
| | | prop="supplierName" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="项目名称" |
| | | prop="projectName" |
| | | width="420" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="付款方式" |
| | | width="100" |
| | | prop="paymentMethod" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="合同金额(元)" |
| | | prop="contractAmount" |
| | | width="200" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | /> |
| | | <el-table-column |
| | | label="录入人" |
| | | prop="recorderName" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | label="录入日期" |
| | | prop="entryDate" |
| | | width="100" |
| | | show-overflow-tooltip |
| | | /> |
| | | <el-table-column |
| | | fixed="right" |
| | | label="操作" |
| | | min-width="60" |
| | | min-width="150" |
| | | align="center" |
| | | > |
| | | <template #default="scope"> |
| | |
| | | @click="openForm('edit', scope.row)" |
| | | >编辑</el-button |
| | | > |
| | | <el-button |
| | | link |
| | | type="success" |
| | | size="small" |
| | | @click="showQRCode(scope.row)" |
| | | >生成二维码</el-button |
| | | > |
| | | |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | v-model="form.supplierId" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="handleSupplierChange" |
| | | > |
| | | <el-option |
| | | v-for="item in supplierList" |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="付款方式"> |
| | | <el-input |
| | | v-model="form.paymentMethod" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="签订日期:" prop="executionDate"> |
| | | <el-date-picker |
| | | style="width: 100%" |
| | | v-model="form.executionDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="date" |
| | | placeholder="请选择" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="录入人:" prop="recorderId"> |
| | |
| | | <el-col :span="12"> |
| | | <el-form-item label="录入日期:" prop="entryDate"> |
| | | <el-date-picker |
| | | disabled |
| | | style="width: 100%" |
| | | v-model="form.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | |
| | | <el-table-column label="规格型号" prop="specificationModel" /> |
| | | <el-table-column label="单位" prop="unit" width="70" /> |
| | | <el-table-column label="数量" prop="quantity" width="70" /> |
| | | <el-table-column label="税率(%)" prop="taxRate" width="70" /> |
| | | <el-table-column label="库存预警数量" prop="warnNum" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="税率(%)" prop="taxRate" width="80" /> |
| | | <el-table-column |
| | | label="含税单价(元)" |
| | | prop="taxInclusiveUnitPrice" |
| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-input |
| | | v-model="productForm.quantity" |
| | | placeholder="请输入" |
| | | clearable |
| | | @change="mathNum" |
| | | /> |
| | | <el-col :span="12"> |
| | | <el-form-item label="税率(%):" prop="taxRate"> |
| | | <el-select |
| | | v-model="productForm.taxRate" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="mathNum" |
| | | > |
| | | <el-option label="1" value="1" /> |
| | | <el-option label="6" value="6" /> |
| | | <el-option label="13" value="13" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="24"> |
| | | <el-form-item label=" "> |
| | | <el-button type="warning" :disabled="!(productForm.productId && productForm.productModelId && productForm.taxRate)" @click="showPriceReference" icon="Search">查看历史采购价格参考</el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="税率(%):" prop="taxRate"> |
| | | <el-select |
| | | v-model="productForm.taxRate" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="mathNum" |
| | | > |
| | | <el-option label="1" value="1" /> |
| | | <el-option label="6" value="6" /> |
| | | <el-option label="13" value="13" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="数量:" prop="quantity"> |
| | | <el-input-number |
| | | :step="0.1" |
| | | clearable |
| | | :precision="2" |
| | | style="width: 100%" |
| | | v-model="productForm.quantity" |
| | | placeholder="请输入" |
| | | @change="mathNum" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | |
| | | :step="0.1" |
| | | clearable |
| | | style="width: 100%" |
| | | disabled |
| | | @change="reverseMathNum('taxInclusiveTotalPrice')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | label="不含税总价(元):" |
| | | prop="taxExclusiveTotalPrice" |
| | | > |
| | | <el-input v-model="productForm.taxExclusiveTotalPrice" disabled /> |
| | | <el-input |
| | | v-model="productForm.taxExclusiveTotalPrice" |
| | | @change="reverseMathNum('taxExclusiveTotalPrice')" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="库存预警数量:" prop="warnNum"> |
| | | <el-input-number |
| | | v-model="productForm.warnNum" |
| | | :precision="2" |
| | | :step="0.1" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 二维码显示对话框 --> |
| | | <el-dialog |
| | | v-model="qrCodeDialogVisible" |
| | | title="采购合同号二维码" |
| | | width="400px" |
| | | center |
| | | > |
| | | <div style="text-align: center;"> |
| | | <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" /> |
| | | <div style="margin: 20px;"> |
| | | <el-button type="primary" @click="downloadQRCode">下载二维码图片</el-button> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <!-- 扫码新增对话框 --> |
| | | <el-dialog |
| | | v-model="scanAddDialogVisible" |
| | | title="扫码新增采购台账" |
| | | width="70%" |
| | | @close="closeScanAddDialog" |
| | | > |
| | | <el-form |
| | | :model="scanAddForm" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="scanAddRules" |
| | | ref="scanAddFormRef" |
| | | > |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="扫码内容:"> |
| | | <el-input |
| | | v-model="scanAddForm.scanContent" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请扫描二维码或手动输入采购合同信息" |
| | | @input="parseScanContent" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="采购合同号:" prop="purchaseContractNumber"> |
| | | <el-input |
| | | v-model="scanAddForm.purchaseContractNumber" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="供应商名称:" prop="supplierName"> |
| | | <el-input |
| | | v-model="scanAddForm.supplierName" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项目名称:" prop="projectName"> |
| | | <el-input |
| | | v-model="scanAddForm.projectName" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="合同金额(元):" prop="contractAmount"> |
| | | <el-input-number |
| | | v-model="scanAddForm.contractAmount" |
| | | :precision="2" |
| | | :step="0.1" |
| | | clearable |
| | | style="width: 100%" |
| | | placeholder="请输入" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="付款方式:"> |
| | | <el-input |
| | | v-model="scanAddForm.paymentMethod" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="录入人:"> |
| | | <el-input v-model="scanAddForm.recorderName" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注:"> |
| | | <el-input |
| | | v-model="scanAddForm.remark" |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="请输入备注信息" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitScanAdd">确认新增</el-button> |
| | | <el-button @click="closeScanAddDialog">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 历史采购价格参考弹窗 --> |
| | | <el-dialog |
| | | v-model="priceReferenceVisible" |
| | | :title="`历史采购价格参考 ${productForm.supplierName}-${productForm.productName}-${productForm.productModel}`" |
| | | width="1000px" |
| | | append-to-body |
| | | > |
| | | <el-table |
| | | :data="priceReferenceData" |
| | | border |
| | | v-loading="priceReferenceLoading" |
| | | :default-sort="{ prop: 'updateTime', order: 'descending' }" |
| | | > |
| | | <!-- <el-table-column label="商品信息" min-width="200"> |
| | | <template #default="{ row }"> |
| | | <div class="product-info"> |
| | | <div class="product-name">{{ row.productName }}</div> |
| | | <div class="product-spec">{{ row.specification }}</div> |
| | | <div class="product-code">编码: {{ row.productCode }}</div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> --> |
| | | <!-- <el-table-column label="供应商" prop="supplierName" width="200" /> --> |
| | | <el-table-column label="基础价格(不含税)" width="150" align="right"> |
| | | <template #default="{ row }"> |
| | | <span class="price-text">¥{{ row.basePrice }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="实际价格(不含税)" prop="actuallyPrice" width="150" align="right"/> |
| | | <el-table-column label="折扣信息" width="120" align="center"> |
| | | <template #default="{ row }"> |
| | | <div v-if="row.discountType"> |
| | | <el-tag :type="getDiscountTagType(row.discountType)" size="small"> |
| | | {{ getDiscountText(row.discountType) }} |
| | | </el-tag> |
| | | <div class="discount-value">{{ row.discountValue }}{{ row.discountType === 'percentage' ? '%' : '元' }}</div> |
| | | </div> |
| | | <span v-else class="no-discount">无折扣</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="状态" width="100" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ getStatusText(row.status) }}</el-tag> |
| | | <!-- <div v-if="isPriceWarning(row)" class="warning-indicator"> |
| | | <el-icon color="#F56C6C"><Warning /></el-icon> |
| | | </div> --> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="生效时间" prop="effectiveTime" width="100" align="center" /> |
| | | <el-table-column label="更新时间" prop="updateTime" width="160" sortable align="center" /> |
| | | <el-table-column label="备注" width="200" prop="remark" align="center" show-overflow-tooltip /> |
| | | <el-table-column label="操作" width="100" align="center" fixed="right"> |
| | | <template #default="{ row }"> |
| | | <el-button type="success" link @click="selectPriceReference(row)"> |
| | | <el-icon><Check /></el-icon> |
| | | 引用 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <div class="pagination-container" style="margin-top: 20px; display: flex; justify-content: flex-end;"> |
| | | <el-pagination |
| | | v-model:current-page="priceReferencePagination.current" |
| | | v-model:page-size="priceReferencePagination.size" |
| | | :page-sizes="[10, 20, 50]" |
| | | :total="priceReferenceTotal" |
| | | layout="total, sizes, prev, pager, next" |
| | | @size-change="handlePriceReferenceSizeChange" |
| | | @current-change="handlePriceReferenceCurrentChange" |
| | | /> |
| | | </div> |
| | | <template #footer> |
| | | <el-button @click="priceReferenceVisible = false">关闭</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 扫码登记对话框 --> |
| | | <el-dialog |
| | | v-model="scanDialogVisible" |
| | | title="扫码登记" |
| | | width="60%" |
| | | @close="closeScanDialog" |
| | | > |
| | | <el-form |
| | | :model="scanForm" |
| | | label-width="120px" |
| | | label-position="left" |
| | | :rules="scanRules" |
| | | ref="scanFormRef" |
| | | > |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="采购合同号:"> |
| | | <el-input v-model="scanForm.purchaseContractNumber" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="供应商名称:"> |
| | | <el-input v-model="scanForm.supplierName" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="项目名称:"> |
| | | <el-input v-model="scanForm.projectName" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="扫码时间:"> |
| | | <el-input v-model="scanForm.scanTime" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="扫码人:"> |
| | | <el-input v-model="scanForm.scannerName" disabled /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="扫码状态:"> |
| | | <el-tag :type="scanForm.scanStatus === '已扫码' ? 'success' : 'warning'"> |
| | | {{ scanForm.scanStatus }} |
| | | </el-tag> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="扫码备注:"> |
| | | <el-input |
| | | v-model="scanForm.scanRemark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请输入扫码备注信息" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="扫码记录:"> |
| | | <el-table :data="scanRecords" border style="width: 100%"> |
| | | <el-table-column label="序号" type="index" width="60" align="center" /> |
| | | <el-table-column label="扫码时间" prop="scanTime" width="180" /> |
| | | <el-table-column label="扫码人" prop="scannerName" width="120" /> |
| | | <el-table-column label="扫码状态" prop="scanStatus" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.scanStatus === '已扫码' ? 'success' : 'warning'"> |
| | | {{ scope.row.scanStatus }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="备注" prop="scanRemark" /> |
| | | </el-table> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitScan">确认扫码</el-button> |
| | | <el-button @click="closeScanDialog">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { getToken } from "@/utils/auth"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import { ref, onMounted } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue"; |
| | | import { Search, Check } from "@element-plus/icons-vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { |
| | |
| | | getOptions, |
| | | createPurchaseNo, |
| | | } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | import QRCode from "qrcode"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableData = ref([]); |
| | | const productData = ref([]); |
| | |
| | | const fileList = ref([]); |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { listPage as listAdvancedPrice } from "@/api/procurementManagement/advancedPriceManagement.js"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const userStore = useUserStore(); |
| | | |
| | | // 二维码相关变量 |
| | | const qrCodeDialogVisible = ref(false); |
| | | const qrCodeUrl = ref(""); |
| | | |
| | | // 用户信息表单弹框数据 |
| | | const operationType = ref(""); |
| | | const dialogFormVisible = ref(false); |
| | | const data = reactive({ |
| | | searchForm: { |
| | | purchaseContractNumber: "", |
| | | supplierName: "", // 供应商名称 |
| | | purchaseContractNumber: "", // 采购合同编号 |
| | | salesContractNo: "", // 销售合同编号 |
| | | projectName: "", // 项目名称 |
| | | entryDate: null, // 录入日期 |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | | }, |
| | | form: { |
| | | purchaseContractNumber: "", |
| | |
| | | productData: [], |
| | | supplierName: "", |
| | | supplierId: "", |
| | | paymentMethod: "", |
| | | executionDate: "", |
| | | }, |
| | | rules: { |
| | | purchaseContractNumber: [ |
| | |
| | | ], |
| | | projectName: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | supplierId: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | entryDate: [{ required: true, message: "请选择", trigger: "change" }], |
| | | executionDate: [{ required: true, message: "请选择", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { searchForm, form, rules } = toRefs(data); |
| | | const { form, rules } = toRefs(data); |
| | | const { form: searchForm } = useFormData(data.searchForm); |
| | | |
| | | // 产品表单弹框数据 |
| | | const productFormVisible = ref(false); |
| | | const productOperationType = ref(""); |
| | |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | warnNum: "", |
| | | supplierName: "", |
| | | productName: "", |
| | | productModel: "", |
| | | }, |
| | | productRules: { |
| | | productId: [{ required: true, message: "请选择", trigger: "change" }], |
| | |
| | | { required: true, message: "请输入", trigger: "blur" }, |
| | | ], |
| | | taxRate: [{ required: true, message: "请选择", trigger: "change" }], |
| | | warnNum: [{ required: false, message: "请选择", trigger: "change" }], |
| | | taxInclusiveTotalPrice: [ |
| | | { required: true, message: "请输入", trigger: "blur" }, |
| | | ], |
| | |
| | | }, |
| | | }); |
| | | const { productForm, productRules } = toRefs(productFormData); |
| | | |
| | | // 采购价格管理参考弹窗 |
| | | const priceReferenceVisible = ref(false); |
| | | const priceReferenceLoading = ref(false); |
| | | const priceReferenceData = ref([]); |
| | | const priceReferenceTotal = ref(0); |
| | | const priceReferencePagination = reactive({ |
| | | current: 1, |
| | | size: 10 |
| | | }); |
| | | |
| | | const calculateFinalPrice = (row) => { |
| | | let finalPrice = row.basePrice; |
| | | if (row.discountType === "percentage") { |
| | | finalPrice = row.basePrice * (1 - row.discountValue / 100); |
| | | } else if (row.discountType === "fixed") { |
| | | finalPrice = row.basePrice - row.discountValue; |
| | | } |
| | | let man = Math.max(finalPrice, 0); |
| | | return man.toFixed(2); |
| | | }; |
| | | |
| | | const getDiscountTagType = (discountType) => { |
| | | const typeMap = { |
| | | percentage: "success", |
| | | fixed: "warning", |
| | | tiered: "info", |
| | | }; |
| | | return typeMap[discountType] || "info"; |
| | | }; |
| | | |
| | | const getDiscountText = (discountType) => { |
| | | const textMap = { |
| | | percentage: "百分比", |
| | | fixed: "固定金额", |
| | | }; |
| | | return textMap[discountType] || "未知"; |
| | | }; |
| | | |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | active: "success", |
| | | pending: "warning", |
| | | expired: "info", |
| | | }; |
| | | return statusMap[status] || "info"; |
| | | }; |
| | | |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | active: "有效", |
| | | pending: "待生效", |
| | | expired: "已过期", |
| | | }; |
| | | return statusMap[status] || "未知"; |
| | | }; |
| | | |
| | | const showPriceReference = () => { |
| | | if (form.value.supplierId) { |
| | | const supplier = supplierList.value.find((item) => item.id === form.value.supplierId); |
| | | if (supplier) { |
| | | productForm.value.supplierName = supplier.supplierName; |
| | | } |
| | | } |
| | | priceReferenceVisible.value = true; |
| | | handlePriceReferenceSearch(); |
| | | }; |
| | | |
| | | const handlePriceReferenceSearch = () => { |
| | | priceReferenceLoading.value = true; |
| | | // 模拟搜索参数:productId 映射为 productName 或 ID,这里根据 advancedPriceManagement 的 API 确定参数 |
| | | // 假设高级价格管理的 listPage 接收 productId |
| | | const query = { |
| | | productId: productForm.value.productId, |
| | | supplierId: form.value.supplierId, |
| | | current: priceReferencePagination.current, |
| | | size: priceReferencePagination.size |
| | | }; |
| | | listAdvancedPrice(query).then(res => { |
| | | console.log(res); |
| | | priceReferenceData.value = res.data.records; |
| | | priceReferenceTotal.value = res.data.total; |
| | | priceReferenceLoading.value = false; |
| | | }).catch(() => { |
| | | priceReferenceLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handlePriceReferenceSizeChange = (size) => { |
| | | priceReferencePagination.size = size; |
| | | handlePriceReferenceSearch(); |
| | | }; |
| | | |
| | | const handlePriceReferenceCurrentChange = (page) => { |
| | | priceReferencePagination.current = page; |
| | | handlePriceReferenceSearch(); |
| | | }; |
| | | |
| | | const selectPriceReference = (row) => { |
| | | // 计算历史价格(作为不含税单价):基础价格 - 折扣 |
| | | let taxExclusivePrice = row.basePrice; |
| | | if (row.discountType === "percentage") { |
| | | taxExclusivePrice = row.basePrice * (1 - row.discountValue / 100); |
| | | } else if (row.discountType === "fixed") { |
| | | taxExclusivePrice = row.basePrice - row.discountValue; |
| | | } |
| | | taxExclusivePrice = Number(Math.max(taxExclusivePrice, 0)); |
| | | |
| | | // 设置数量为 1 |
| | | if (!productForm.value.quantity) { |
| | | productForm.value.quantity = 1; |
| | | } |
| | | |
| | | // 如果已有税率,则计算含税单价 |
| | | if (productForm.value.taxRate) { |
| | | const taxRate = Number(productForm.value.taxRate); |
| | | productForm.value.taxInclusiveUnitPrice = ( |
| | | taxExclusivePrice * |
| | | (1 + taxRate / 100) |
| | | ).toFixed(2); |
| | | } else { |
| | | // 如果没有税率,暂时将含税单价设为不含税价格,提示用户选择税率 |
| | | productForm.value.taxInclusiveUnitPrice = taxExclusivePrice.toFixed(2); |
| | | proxy.$modal.msgWarning("请选择税率以准确计算含税价格"); |
| | | } |
| | | |
| | | // 自动触发计算逻辑(计算总价等) |
| | | mathNum(); |
| | | |
| | | priceReferenceVisible.value = false; |
| | | proxy.$modal.msgSuccess("已引用历史价格(不含税)"); |
| | | }; |
| | | |
| | | const upload = reactive({ |
| | | // 上传的地址 |
| | | url: import.meta.env.VITE_APP_BASE_API + "/file/upload", |
| | | // 设置上传的请求头部 |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | }); |
| | | |
| | | const changeDaterange = (value) => { |
| | | if (value) { |
| | | searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD"); |
| | | searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD"); |
| | | } else { |
| | | searchForm.entryDateStart = undefined; |
| | | searchForm.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | purchaseListPage({ ...searchForm.value, ...page }) |
| | | const { entryDate, ...rest } = searchForm; |
| | | purchaseListPage({ ...rest, ...page }) |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | |
| | | productList({ salesLedgerId: row.id, type: 2 }).then((res) => { |
| | | const index = tableData.value.findIndex((item) => item.id === row.id); |
| | | if (index > -1) { |
| | | tableData.value[index].children = res; |
| | | tableData.value[index].children = res.data; |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | |
| | | // 移除文件 |
| | | function handleRemove(file) { |
| | | console.log("handleRemove", file.id); |
| | | if (file.size > 1024 * 1024 * 10) { |
| | | // 仅前端清理,不调用删除接口和提示 |
| | | return; |
| | | } |
| | | if (operationType.value === "edit") { |
| | | let ids = []; |
| | | ids.push(file.id); |
| | |
| | | }; |
| | | // 打开产品弹框 |
| | | const openProductForm = (type, row, index) => { |
| | | if(!form.value.supplierId){ |
| | | proxy.$modal.msgWarning("请选择供应商"); |
| | | return; |
| | | } |
| | | productOperationType.value = type; |
| | | productOperationIndex.value = index; |
| | | productForm.value = {}; |
| | | productForm.value = { |
| | | supplierName: "", |
| | | productName: "", |
| | | productModel: "", |
| | | }; |
| | | proxy.resetForm("productFormRef"); |
| | | if (type === "edit") { |
| | | productForm.value = { ...row }; |
| | | productForm.value = { |
| | | ...row, |
| | | productName: row.productCategory || "", |
| | | productModel: row.specificationModel || "" |
| | | }; |
| | | // 编辑时,根据 productId 预加载规格型号列表,确保下拉框显示名称而非 ID |
| | | if (productForm.value.productId) { |
| | | modelList({ id: productForm.value.productId }).then((res) => { |
| | | modelOptions.value = res; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // 确保供应商名称始终同步 |
| | | const supplier = supplierList.value.find((item) => item.id === form.value.supplierId); |
| | | if (supplier) { |
| | | productForm.value.supplierName = supplier.supplierName; |
| | | } |
| | | |
| | | productFormVisible.value = true; |
| | | getProductOptions(); |
| | | }; |
| | |
| | | }); |
| | | }; |
| | | const getModels = (value) => { |
| | | productForm.value.productCategory = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }); |
| | | if (value) { |
| | | productForm.value.productCategory = findNodeById(productOptions.value, value) || ""; |
| | | productForm.value.productName = productForm.value.productCategory; |
| | | productForm.value.productModelId = ""; |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }); |
| | | } else { |
| | | productForm.value.productCategory = ""; |
| | | productForm.value.productName = ""; |
| | | modelOptions.value = []; |
| | | } |
| | | }; |
| | | const getProductModel = (value) => { |
| | | const index = modelOptions.value.findIndex((item) => item.id === value); |
| | | if (index !== -1) { |
| | | productForm.value.specificationModel = modelOptions.value[index].model; |
| | | productForm.value.productModel = modelOptions.value[index].model; |
| | | productForm.value.unit = modelOptions.value[index].unit; |
| | | } else { |
| | | productForm.value.specificationModel = null; |
| | | productForm.value.productModel = null; |
| | | productForm.value.unit = null; |
| | | } |
| | | }; |
| | | const handleSupplierChange = (value) => { |
| | | const supplier = supplierList.value.find((item) => item.id === value); |
| | | if (supplier) { |
| | | form.value.supplierName = supplier.supplierName; |
| | | productForm.value.supplierName = supplier.supplierName; |
| | | } else { |
| | | form.value.supplierName = ""; |
| | | productForm.value.supplierName = ""; |
| | | } |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | if (nodes[i].value === productId) { |
| | | return nodes[i].label; // 找到节点,返回该节点 |
| | | return nodes[i].label; // 找到节点,返回该节点的label |
| | | } |
| | | if (nodes[i].children && nodes[i].children.length > 0) { |
| | | const foundNode = findNodeById(nodes[i].children, productId); |
| | | if (foundNode) { |
| | | return foundNode.label; // 在子节点中找到,返回该节点 |
| | | return foundNode; // 在子节点中找到,直接返回(已经是label字符串) |
| | | } |
| | | } |
| | | } |
| | |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | // 检查是否有他人维护的数据 |
| | | const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName); |
| | | if (unauthorizedData.length > 0) { |
| | | proxy.$modal.msgWarning("不可删除他人维护的数据"); |
| | | return; |
| | | } |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("请选择数据"); |
| | |
| | | return `${year}-${month}-${day}`; |
| | | } |
| | | const mathNum = () => { |
| | | console.log("productForm.value", productForm.value); |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请先选择税率"); |
| | | return; |
| | | } |
| | | if (!productForm.value.taxInclusiveUnitPrice) { |
| | | return; |
| | | } |
| | |
| | | ); |
| | | } |
| | | }; |
| | | const reverseMathNum = (field) => { |
| | | if (!productForm.value.taxRate) { |
| | | proxy.$modal.msgWarning("请先选择税率"); |
| | | return; |
| | | } |
| | | const taxRate = Number(productForm.value.taxRate); |
| | | if (!taxRate) return; |
| | | if (field === 'taxInclusiveTotalPrice') { |
| | | // 已知含税总价和数量,反算含税单价 |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2); |
| | | } |
| | | // 已知含税总价和含税单价,反算数量 |
| | | else if (productForm.value.taxInclusiveUnitPrice) { |
| | | productForm.value.quantity = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2); |
| | | } |
| | | // 反算不含税总价 |
| | | productForm.value.taxExclusiveTotalPrice = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2); |
| | | } else if (field === 'taxExclusiveTotalPrice') { |
| | | // 反算含税总价 |
| | | productForm.value.taxInclusiveTotalPrice = |
| | | (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2); |
| | | // 已知数量,反算含税单价 |
| | | if (productForm.value.quantity) { |
| | | productForm.value.taxInclusiveUnitPrice = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2); |
| | | } |
| | | // 已知含税单价,反算数量 |
| | | else if (productForm.value.taxInclusiveUnitPrice) { |
| | | productForm.value.quantity = |
| | | (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2); |
| | | } |
| | | } |
| | | }; |
| | | // 销售合同选择改变方法 |
| | | const salesLedgerChange = async (row) => { |
| | | console.log("row", row); |
| | |
| | | } |
| | | }; |
| | | |
| | | // 显示二维码 |
| | | const showQRCode = async (row) => { |
| | | try { |
| | | // 构建二维码内容,只包含采购合同号(纯文本) |
| | | const qrContent = row.purchaseContractNumber || ''; |
| | | // 检查内容是否为空 |
| | | if (!qrContent || qrContent.trim() === '') { |
| | | proxy.$modal.msgWarning("该行没有采购合同号,无法生成二维码"); |
| | | return; |
| | | } |
| | | qrCodeUrl.value = await QRCode.toDataURL(qrContent, { |
| | | width: 200, |
| | | margin: 2, |
| | | color: { |
| | | dark: '#000000', |
| | | light: '#FFFFFF' |
| | | } |
| | | }); |
| | | qrCodeDialogVisible.value = true; |
| | | } catch (error) { |
| | | console.error('生成二维码失败:', error); |
| | | proxy.$modal.msgError("生成二维码失败:" + error.message); |
| | | } |
| | | }; |
| | | |
| | | // 下载二维码 |
| | | const downloadQRCode = () => { |
| | | if (!qrCodeUrl.value) { |
| | | proxy.$modal.msgWarning("二维码未生成"); |
| | | return; |
| | | } |
| | | |
| | | const a = document.createElement('a'); |
| | | a.href = qrCodeUrl.value; |
| | | a.download = `采购合同号二维码_${new Date().getTime()}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | proxy.$modal.msgSuccess("下载成功"); |
| | | }; |
| | | |
| | | // 扫码新增对话框相关变量 |
| | | const scanAddDialogVisible = ref(false); |
| | | const scanAddForm = reactive({ |
| | | scanContent: "", |
| | | purchaseContractNumber: "", |
| | | supplierName: "", |
| | | projectName: "", |
| | | contractAmount: "", |
| | | paymentMethod: "", |
| | | recorderName: "", |
| | | scanRemark: "", |
| | | }); |
| | | const scanAddRules = { |
| | | purchaseContractNumber: [{ required: true, message: "请输入采购合同号", trigger: "blur" }], |
| | | supplierName: [{ required: true, message: "请输入供应商名称", trigger: "blur" }], |
| | | projectName: [{ required: true, message: "请输入项目名称", trigger: "blur" }], |
| | | }; |
| | | |
| | | // 扫码登记对话框相关变量 |
| | | const scanDialogVisible = ref(false); |
| | | const scanForm = reactive({ |
| | | purchaseContractNumber: "", |
| | | supplierName: "", |
| | | projectName: "", |
| | | scanTime: "", |
| | | scannerName: "", |
| | | scanStatus: "未扫码", |
| | | scanRemark: "", |
| | | }); |
| | | const scanRules = { |
| | | scanRemark: [{ required: true, message: "请输入扫码备注", trigger: "blur" }], |
| | | }; |
| | | const scanRecords = ref([]); |
| | | |
| | | // 打开扫码新增对话框 |
| | | const openScanAddDialog = () => { |
| | | scanAddForm.scanContent = ""; |
| | | scanAddForm.purchaseContractNumber = ""; |
| | | scanAddForm.supplierName = ""; |
| | | scanAddForm.projectName = ""; |
| | | scanAddForm.contractAmount = ""; |
| | | scanAddForm.paymentMethod = ""; |
| | | scanAddForm.recorderName = userStore.nickName; |
| | | scanAddForm.scanRemark = ""; |
| | | scanAddDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 解析扫码内容(模拟解析二维码数据) |
| | | const parseScanContent = (content) => { |
| | | if (!content) return; |
| | | |
| | | // 模拟解析二维码内容,这里可以根据实际需求调整解析逻辑 |
| | | // 假设扫码内容格式为:合同号|供应商|项目|金额|付款方式 |
| | | const parts = content.split('|'); |
| | | if (parts.length >= 3) { |
| | | scanAddForm.purchaseContractNumber = parts[0] || ""; |
| | | scanAddForm.supplierName = parts[1] || ""; |
| | | scanAddForm.projectName = parts[2] || ""; |
| | | scanAddForm.contractAmount = parts[3] || ""; |
| | | scanAddForm.paymentMethod = parts[4] || ""; |
| | | } |
| | | }; |
| | | |
| | | // 关闭扫码新增对话框 |
| | | const closeScanAddDialog = () => { |
| | | scanAddDialogVisible.value = false; |
| | | proxy.resetForm("scanAddFormRef"); |
| | | }; |
| | | |
| | | // 提交扫码新增 |
| | | const submitScanAdd = () => { |
| | | proxy.$refs["scanAddFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | // 构建新增数据 |
| | | const newData = { |
| | | purchaseContractNumber: scanAddForm.purchaseContractNumber, |
| | | supplierName: scanAddForm.supplierName, |
| | | projectName: scanAddForm.projectName, |
| | | contractAmount: scanAddForm.contractAmount, |
| | | paymentMethod: scanAddForm.paymentMethod, |
| | | recorderName: scanAddForm.recorderName, |
| | | entryDate: getCurrentDate(), |
| | | remark: scanAddForm.scanRemark, |
| | | type: 2 |
| | | }; |
| | | |
| | | // 模拟新增成功 |
| | | proxy.$modal.msgSuccess("扫码新增成功!"); |
| | | closeScanAddDialog(); |
| | | |
| | | // 可以选择是否刷新列表 |
| | | // getList(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 打开扫码登记对话框 |
| | | const openScanDialog = (row) => { |
| | | scanForm.purchaseContractNumber = row.purchaseContractNumber; |
| | | scanForm.supplierName = row.supplierName; |
| | | scanForm.projectName = row.projectName; |
| | | scanForm.scanTime = getCurrentDateTime(); |
| | | scanForm.scannerName = userStore.nickName; |
| | | scanForm.scanStatus = "未扫码"; |
| | | scanForm.scanRemark = ""; |
| | | scanRecords.value = []; |
| | | scanDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 关闭扫码登记对话框 |
| | | const closeScanDialog = () => { |
| | | scanDialogVisible.value = false; |
| | | proxy.resetForm("scanFormRef"); |
| | | }; |
| | | |
| | | // 提交扫码登记 |
| | | const submitScan = () => { |
| | | proxy.$refs["scanFormRef"].validate((valid) => { |
| | | if (valid) { |
| | | // 添加扫码记录 |
| | | scanRecords.value.push({ |
| | | ...scanForm, |
| | | id: Date.now(), // 模拟ID |
| | | scanTime: getCurrentDateTime(), |
| | | }); |
| | | scanForm.scanStatus = "已扫码"; |
| | | scanForm.scanRemark = scanForm.scanRemark || "无"; |
| | | proxy.$modal.msgSuccess("扫码登记成功!"); |
| | | closeScanDialog(); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 获取当前日期时间 |
| | | function getCurrentDateTime() { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); |
| | | const month = String(now.getMonth() + 1).padStart(2, "0"); |
| | | const day = String(now.getDate()).padStart(2, "0"); |
| | | const hours = String(now.getHours()).padStart(2, "0"); |
| | | const minutes = String(now.getMinutes()).padStart(2, "0"); |
| | | const seconds = String(now.getSeconds()).padStart(2, "0"); |
| | | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; |
| | | } |
| | | |
| | | |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |