764 lines
23 KiB
Vue
764 lines
23 KiB
Vue
<template>
|
||
<div class="page-container">
|
||
<!-- 搜索区域 -->
|
||
<el-card class="search-card" shadow="never">
|
||
<el-form :model="queryParams" :inline="true" @submit.prevent="handleQuery">
|
||
<!-- 明细视图搜索 -->
|
||
<template v-if="viewMode === 'detail'">
|
||
<el-form-item label="跟单单号">
|
||
<el-input v-model="queryParams.salesOrderCode" placeholder="请输入" clearable style="width: 140px" />
|
||
</el-form-item>
|
||
<el-form-item label="单据编码">
|
||
<el-input v-model="queryParams.purchaseCode" placeholder="请输入" clearable style="width: 140px" />
|
||
</el-form-item>
|
||
<el-form-item label="物料编码">
|
||
<el-input v-model="queryParams.itemCode" placeholder="请输入" clearable style="width: 140px" />
|
||
</el-form-item>
|
||
<el-form-item label="物料名称">
|
||
<el-input v-model="queryParams.itemName" placeholder="请输入" clearable style="width: 140px" />
|
||
</el-form-item>
|
||
</template>
|
||
<!-- 单据视图搜索 -->
|
||
<template v-else>
|
||
<el-form-item label="单据编码">
|
||
<el-input v-model="queryParams.purchaseCode" placeholder="请输入" clearable style="width: 160px" />
|
||
</el-form-item>
|
||
<el-form-item label="单据状态">
|
||
<el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 120px">
|
||
<el-option label="开立" value="PREPARE" />
|
||
<el-option label="审核" value="APPROVED" />
|
||
<el-option label="退回" value="REJECTED" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="业务状态">
|
||
<el-select v-model="queryParams.businessStatus" placeholder="全部" clearable style="width: 120px">
|
||
<el-option label="正常" value="NORMAL" />
|
||
<el-option label="暂停" value="PAUSE" />
|
||
<el-option label="取消" value="CANCEL" />
|
||
<el-option label="完成" value="COMPLETED" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</template>
|
||
<!-- 公共搜索:日期范围 -->
|
||
<el-form-item label="开始日期">
|
||
<el-date-picker
|
||
v-model="queryParams.beginDate"
|
||
type="date"
|
||
placeholder="开始日期"
|
||
value-format="YYYY-MM-DD"
|
||
style="width: 140px"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="结束日期">
|
||
<el-date-picker
|
||
v-model="queryParams.endDate"
|
||
type="date"
|
||
placeholder="结束日期"
|
||
value-format="YYYY-MM-DD"
|
||
style="width: 140px"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="handleQuery">
|
||
<el-icon><Search /></el-icon>搜索
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
|
||
<!-- 工具栏与表格 -->
|
||
<el-card class="table-card" shadow="never">
|
||
<!-- 工具栏 -->
|
||
<div class="toolbar">
|
||
<div class="toolbar-left">
|
||
<!-- 视图切换按钮 -->
|
||
<el-button
|
||
v-if="viewMode === 'detail'"
|
||
type="primary"
|
||
@click="switchView('document')"
|
||
>单据</el-button>
|
||
<el-button
|
||
v-else
|
||
type="primary"
|
||
@click="switchView('detail')"
|
||
>明细</el-button>
|
||
|
||
<el-button @click="handleQueryAll">
|
||
<el-icon><Tickets /></el-icon>查询所有
|
||
</el-button>
|
||
<el-button type="success" @click="handleAdd">
|
||
<el-icon><Plus /></el-icon>新增
|
||
</el-button>
|
||
|
||
<!-- 明细视图特有按钮 -->
|
||
<el-button v-if="viewMode === 'detail'" @click="handleExport">
|
||
<el-icon><Download /></el-icon>导出
|
||
</el-button>
|
||
|
||
<!-- 单据视图特有按钮 -->
|
||
<template v-if="viewMode === 'document'">
|
||
<el-button
|
||
type="danger"
|
||
:disabled="selectedRows.length === 0"
|
||
@click="handleBatchDelete"
|
||
>
|
||
<el-icon><Delete /></el-icon>删除
|
||
</el-button>
|
||
<el-button
|
||
type="primary"
|
||
:disabled="!canBatchApprove"
|
||
@click="handleBatchApprove"
|
||
>
|
||
<el-icon><Check /></el-icon>审核
|
||
</el-button>
|
||
<el-button
|
||
:disabled="!canBatchUnapprove"
|
||
@click="handleBatchUnapprove"
|
||
>反审核
|
||
</el-button>
|
||
</template>
|
||
</div>
|
||
|
||
<!-- 明细视图 - 物料分类快捷筛选标签 -->
|
||
<div v-if="viewMode === 'detail'" class="toolbar-right">
|
||
<div class="category-tags">
|
||
<el-check-tag
|
||
v-for="tag in categoryTags"
|
||
:key="tag.itemTypeId"
|
||
:checked="queryParams.itemTypeId === tag.itemTypeId"
|
||
@change="handleCategoryTag(tag.itemTypeId)"
|
||
>{{ tag.itemTypeName }}</el-check-tag>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ 明细视图表格 ============ -->
|
||
<el-table
|
||
v-if="viewMode === 'detail'"
|
||
v-loading="loading"
|
||
:data="tableData"
|
||
stripe
|
||
border
|
||
style="width: 100%"
|
||
:max-height="tableHeight"
|
||
>
|
||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||
<el-table-column prop="salesOrderCode" label="跟单编号" width="140">
|
||
<template #default="{ row }">
|
||
<el-link v-if="row.salesOrderCode" type="primary">{{ row.salesOrderCode }}</el-link>
|
||
<span v-else>-</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="deliveryDate" label="订单交期" width="110" align="center" />
|
||
<el-table-column prop="purchaseCode" label="单据编码" width="140">
|
||
<template #default="{ row }">
|
||
<el-link type="primary" @click="handleViewDetail(row)">{{ row.purchaseCode }}</el-link>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="status" label="单据状态" width="90" align="center">
|
||
<template #default="{ row }">
|
||
<el-tag :type="getStatusType(row.status)" size="small">{{ getStatusLabel(row.status) }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="itemName" label="物料名称" min-width="200" show-overflow-tooltip />
|
||
<el-table-column prop="itemCode" label="物料编码" width="120" />
|
||
<el-table-column prop="purchaseQty" label="采购数量" width="100" align="right">
|
||
<template #default="{ row }">{{ formatNumber(row.purchaseQty) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="orderedQty" label="已订数量" width="100" align="right">
|
||
<template #default="{ row }">{{ formatNumber(row.orderedQty ?? 0) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="purchaseDate" label="单据日期" width="110" align="center" />
|
||
<el-table-column prop="remark" label="采购说明" width="120" show-overflow-tooltip />
|
||
</el-table>
|
||
|
||
<!-- ============ 单据视图表格 ============ -->
|
||
<el-table
|
||
v-else
|
||
v-loading="loading"
|
||
:data="tableData"
|
||
stripe
|
||
border
|
||
style="width: 100%"
|
||
:max-height="tableHeight"
|
||
@selection-change="handleSelectionChange"
|
||
>
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column prop="purchaseCode" label="单据编码" width="140">
|
||
<template #default="{ row }">
|
||
<el-link type="primary" @click="handleViewDetail(row)">{{ row.purchaseCode }}</el-link>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="purchaseDate" label="单据日期" width="120" align="center" sortable />
|
||
<el-table-column prop="status" label="单据状态" width="90" align="center">
|
||
<template #default="{ row }">
|
||
<el-tag :type="getStatusType(row.status)" size="small">{{ getStatusLabel(row.status) }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="businessType" label="业务类型" width="100" align="center">
|
||
<template #default="{ row }">{{ getBusinessTypeLabel(row.businessType) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="deptName" label="采购部门" width="100" align="center" />
|
||
<el-table-column prop="operatorName" label="采购人员" width="100" align="center" />
|
||
<el-table-column prop="businessStatus" label="业务状态" width="90" align="center">
|
||
<template #default="{ row }">{{ getBusinessStatusLabel(row.businessStatus) }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="approveDate" label="审核日期" width="120" align="center" />
|
||
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||
<template #default="{ row }">
|
||
<el-button type="success" link size="small" @click="handleViewDetail(row)">查看</el-button>
|
||
<el-button
|
||
v-if="isEditable(row.status)"
|
||
type="primary"
|
||
link
|
||
size="small"
|
||
@click="handleEdit(row)"
|
||
>编辑</el-button>
|
||
<el-button
|
||
v-if="isApproved(row.status)"
|
||
type="primary"
|
||
link
|
||
size="small"
|
||
@click="handleCopyPlan(row)"
|
||
>翻单</el-button>
|
||
<el-popconfirm
|
||
v-if="isEditable(row.status)"
|
||
title="确认删除该采购计划吗?"
|
||
@confirm="handleDeleteRow(row)"
|
||
>
|
||
<template #reference>
|
||
<el-button type="danger" link size="small">删除</el-button>
|
||
</template>
|
||
</el-popconfirm>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 底部汇总和分页 -->
|
||
<div class="table-footer">
|
||
<div class="summary">
|
||
采购数量:<span class="summary-value primary">{{ formatNumber(totalPurchaseQty) }}</span>
|
||
已订数量:<span class="summary-value primary">{{ formatNumber(totalOrderedQty) }}</span>
|
||
</div>
|
||
<el-pagination
|
||
v-model:current-page="queryParams.pageNum"
|
||
v-model:page-size="queryParams.pageSize"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
:total="total"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
/>
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 导出确认弹窗 -->
|
||
<el-dialog v-model="showExportDialog" title="导出确认" width="420px">
|
||
<p>确认导出当前查询条件下的采购计划数据吗?</p>
|
||
<template #footer>
|
||
<el-button @click="showExportDialog = false">取消</el-button>
|
||
<el-button type="primary" :loading="exportLoading" @click="confirmExport">确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { Search, Plus, Delete, Download, Check, Tickets } from '@element-plus/icons-vue'
|
||
import {
|
||
getPurchasePlanList,
|
||
getPurchasePlanDocList,
|
||
getPurchasePlanSummary,
|
||
deletePurchasePlan,
|
||
approvePurchasePlan,
|
||
unapprovePurchasePlan,
|
||
exportPurchasePlan,
|
||
getItemTypeList,
|
||
STATUS_MAP,
|
||
BUSINESS_STATUS_MAP,
|
||
BUSINESS_TYPE_OPTIONS,
|
||
type PurchasePlan,
|
||
type PurchasePlanQuery,
|
||
type PurchasePlanSummary
|
||
} from '@/api/purchasePlan'
|
||
|
||
const router = useRouter()
|
||
|
||
// ============ 响应式状态 ============
|
||
|
||
/** 当前视图模式:明细视图 / 单据视图 */
|
||
const viewMode = ref<'detail' | 'document'>('detail')
|
||
const loading = ref(false)
|
||
|
||
/** 本地搜索参数(用户交互用,loadData 时转换为 RuoYi 格式) */
|
||
const queryParams = reactive({
|
||
salesOrderCode: '',
|
||
purchaseCode: '',
|
||
itemCode: '',
|
||
itemName: '',
|
||
itemTypeId: '' as number | string,
|
||
needType: 0,
|
||
beginDate: '',
|
||
endDate: '',
|
||
status: '',
|
||
businessStatus: '',
|
||
pageNum: 1,
|
||
pageSize: 100
|
||
})
|
||
|
||
const tableData = ref<PurchasePlan[]>([])
|
||
const total = ref(0)
|
||
const selectedRows = ref<PurchasePlan[]>([])
|
||
const tableHeight = ref(500)
|
||
|
||
/** 物料分类快捷筛选标签 */
|
||
const categoryTags = ref<Array<{ itemTypeId: number; itemTypeName: string }>>([])
|
||
|
||
/** 底部汇总数据(来自后端 /summary 接口) */
|
||
const summaryData = ref<PurchasePlanSummary>({ totalPurchaseQty: 0, totalOrderedQty: 0 })
|
||
|
||
/** 导出弹窗 */
|
||
const showExportDialog = ref(false)
|
||
const exportLoading = ref(false)
|
||
|
||
// ============ 计算属性 ============
|
||
|
||
/** 底部汇总优先使用后端 summary 接口,fallback 到前端计算 */
|
||
const totalPurchaseQty = computed(() =>
|
||
summaryData.value.totalPurchaseQty || tableData.value.reduce((sum, r) => sum + (r.purchaseQty || 0), 0)
|
||
)
|
||
|
||
const totalOrderedQty = computed(() =>
|
||
summaryData.value.totalOrderedQty || tableData.value.reduce((sum, r) => sum + (r.orderedQty || 0), 0)
|
||
)
|
||
|
||
const canBatchApprove = computed(() =>
|
||
selectedRows.value.length > 0 &&
|
||
selectedRows.value.every(row => isEditable(row.status))
|
||
)
|
||
|
||
const canBatchUnapprove = computed(() =>
|
||
selectedRows.value.length > 0 &&
|
||
selectedRows.value.every(row => isApproved(row.status))
|
||
)
|
||
|
||
// ============ 工具方法 ============
|
||
|
||
function getStatusType(status: string): string {
|
||
return (STATUS_MAP[status]?.type ?? 'info') as string
|
||
}
|
||
|
||
function getStatusLabel(status: string): string {
|
||
return STATUS_MAP[status]?.label ?? status ?? '开立'
|
||
}
|
||
|
||
function getBusinessTypeLabel(type: string): string {
|
||
const opt = BUSINESS_TYPE_OPTIONS.find(o => o.value === type)
|
||
return opt?.label ?? type ?? '-'
|
||
}
|
||
|
||
function getBusinessStatusLabel(status: string): string {
|
||
return BUSINESS_STATUS_MAP[status] ?? status ?? '-'
|
||
}
|
||
|
||
function isEditable(status: string): boolean {
|
||
return status === 'PREPARE' || status === '开立' || status === 'REJECTED' || status === '退回'
|
||
}
|
||
|
||
function isApproved(status: string): boolean {
|
||
return status === 'APPROVED' || status === '审核'
|
||
}
|
||
|
||
function formatNumber(val: number | undefined | null): string {
|
||
if (val === undefined || val === null) return '-'
|
||
return Number(val).toLocaleString('zh-CN', { minimumFractionDigits: 0, maximumFractionDigits: 2 })
|
||
}
|
||
|
||
// ============ 数据加载 ============
|
||
|
||
/** 将本地 queryParams 转换为后端 RuoYi 格式参数 */
|
||
function buildApiParams(): PurchasePlanQuery {
|
||
const p: PurchasePlanQuery = {
|
||
pageNum: queryParams.pageNum,
|
||
pageSize: queryParams.pageSize,
|
||
needType: queryParams.needType
|
||
}
|
||
if (queryParams.salesOrderCode) p.salesOrderCode = queryParams.salesOrderCode
|
||
if (queryParams.purchaseCode) p.purchaseCode = queryParams.purchaseCode
|
||
if (queryParams.itemCode) p.itemCode = queryParams.itemCode
|
||
if (queryParams.itemName) p.itemName = queryParams.itemName
|
||
if (queryParams.status) p.status = queryParams.status
|
||
if (queryParams.businessStatus) p.businessStatus = queryParams.businessStatus
|
||
if (queryParams.beginDate) p['params[beginDate]'] = queryParams.beginDate
|
||
if (queryParams.endDate) p['params[endDate]'] = queryParams.endDate
|
||
if (queryParams.itemTypeId) p['params[itemTypeId]'] = queryParams.itemTypeId
|
||
return p
|
||
}
|
||
|
||
async function loadData() {
|
||
loading.value = true
|
||
try {
|
||
const params = buildApiParams()
|
||
let res
|
||
if (viewMode.value === 'detail') {
|
||
res = await getPurchasePlanList(params)
|
||
} else {
|
||
res = await getPurchasePlanDocList(params)
|
||
}
|
||
tableData.value = res.rows
|
||
total.value = res.total
|
||
|
||
// 同时加载底部汇总
|
||
loadSummary(params)
|
||
} catch (error) {
|
||
console.error('加载数据失败:', error)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
/** 加载底部汇总数据 */
|
||
async function loadSummary(params: PurchasePlanQuery) {
|
||
try {
|
||
const summary = await getPurchasePlanSummary(params)
|
||
summaryData.value = summary
|
||
} catch (error) {
|
||
console.error('加载汇总失败:', error)
|
||
}
|
||
}
|
||
|
||
async function loadCategoryTags() {
|
||
try {
|
||
categoryTags.value = await getItemTypeList()
|
||
} catch (error) {
|
||
console.error('加载物料分类失败:', error)
|
||
}
|
||
}
|
||
|
||
// ============ 搜索与筛选 ============
|
||
|
||
function handleQuery() {
|
||
queryParams.pageNum = 1
|
||
loadData()
|
||
}
|
||
|
||
function handleQueryAll() {
|
||
// 重置所有搜索条件
|
||
queryParams.salesOrderCode = ''
|
||
queryParams.purchaseCode = ''
|
||
queryParams.itemCode = ''
|
||
queryParams.itemName = ''
|
||
queryParams.itemTypeId = ''
|
||
queryParams.beginDate = ''
|
||
queryParams.endDate = ''
|
||
queryParams.status = ''
|
||
queryParams.businessStatus = ''
|
||
queryParams.pageNum = 1
|
||
loadData()
|
||
}
|
||
|
||
function handleCategoryTag(typeId: number) {
|
||
if (queryParams.itemTypeId === typeId) {
|
||
queryParams.itemTypeId = '' // 取消选中
|
||
} else {
|
||
queryParams.itemTypeId = typeId
|
||
}
|
||
handleQuery()
|
||
}
|
||
|
||
// ============ 视图切换 ============
|
||
|
||
function switchView(mode: 'detail' | 'document') {
|
||
// 保留公共搜索条件
|
||
const commonFields = {
|
||
purchaseCode: queryParams.purchaseCode,
|
||
beginDate: queryParams.beginDate,
|
||
endDate: queryParams.endDate,
|
||
pageNum: 1,
|
||
pageSize: queryParams.pageSize,
|
||
needType: queryParams.needType
|
||
}
|
||
|
||
// 清空视图特有字段
|
||
queryParams.salesOrderCode = ''
|
||
queryParams.itemCode = ''
|
||
queryParams.itemName = ''
|
||
queryParams.itemTypeId = ''
|
||
queryParams.status = ''
|
||
queryParams.businessStatus = ''
|
||
|
||
// 恢复公共字段
|
||
Object.assign(queryParams, commonFields)
|
||
|
||
viewMode.value = mode
|
||
selectedRows.value = []
|
||
loadData()
|
||
}
|
||
|
||
// ============ 选择操作 ============
|
||
|
||
function handleSelectionChange(rows: PurchasePlan[]) {
|
||
selectedRows.value = rows
|
||
}
|
||
|
||
// ============ 路由操作 ============
|
||
|
||
function handleAdd() {
|
||
router.push('/production/purchase-plan/new')
|
||
}
|
||
|
||
function handleViewDetail(row: PurchasePlan) {
|
||
router.push(`/production/purchase-plan/view/${row.purchaseId}`)
|
||
}
|
||
|
||
function handleEdit(row: PurchasePlan) {
|
||
router.push(`/production/purchase-plan/edit/${row.purchaseId}`)
|
||
}
|
||
|
||
function handleCopyPlan(row: PurchasePlan) {
|
||
// 翻单:复制采购计划生成新单据
|
||
router.push({
|
||
path: '/production/purchase-plan/new',
|
||
query: { copyFrom: String(row.purchaseId) }
|
||
})
|
||
}
|
||
|
||
// ============ 删除操作 ============
|
||
|
||
async function handleDeleteRow(row: PurchasePlan) {
|
||
try {
|
||
await deletePurchasePlan(String(row.purchaseId))
|
||
ElMessage.success('删除成功')
|
||
loadData()
|
||
} catch (error) {
|
||
console.error('删除失败:', error)
|
||
}
|
||
}
|
||
|
||
async function handleBatchDelete() {
|
||
if (selectedRows.value.length === 0) return
|
||
|
||
// 校验状态
|
||
const invalidRows = selectedRows.value.filter(row => !isEditable(row.status))
|
||
if (invalidRows.length > 0) {
|
||
ElMessage.warning('只能删除开立或退回状态的单据')
|
||
return
|
||
}
|
||
|
||
try {
|
||
await ElMessageBox.confirm(
|
||
`确认删除选中的 ${selectedRows.value.length} 条采购计划吗?`,
|
||
'删除确认',
|
||
{ type: 'warning' }
|
||
)
|
||
const ids = selectedRows.value.map(row => row.purchaseId).join(',')
|
||
await deletePurchasePlan(ids)
|
||
ElMessage.success('删除成功')
|
||
selectedRows.value = []
|
||
loadData()
|
||
} catch (error: any) {
|
||
if (error !== 'cancel') {
|
||
console.error('删除失败:', error)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============ 审核操作 ============
|
||
|
||
async function handleBatchApprove() {
|
||
if (selectedRows.value.length === 0) return
|
||
|
||
const invalidRows = selectedRows.value.filter(row => !isEditable(row.status))
|
||
if (invalidRows.length > 0) {
|
||
ElMessage.warning('只能审核开立或退回状态的单据')
|
||
return
|
||
}
|
||
|
||
try {
|
||
await ElMessageBox.confirm(
|
||
`确认审核选中的 ${selectedRows.value.length} 条采购计划吗?`,
|
||
'审核确认',
|
||
{ type: 'info' }
|
||
)
|
||
for (const row of selectedRows.value) {
|
||
await approvePurchasePlan(row.purchaseId)
|
||
}
|
||
ElMessage.success('审核成功')
|
||
selectedRows.value = []
|
||
loadData()
|
||
} catch (error: any) {
|
||
if (error !== 'cancel') {
|
||
console.error('审核失败:', error)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============ 反审核操作 ============
|
||
|
||
async function handleBatchUnapprove() {
|
||
if (selectedRows.value.length === 0) return
|
||
|
||
const invalidRows = selectedRows.value.filter(row => !isApproved(row.status))
|
||
if (invalidRows.length > 0) {
|
||
ElMessage.warning('只能反审核已审核状态的单据')
|
||
return
|
||
}
|
||
|
||
try {
|
||
await ElMessageBox.confirm(
|
||
`确认反审核选中的 ${selectedRows.value.length} 条采购计划吗?反审核后可重新编辑。`,
|
||
'反审核确认',
|
||
{ type: 'warning' }
|
||
)
|
||
const ids = selectedRows.value.map(row => row.purchaseId).join(',')
|
||
await unapprovePurchasePlan(ids)
|
||
ElMessage.success('反审核成功')
|
||
selectedRows.value = []
|
||
loadData()
|
||
} catch (error: any) {
|
||
if (error !== 'cancel') {
|
||
console.error('反审核失败:', error)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============ 导出操作 ============
|
||
|
||
function handleExport() {
|
||
showExportDialog.value = true
|
||
}
|
||
|
||
async function confirmExport() {
|
||
exportLoading.value = true
|
||
try {
|
||
const blob = (await exportPurchasePlan(queryParams)).data
|
||
const url = window.URL.createObjectURL(blob)
|
||
const link = document.createElement('a')
|
||
link.href = url
|
||
link.download = `采购计划单_${new Date().toISOString().split('T')[0]}.xlsx`
|
||
link.click()
|
||
window.URL.revokeObjectURL(url)
|
||
showExportDialog.value = false
|
||
ElMessage.success('导出成功')
|
||
} catch (error) {
|
||
console.error('导出失败:', error)
|
||
} finally {
|
||
exportLoading.value = false
|
||
}
|
||
}
|
||
|
||
// ============ 分页操作 ============
|
||
|
||
function handleSizeChange() {
|
||
queryParams.pageNum = 1
|
||
loadData()
|
||
}
|
||
|
||
function handleCurrentChange() {
|
||
loadData()
|
||
}
|
||
|
||
// ============ 表格高度 ============
|
||
|
||
function calcTableHeight() {
|
||
tableHeight.value = window.innerHeight - 310
|
||
}
|
||
|
||
// ============ 生命周期 ============
|
||
|
||
onMounted(() => {
|
||
loadData()
|
||
loadCategoryTags()
|
||
calcTableHeight()
|
||
window.addEventListener('resize', calcTableHeight)
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
window.removeEventListener('resize', calcTableHeight)
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page-container {
|
||
padding: 8px;
|
||
}
|
||
|
||
.search-card {
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.search-card :deep(.el-card__body) {
|
||
padding: 12px 12px 0;
|
||
}
|
||
|
||
.search-card :deep(.el-form-item) {
|
||
margin-bottom: 12px;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.table-card :deep(.el-card__body) {
|
||
padding: 12px;
|
||
}
|
||
|
||
.toolbar {
|
||
margin-bottom: 12px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
.toolbar-left {
|
||
display: flex;
|
||
gap: 8px;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
}
|
||
|
||
.toolbar-right {
|
||
display: flex;
|
||
align-items: center;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.category-tags {
|
||
display: flex;
|
||
gap: 6px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.category-tags :deep(.el-check-tag) {
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
padding: 2px 10px;
|
||
}
|
||
|
||
.table-footer {
|
||
margin-top: 12px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
}
|
||
|
||
.summary {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
}
|
||
|
||
.summary-value {
|
||
font-weight: bold;
|
||
margin-left: 4px;
|
||
margin-right: 20px;
|
||
}
|
||
|
||
.summary-value.primary {
|
||
color: #409eff;
|
||
}
|
||
</style>
|