<template>
|
<view class="output-statistics">
|
<PageHeader title="产量统计"
|
@back="goBack" />
|
|
<!-- 顶部页签:产量统计 / 计件工资汇总 -->
|
<view class="main-tabs">
|
<up-tabs :list="mainTabs"
|
:current="mainTabIndex"
|
@change="handleMainTabChange"
|
:activeStyle="{ color: '#2979ff', fontWeight: 'bold' }"
|
lineWidth="40"
|
lineHeight="3" />
|
</view>
|
|
<!-- ============ 产量统计 ============ -->
|
<template v-if="mainTabIndex === 0">
|
<view class="search-section">
|
<view class="dimension-tabs">
|
<up-tabs :list="dimensionTabs"
|
:current="dimensionIndex"
|
@change="handleDimensionChange"
|
:activeStyle="{ color: '#2979ff', fontWeight: 'bold' }"
|
lineWidth="30"
|
lineHeight="3" />
|
</view>
|
<view class="filter-bar">
|
<view class="date-input"
|
@click="showOutputCalendar = true">
|
<up-icon name="calendar"
|
size="18"
|
color="#999"></up-icon>
|
<text class="date-text"
|
:class="{ placeholder: !outputForm.startDate }">{{ outputDateText }}</text>
|
</view>
|
<view v-if="dimension === 'device'"
|
class="device-input"
|
@click="openDeviceSheet">
|
<text :class="{ placeholder: !outputForm.deviceName }">{{ outputForm.deviceName || "选择设备" }}</text>
|
<up-icon name="arrow-down"
|
size="14"
|
color="#999"></up-icon>
|
</view>
|
</view>
|
<view class="filter-actions-top">
|
<up-button type="primary"
|
size="small"
|
text="搜索"
|
@click="handleOutputSearch"></up-button>
|
</view>
|
</view>
|
|
<view v-if="outputList.length > 0"
|
class="ledger-list">
|
<view v-for="(item, index) in outputList"
|
:key="index"
|
class="ledger-item no-click">
|
<view class="item-header">
|
<view class="item-left">
|
<view class="document-icon">
|
<up-icon name="list-dot"
|
size="16"
|
color="#ffffff"></up-icon>
|
</view>
|
<text class="item-id">{{ item.groupName || "未指定" }}</text>
|
</view>
|
<text class="item-index">{{ item.workCount || 0 }} 单</text>
|
</view>
|
<up-divider></up-divider>
|
<view class="item-details">
|
<view class="detail-row">
|
<text class="detail-label">产量</text>
|
<text class="detail-value highlight">{{ Number(item.quantity) || 0 }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">报废数量</text>
|
<text class="detail-value danger">{{ Number(item.scrapQty) || 0 }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">工时(h)</text>
|
<text class="detail-value">{{ Number(item.workHour) || 0 }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
<view v-else
|
class="no-data">
|
<up-empty mode="data"
|
text="暂无数据"></up-empty>
|
</view>
|
</template>
|
|
<!-- ============ 计件工资汇总 ============ -->
|
<template v-else>
|
<view class="search-section">
|
<view class="filter-bar">
|
<view class="date-input"
|
@click="showWageCalendar = true">
|
<up-icon name="calendar"
|
size="18"
|
color="#999"></up-icon>
|
<text class="date-text"
|
:class="{ placeholder: !wageForm.startDate }">{{ wageDateText }}</text>
|
</view>
|
<up-input class="name-input"
|
v-model="wageForm.userName"
|
placeholder="人员姓名"
|
clearable
|
@change="handleWageSearch" />
|
</view>
|
<view class="filter-actions-top">
|
<up-button type="primary"
|
size="small"
|
text="搜索"
|
@click="handleWageSearch"></up-button>
|
</view>
|
</view>
|
|
<view v-if="wageList.length > 0"
|
class="ledger-list">
|
<view v-for="(item, index) in wageList"
|
:key="index"
|
class="ledger-item no-click">
|
<view class="item-header">
|
<view class="item-left">
|
<view class="document-icon">
|
<up-icon name="account"
|
size="16"
|
color="#ffffff"></up-icon>
|
</view>
|
<text class="item-id">{{ item.userName || "未知" }}</text>
|
</view>
|
<text class="item-index">{{ item.workCount || 0 }} 单</text>
|
</view>
|
<up-divider></up-divider>
|
<view class="item-details">
|
<view class="detail-row">
|
<text class="detail-label">生产数量</text>
|
<text class="detail-value">{{ Number(item.quantity) || 0 }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">总工时(h)</text>
|
<text class="detail-value">{{ Number(item.workHour) || 0 }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">计件工资</text>
|
<text class="detail-value">¥{{ Number(item.pieceWage) || 0 }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">应发合计</text>
|
<text class="detail-value highlight">¥{{ Number(item.wages) || 0 }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
<view v-else
|
class="no-data">
|
<up-empty mode="data"
|
text="暂无数据"></up-empty>
|
</view>
|
</template>
|
|
<!-- 日期区间选择 -->
|
<up-calendar :show="showOutputCalendar"
|
mode="range"
|
:maxDate="maxDate"
|
minDate="2022-01-01"
|
:monthNum="monthNum"
|
@confirm="onOutputDateConfirm"
|
@close="showOutputCalendar = false"></up-calendar>
|
<up-calendar :show="showWageCalendar"
|
mode="range"
|
:maxDate="maxDate"
|
minDate="2022-01-01"
|
:monthNum="monthNum"
|
@confirm="onWageDateConfirm"
|
@close="showWageCalendar = false"></up-calendar>
|
|
<!-- 设备筛选 -->
|
<up-action-sheet :show="showDeviceSheet"
|
:actions="deviceOptions"
|
title="选择设备"
|
@select="selectDevice"
|
@close="showDeviceSheet = false" />
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed } from "vue";
|
import { onShow } from "@dcloudio/uni-app";
|
import dayjs from "dayjs";
|
import PageHeader from "@/components/PageHeader.vue";
|
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
|
import {
|
outputStat,
|
wageSummary,
|
} from "@/api/productionManagement/productionProductMain.js";
|
|
const goBack = () => {
|
uni.navigateBack();
|
};
|
|
// 顶部页签
|
const mainTabs = [{ name: "产量统计" }, { name: "计件工资汇总" }];
|
const mainTabIndex = ref(0);
|
const handleMainTabChange = item => {
|
mainTabIndex.value = item.index;
|
if (mainTabIndex.value === 0) {
|
handleOutputSearch();
|
} else {
|
handleWageSearch();
|
}
|
};
|
|
// 维度:device / operation / product
|
const dimensionTabs = [
|
{ name: "按设备", value: "device" },
|
{ name: "按工序", value: "operation" },
|
{ name: "按产品", value: "product" },
|
];
|
const dimensionIndex = ref(0);
|
const dimension = computed(() => dimensionTabs[dimensionIndex.value].value);
|
const handleDimensionChange = item => {
|
dimensionIndex.value = item.index;
|
handleOutputSearch();
|
};
|
|
// 日期区间
|
const maxDate = dayjs().format("YYYY-MM-DD");
|
const monthNum = computed(() => dayjs(maxDate).diff(dayjs("2022-02-01"), "month") + 1);
|
const showOutputCalendar = ref(false);
|
const showWageCalendar = ref(false);
|
|
// 产量统计
|
const outputForm = reactive({
|
startDate: dayjs().startOf("month").format("YYYY-MM-DD"),
|
endDate: dayjs().endOf("month").format("YYYY-MM-DD"),
|
deviceId: "",
|
deviceName: "",
|
});
|
const outputList = ref([]);
|
const outputDateText = computed(() =>
|
outputForm.startDate && outputForm.endDate
|
? `${outputForm.startDate} 至 ${outputForm.endDate}`
|
: "请选择日期区间"
|
);
|
const onOutputDateConfirm = e => {
|
outputForm.startDate = e[0];
|
outputForm.endDate = e[e.length - 1];
|
showOutputCalendar.value = false;
|
handleOutputSearch();
|
};
|
|
// 计件工资汇总
|
const wageForm = reactive({
|
startDate: dayjs().startOf("month").format("YYYY-MM-DD"),
|
endDate: dayjs().endOf("month").format("YYYY-MM-DD"),
|
userName: "",
|
});
|
const wageList = ref([]);
|
const wageDateText = computed(() =>
|
wageForm.startDate && wageForm.endDate
|
? `${wageForm.startDate} 至 ${wageForm.endDate}`
|
: "请选择日期区间"
|
);
|
const onWageDateConfirm = e => {
|
wageForm.startDate = e[0];
|
wageForm.endDate = e[e.length - 1];
|
showWageCalendar.value = false;
|
handleWageSearch();
|
};
|
|
// 设备下拉
|
const showDeviceSheet = ref(false);
|
const deviceOptions = ref([]);
|
const loadDevices = async () => {
|
if (deviceOptions.value.length > 0) return;
|
try {
|
const { data } = await getDeviceLedger();
|
const rows = Array.isArray(data) ? data : data?.records || [];
|
deviceOptions.value = rows.map(item => ({
|
name: item.deviceName || item.name || "-",
|
value: item.id,
|
}));
|
} catch (e) {
|
console.error("加载设备列表失败", e);
|
}
|
};
|
const openDeviceSheet = () => {
|
loadDevices();
|
showDeviceSheet.value = true;
|
};
|
const selectDevice = e => {
|
outputForm.deviceId = e.value;
|
outputForm.deviceName = e.name;
|
showDeviceSheet.value = false;
|
handleOutputSearch();
|
};
|
|
// 查询:产量统计
|
const handleOutputSearch = () => {
|
outputStat({
|
dimension: dimension.value,
|
startDate: outputForm.startDate || undefined,
|
endDate: outputForm.endDate || undefined,
|
deviceId: dimension.value === "device" ? outputForm.deviceId || undefined : undefined,
|
})
|
.then(res => {
|
outputList.value = Array.isArray(res.data) ? res.data : res.data?.records || [];
|
})
|
.catch(() => {
|
outputList.value = [];
|
});
|
};
|
|
// 查询:计件工资汇总
|
const handleWageSearch = () => {
|
wageSummary({
|
startDate: wageForm.startDate || undefined,
|
endDate: wageForm.endDate || undefined,
|
userName: wageForm.userName || undefined,
|
})
|
.then(res => {
|
wageList.value = Array.isArray(res.data) ? res.data : res.data?.records || [];
|
})
|
.catch(() => {
|
wageList.value = [];
|
});
|
};
|
|
onShow(() => {
|
handleOutputSearch();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
@import "@/styles/procurement-common.scss";
|
|
.output-statistics {
|
min-height: 100vh;
|
background: #f8f9fa;
|
}
|
|
.main-tabs {
|
background: #fff;
|
margin-bottom: 10px;
|
}
|
|
.dimension-tabs {
|
margin-bottom: 16px;
|
}
|
|
.filter-bar {
|
display: flex;
|
align-items: center;
|
gap: 12px;
|
}
|
|
.date-input,
|
.device-input {
|
flex: 1;
|
height: 40px;
|
background: #f5f5f5;
|
border-radius: 8px;
|
padding: 0 12px;
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
}
|
|
.date-input .date-text,
|
.device-input {
|
font-size: 14px;
|
color: #333;
|
}
|
|
.device-input {
|
justify-content: space-between;
|
}
|
|
.placeholder {
|
color: #999;
|
}
|
|
.name-input {
|
flex: 1;
|
height: 40px;
|
background: #f5f5f5;
|
border-radius: 8px;
|
padding: 0 12px;
|
}
|
|
.filter-actions-top {
|
display: flex;
|
justify-content: flex-end;
|
gap: 12px;
|
margin-top: 12px;
|
}
|
|
.item-index {
|
font-size: 12px;
|
color: #999;
|
background: #f5f5f5;
|
padding: 2px 8px;
|
border-radius: 12px;
|
}
|
|
.ledger-item.no-click:active {
|
transform: none;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
}
|
</style>
|