| | |
| | | start-placeholder="开始日期" |
| | | end-placeholder="结束日期" |
| | | :shortcuts="dateShortcuts" |
| | | value-format="YYYY-MM-DD" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="煤种"> |
| | | <el-input v-model="searchParams.coalType" placeholder="请输入煤种" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="车次编码"> |
| | | <el-input v-model="searchParams.trainCode" placeholder="请输入车次编码" clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch" :loading="loading">查询</el-button> |
| | | <el-button @click="handleReset">重置</el-button> |
| | | <el-button @click="handleRefresh">刷新</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-title">今日入库总量</div> |
| | | <div class="card-value">{{ todayInStock.toFixed(2) }} 吨</div> |
| | | <div class="card-percentage" :class="{ positive: todayInStockPercentage > 0, negative: todayInStockPercentage < 0 }"> |
| | | <i v-if="todayInStockPercentage > 0" class="el-icon-caret-top" /> |
| | | <i v-else-if="todayInStockPercentage < 0" class="el-icon-caret-bottom" /> |
| | | {{ Math.abs(todayInStockPercentage).toFixed(1) }}% |
| | | </div> |
| | | <div class="card-value">{{ todayInboundTotal.toFixed(2) }} 吨</div> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-title">今日出库总量</div> |
| | | <div class="card-value">{{ todayOutStock.toFixed(2) }} 吨</div> |
| | | <div class="card-percentage" :class="{ positive: todayOutStockPercentage > 0, negative: todayOutStockPercentage < 0 }"> |
| | | <i v-if="todayOutStockPercentage > 0" class="el-icon-caret-top" /> |
| | | <i v-else-if="todayOutStockPercentage < 0" class="el-icon-caret-bottom" /> |
| | | {{ Math.abs(todayOutStockPercentage).toFixed(1) }}% |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="stat-card"> |
| | | <div class="card-icon"> |
| | | <i class="el-icon-takeaway-box" /> |
| | | </div> |
| | | <div class="card-content"> |
| | | <div class="card-title">今日领料总量</div> |
| | | <div class="card-value">{{ todayMaterialPick.toFixed(2) }} 吨</div> |
| | | <div class="card-percentage" :class="{ positive: todayMaterialPickPercentage > 0, negative: todayMaterialPickPercentage < 0 }"> |
| | | <i v-if="todayMaterialPickPercentage > 0" class="el-icon-caret-top" /> |
| | | <i v-else-if="todayMaterialPickPercentage < 0" class="el-icon-caret-bottom" /> |
| | | {{ Math.abs(todayMaterialPickPercentage).toFixed(1) }}% |
| | | </div> |
| | | <div class="card-value">{{ todayOutboundTotal.toFixed(2) }} 吨</div> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 产地分类 --> |
| | | <div class="category-section"> |
| | | <h3 class="category-title"><i class="el-icon-location"></i> 产地分布</h3> |
| | | <div class="category-items"> |
| | | <div v-for="item in originData" :key="item.name" class="category-item"> |
| | | <span class="item-name">{{ item.name }}</span> |
| | | <div class="item-bar"> |
| | | <div class="item-progress" :style="{ width: item.percentage + '%' }"></div> |
| | | </div> |
| | | <span class="item-value">{{ item.value.toFixed(2) }} 吨 ({{ item.percentage.toFixed(1) }}%)</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 热值分类 --> |
| | | <div class="category-section"> |
| | | <h3 class="category-title"><i class="el-icon-fire"></i> 热值分布</h3> |
| | |
| | | <!-- 车次编码分类 --> |
| | | <div class="category-section"> |
| | | <h3 class="category-title"><i class="el-icon-train"></i> 车次编码统计</h3> |
| | | <el-table :data="trainCodeData" style="width: 100%"> |
| | | <el-table-column prop="trainCode" label="车次编码" width="160" /> |
| | | <el-table-column prop="count" label="次数" width="100" align="right" /> |
| | | <el-table-column prop="totalQuantity" label="总量(吨)" width="120" align="right"> |
| | | <template #default="scope">{{ scope.row.totalQuantity.toFixed(2) }}</template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div class="train-code-list"> |
| | | <div v-for="item in trainCodeData" :key="item.code" class="train-code-item"> |
| | | <div class="train-code-info"> |
| | | <span class="train-code-label">{{ item.code }}</span> |
| | | <div class="train-code-details"> |
| | | <span class="train-count">次数: {{ item.count }}</span> |
| | | <span class="train-total">总量: {{ item.total.toFixed(2) }}吨</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | |
| | |
| | | </div> |
| | | </template> |
| | | <el-table v-loading="loading" :data="recentTransactions" style="width: 100%"> |
| | | <el-table-column prop="code" label="编码" width="160" /> |
| | | <el-table-column prop="type" label="类型" width="80"> |
| | | <el-table-column prop="id" label="序号" width="60" type="index" align="center"/> |
| | | <el-table-column prop="type" label="煤料类型" width="100"> |
| | | <template #default="scope"> |
| | | <span class="type-tag" |
| | | :class="{ |
| | | 'type-in': scope.row.type === '入库', |
| | | 'type-out': scope.row.type === '出库', |
| | | 'type-pick': scope.row.type === '领料' |
| | | }"> |
| | | {{ scope.row.type }} |
| | | </span> |
| | | <span>{{ scope.row.type === 1 ? '成品' : scope.row.type === 2 ? '原料' : '-' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="coalType" label="煤种" width="100" /> |
| | | <el-table-column prop="origin" label="产地" width="100" /> |
| | | <el-table-column prop="calorificValue" label="热值" width="120" /> |
| | | <el-table-column prop="quantity" label="数量(吨)" width="120" /> |
| | | <el-table-column prop="trainCode" label="车次编码" width="120" /> |
| | | <el-table-column prop="supplierName" label="供应商名称" /> |
| | | <el-table-column prop="coalName" label="煤种" /> |
| | | <el-table-column prop="purchaseQuantity" label="采购数量" width="100" /> |
| | | <el-table-column prop="priceIncludingTax" label=" 单价(含税)" /> |
| | | <el-table-column prop="totalPriceIncludingTax" label="总价(含税)" /> |
| | | <el-table-column prop="createTime" label="创建时间" width="180" /> |
| | | </el-table> |
| | | </el-card> |
| | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { getMaterialStatistics, getCoalTypeDistribution, getHeatValueDistribution, getCarCodeDistribution, getRecentTransaction } from '@/api/productionScheduling' |
| | | |
| | | // 搜索参数 |
| | | const searchParams = ref({ |
| | |
| | | { |
| | | text: '今天', |
| | | value: () => { |
| | | const formatDate = (date) => { |
| | | 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 end = new Date() |
| | | const start = new Date() |
| | | return [start, end] |
| | | return [formatDate(start), formatDate(end)] |
| | | } |
| | | }, |
| | | { |
| | | text: '昨天', |
| | | value: () => { |
| | | const formatDate = (date) => { |
| | | 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 end = new Date() |
| | | const start = new Date() |
| | | start.setTime(start.getTime() - 3600 * 1000 * 24) |
| | | return [start, end] |
| | | return [formatDate(start), formatDate(end)] |
| | | } |
| | | }, |
| | | { |
| | | text: '近7天', |
| | | value: () => { |
| | | const formatDate = (date) => { |
| | | 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 end = new Date() |
| | | const start = new Date() |
| | | start.setTime(start.getTime() - 3600 * 1000 * 24 * 7) |
| | | return [start, end] |
| | | return [formatDate(start), formatDate(end)] |
| | | } |
| | | }, |
| | | { |
| | | text: '近30天', |
| | | value: () => { |
| | | const formatDate = (date) => { |
| | | 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 end = new Date() |
| | | const start = new Date() |
| | | start.setTime(start.getTime() - 3600 * 1000 * 24 * 30) |
| | | return [start, end] |
| | | return [formatDate(start), formatDate(end)] |
| | | } |
| | | }, |
| | | { |
| | | text: '本月', |
| | | value: () => { |
| | | const formatDate = (date) => { |
| | | 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 end = new Date() |
| | | const start = new Date(end.getFullYear(), end.getMonth(), 1) |
| | | return [start, end] |
| | | return [formatDate(start), formatDate(end)] |
| | | } |
| | | } |
| | | ] |
| | | |
| | | // 统计数据 |
| | | const todayInStock = ref(1250.75) |
| | | const todayInStockPercentage = ref(5.2) |
| | | const todayOutStock = ref(890.30) |
| | | const todayOutStockPercentage = ref(-2.1) |
| | | const todayMaterialPick = ref(320.45) |
| | | const todayMaterialPickPercentage = ref(10.3) |
| | | const currentTotalStock = ref(15680.95) |
| | | const todayInboundTotal = ref(0) |
| | | const todayOutboundTotal = ref(0) |
| | | const currentTotalStock = ref(0) |
| | | |
| | | // 百分比数据(根据需要保留) |
| | | const todayInStockPercentage = ref(0) |
| | | const todayOutStockPercentage = ref(0) |
| | | |
| | | // 分类数据 - 煤种分布 |
| | | const coalTypeData = ref([ |
| | |
| | | { name: '褐煤', value: 2800, percentage: 18.7 }, |
| | | { name: '贫煤', value: 2500, percentage: 16.7 }, |
| | | { name: '瘦煤', value: 2680, percentage: 17.9 } |
| | | ]) |
| | | |
| | | // 分类数据 - 产地分布 |
| | | const originData = ref([ |
| | | { name: '山西', value: 5200, percentage: 33.1 }, |
| | | { name: '内蒙古', value: 3800, percentage: 24.2 }, |
| | | { name: '陕西', value: 2900, percentage: 18.5 }, |
| | | { name: '新疆', value: 2100, percentage: 13.4 }, |
| | | { name: '贵州', value: 1680, percentage: 10.7 } |
| | | ]) |
| | | |
| | | // 分类数据 - 热值分布 |
| | |
| | | ]) |
| | | |
| | | // 分类数据 - 车次编码统计 |
| | | const trainCodeData = ref([ |
| | | { trainCode: 'C12345', count: 12, totalQuantity: 4200.5 }, |
| | | { trainCode: 'C12346', count: 8, totalQuantity: 2800.2 }, |
| | | { trainCode: 'C12347', count: 10, totalQuantity: 3500.0 }, |
| | | { trainCode: 'C12348', count: 7, totalQuantity: 2450.8 }, |
| | | { trainCode: 'C12349', count: 5, totalQuantity: 1800.3 } |
| | | ]) |
| | | const trainCodeData = ref([]) |
| | | |
| | | // 模拟数据 - 最近交易记录 |
| | | const recentTransactions = ref([ |
| | | { code: 'RK20230507001', type: '入库', coalType: '烟煤', origin: '山西', calorificValue: '5300大卡', quantity: 350.5, trainCode: 'C12345', createTime: '2023-05-07 09:30:15' }, |
| | | { code: 'CK20230507001', type: '出库', coalType: '无烟煤', origin: '内蒙古', calorificValue: '5800大卡', quantity: 280.2, trainCode: 'C12346', createTime: '2023-05-07 10:15:30' }, |
| | | { code: 'LL20230507001', type: '领料', coalType: '褐煤', origin: '新疆', calorificValue: '4200大卡', quantity: 120.8, trainCode: '', createTime: '2023-05-07 11:05:45' }, |
| | | { code: 'RK20230507002', type: '入库', coalType: '贫煤', origin: '陕西', calorificValue: '5100大卡', quantity: 400.0, trainCode: 'C12347', createTime: '2023-05-07 13:20:00' }, |
| | | { code: 'CK20230507002', type: '出库', coalType: '瘦煤', origin: '贵州', calorificValue: '5400大卡', quantity: 310.5, trainCode: 'C12348', createTime: '2023-05-07 14:45:15' }, |
| | | { code: 'LL20230507002', type: '领料', coalType: '烟煤', origin: '山西', calorificValue: '5300大卡', quantity: 200.0, trainCode: '', createTime: '2023-05-07 15:30:30' } |
| | | ]) |
| | | // 最近交易记录数据 |
| | | const recentTransactions = ref([]) |
| | | |
| | | // 加载状态 |
| | | const loading = ref(false) |
| | |
| | | loading.value = true |
| | | console.log('搜索参数:', searchParams.value) |
| | | |
| | | // 模拟API请求延迟 |
| | | await new Promise(resolve => setTimeout(resolve, 800)) |
| | | |
| | | // 模拟数据更新 |
| | | todayInStock.value = 1000 + Math.random() * 500 |
| | | todayInStockPercentage.value = -10 + Math.random() * 20 |
| | | todayOutStock.value = 800 + Math.random() * 400 |
| | | todayOutStockPercentage.value = -10 + Math.random() * 20 |
| | | todayMaterialPick.value = 200 + Math.random() * 200 |
| | | todayMaterialPickPercentage.value = -10 + Math.random() * 20 |
| | | currentTotalStock.value = 15000 + Math.random() * 3000 |
| | | |
| | | // 更新分类数据 |
| | | coalTypeData.value.forEach(item => { |
| | | item.value = 2000 + Math.random() * 3000 |
| | | }) |
| | | |
| | | originData.value.forEach(item => { |
| | | item.value = 1500 + Math.random() * 4000 |
| | | }) |
| | | |
| | | calorificData.value.forEach(item => { |
| | | item.value = 500 + Math.random() * 5000 |
| | | }) |
| | | |
| | | trainCodeData.value.forEach(item => { |
| | | item.count = 3 + Math.floor(Math.random() * 15) |
| | | item.totalQuantity = 1000 + Math.random() * 4000 |
| | | }) |
| | | |
| | | // 重新计算百分比 |
| | | const calculatePercentages = (data) => { |
| | | const total = data.reduce((sum, item) => sum + item.value, 0) |
| | | data.forEach(item => { |
| | | item.percentage = total > 0 ? (item.value / total) * 100 : 0 |
| | | }) |
| | | // 构建请求参数 |
| | | const params = { |
| | | entryDateStart: searchParams.value.dateRange && searchParams.value.dateRange[0] ? searchParams.value.dateRange[0] : '', |
| | | entryDateEnd: searchParams.value.dateRange && searchParams.value.dateRange[1] ? searchParams.value.dateRange[1] : '' |
| | | } |
| | | |
| | | calculatePercentages(coalTypeData.value) |
| | | calculatePercentages(originData.value) |
| | | calculatePercentages(calorificData.value) |
| | | // 调用API获取统计数据 |
| | | const response = await getMaterialStatistics(params) |
| | | |
| | | ElMessage.success('查询成功') |
| | | // 根据项目中其他地方的代码风格,检查响应成功的条件应该是code === 200 |
| | | if (response.code === 200 && response.data) { |
| | | // 更新统计数据 |
| | | todayInboundTotal.value = response.data.todayInboundTotal || 0 |
| | | todayOutboundTotal.value = response.data.todayOutboundTotal || 0 |
| | | currentTotalStock.value = response.data.currentInventoryTotal || 0 |
| | | |
| | | // 可以在这里添加百分比的计算逻辑,如果API返回了相关数据 |
| | | // 暂时保留原有的模拟百分比数据 |
| | | todayInStockPercentage.value = -10 + Math.random() * 20 |
| | | todayOutStockPercentage.value = -10 + Math.random() * 20 |
| | | } |
| | | |
| | | // 调用API获取煤种分布数据 |
| | | const coalTypeResponse = await getCoalTypeDistribution(params) |
| | | |
| | | if (coalTypeResponse.code === 200 && coalTypeResponse.data) { |
| | | // 根据接口响应更新煤种分布数据 |
| | | coalTypeData.value = coalTypeResponse.data.map(item => ({ |
| | | name: item.name || '', |
| | | value: item.value || 0, |
| | | percentage: item.percent || 0 |
| | | })) |
| | | } |
| | | |
| | | // 调用API获取热值分布数据 |
| | | const heatValueResponse = await getHeatValueDistribution(params) |
| | | |
| | | if (heatValueResponse.code === 200 && heatValueResponse.data) { |
| | | // 根据接口响应更新热值分布数据 |
| | | calorificData.value = heatValueResponse.data.map(item => ({ |
| | | name: item.name || '', |
| | | value: item.value || 0, |
| | | percentage: item.percent || 0 |
| | | })) |
| | | } |
| | | |
| | | // 调用API获取车次编码分布数据 |
| | | const carCodeResponse = await getCarCodeDistribution(params) |
| | | |
| | | if (carCodeResponse.code === 200 && carCodeResponse.data) { |
| | | // 根据接口响应更新车次编码数据 |
| | | trainCodeData.value = carCodeResponse.data |
| | | } |
| | | |
| | | // 调用API获取最近交易记录 |
| | | const transactionResponse = await getRecentTransaction(params) |
| | | |
| | | if (transactionResponse.code === 200 && transactionResponse.data) { |
| | | // 根据接口响应更新最近交易记录数据 |
| | | // 由于接口返回的数据结构与表格所需的结构不完全匹配,需要进行字段映射 |
| | | recentTransactions.value = transactionResponse.data |
| | | } |
| | | } catch (error) { |
| | | console.error('查询失败:', error) |
| | | ElMessage.error('查询失败,请稍后重试') |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | |
| | | // 初始化页面 |
| | | onMounted(() => { |
| | | // 默认查询近7天数据 |
| | | const formatDate = (date) => { |
| | | 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 end = new Date() |
| | | const start = new Date() |
| | | start.setTime(start.getTime() - 3600 * 1000 * 24 * 7) |
| | | searchParams.value.dateRange = [start, end] |
| | | searchParams.value.dateRange = [formatDate(start), formatDate(end)] |
| | | |
| | | // 初始加载数据 |
| | | handleSearch() |
| | |
| | | gap: 8px; |
| | | } |
| | | |
| | | .train-code-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .train-code-item { |
| | | flex: 1; |
| | | min-width: 200px; |
| | | padding: 16px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .train-code-info { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .train-code-label { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .train-code-details { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .train-count, |
| | | .train-total { |
| | | display: inline-block; |
| | | } |
| | | |
| | | .category-items { |
| | | display: flex; |
| | | flex-direction: column; |
| | |
| | | border: 1px solid #FFCCC7; |
| | | } |
| | | |
| | | /* 修复日期选择器被动事件监听器问题 */ |
| | | .el-date-editor { |
| | | touch-action: none; |
| | | } |
| | | |
| | | /* 禁用日期选择器的默认选择器指示器的触摸交互 */ |
| | | .el-date-picker__editor-wrap .el-input__inner::-webkit-calendar-picker-indicator { |
| | | -webkit-touch-callout: none; |
| | | -webkit-user-select: none; |
| | | user-select: none; |
| | | } |
| | | |
| | | /* 响应式设计 */ |
| | | @media screen and (max-width: 1200px) { |
| | | .stats-cards { |