<template>
|
<view class="production-accounting">
|
<PageHeader title="生产核算"
|
@back="goBack" />
|
<!-- 筛选区域 -->
|
<view class="filter-section">
|
<view class="date-type-selector">
|
<up-tabs :list="dateTypeList"
|
:current="currentDateTypeIndex"
|
@change="handleDateTypeChange"
|
:activeStyle="{ color: '#2979ff', fontWeight: 'bold' }"
|
lineWidth="30"
|
lineHeight="3" />
|
</view>
|
<view class="date-picker-bar"
|
@click="showDatePicker = true">
|
<view class="date-display">
|
<up-icon name="calendar"
|
size="20"
|
color="#2979ff"></up-icon>
|
<text class="date-text">{{ dateDisplayText }}</text>
|
</view>
|
<up-icon name="arrow-right"
|
size="16"
|
color="#999"></up-icon>
|
</view>
|
</view>
|
<!-- 汇总列表 -->
|
<view class="summary-section"
|
v-if="!showDetail">
|
<view class="section-header">
|
<text class="section-title">生产人员汇总</text>
|
</view>
|
<view class="ledger-list"
|
v-if="summaryList.length > 0">
|
<view v-for="(item, index) in summaryList"
|
:key="index"
|
class="ledger-item"
|
@click="handleRowClick(item)">
|
<view class="item-header">
|
<view class="item-left">
|
<view class="user-icon">
|
<up-icon name="account"
|
size="16"
|
color="#ffffff"></up-icon>
|
</view>
|
<text class="item-id">{{ item.schedulingUserName || '未知' }}</text>
|
</view>
|
<view class="item-right">
|
<up-icon name="arrow-right"
|
size="16"
|
color="#999"></up-icon>
|
</view>
|
</view>
|
<up-divider></up-divider>
|
<view class="item-details">
|
<view class="detail-grid">
|
<view class="grid-item">
|
<text class="grid-label">产量</text>
|
<text class="grid-value">{{ item.finishedNum || 0 }}</text>
|
</view>
|
<view class="grid-item">
|
<text class="grid-label">工资</text>
|
<text class="grid-value highlight">¥{{ item.wages || 0 }}</text>
|
</view>
|
<view class="grid-item">
|
<text class="grid-label">合格率</text>
|
<text class="grid-value">{{ formatOutputRate(item.outputRate) }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
<view v-else
|
class="no-data">
|
<up-empty mode="data"
|
text="暂无汇总数据" />
|
</view>
|
</view>
|
<!-- 明细列表 (点击汇总行后显示) -->
|
<view class="detail-section"
|
v-else>
|
<view class="section-header back-bar"
|
@click="showDetail = false">
|
<up-icon name="arrow-left"
|
size="16"
|
color="#2979ff"></up-icon>
|
<text class="back-text">返回汇总 ({{ currentUserName }})</text>
|
</view>
|
<view class="ledger-list"
|
v-if="detailList.length > 0">
|
<view v-for="(item, index) in detailList"
|
:key="index"
|
class="ledger-item no-click">
|
<view class="item-header">
|
<view class="item-left">
|
<view class="product-icon">
|
<up-icon name="shopping-cart"
|
size="16"
|
color="#ffffff"></up-icon>
|
</view>
|
<text class="item-id">{{ item.productName }}</text>
|
</view>
|
<view class="item-tag">
|
<text class="tag-text">{{ item.schedulingDate }}</text>
|
</view>
|
</view>
|
<up-divider></up-divider>
|
<view class="item-details">
|
<view class="detail-row">
|
<text class="detail-label">生产日期</text>
|
<text class="detail-value">{{ item.schedulingDate || '-' }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">生产人</text>
|
<text class="detail-value">{{ item.schedulingUserName || '-' }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">规格型号</text>
|
<text class="detail-value">{{ item.productModelName }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">工序</text>
|
<text class="detail-value">{{ item.process }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">生产数量</text>
|
<text class="detail-value">{{ item.quantity }} {{ item.unit }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">工时(h)</text>
|
<text class="detail-value">{{ item.workHour || 0 }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">工时定额</text>
|
<text class="detail-value">{{ item.workHours }}</text>
|
</view>
|
<view class="detail-row">
|
<text class="detail-label">工资</text>
|
<text class="detail-value highlight">¥{{ item.wages }}</text>
|
</view>
|
</view>
|
</view>
|
<up-loadmore :status="loadStatus"
|
@loadmore="getDetailList" />
|
</view>
|
<view v-else
|
class="no-data">
|
<up-empty mode="data"
|
text="暂无明细数据" />
|
</view>
|
</view>
|
<!-- 日期选择器 -->
|
<up-datetime-picker :show="showDatePicker"
|
v-model="pickerValue"
|
:mode="currentDateType === 'day' ? 'date' : 'year-month'"
|
@confirm="handleDateConfirm"
|
@cancel="showDatePicker = false" />
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed, onMounted } from "vue";
|
import {
|
salesLedgerProductionAccountingList,
|
salesLedgerProductionAccountingListProductionDetails,
|
} from "@/api/productionManagement/productionCosting";
|
import PageHeader from "@/components/PageHeader.vue";
|
import dayjs from "dayjs";
|
|
// 筛选相关
|
const dateTypeList = [{ name: "日" }, { name: "月" }];
|
const currentDateTypeIndex = ref(0);
|
const currentDateType = computed(() =>
|
currentDateTypeIndex.value === 0 ? "day" : "month"
|
);
|
const showDatePicker = ref(false);
|
const pickerValue = ref(Date.now());
|
const selectedDate = ref(dayjs().format("YYYY-MM-DD"));
|
|
const dateDisplayText = computed(() => {
|
return currentDateType.value === "day"
|
? selectedDate.value
|
: dayjs(selectedDate.value).format("YYYY-MM");
|
});
|
|
// 数据相关
|
const summaryList = ref([]);
|
const detailList = ref([]);
|
const showDetail = ref(false);
|
const currentUserName = ref("");
|
const loadStatus = ref("loadmore");
|
|
const page = reactive({
|
current: 1,
|
size: 20,
|
total: 0,
|
});
|
|
const page1 = reactive({
|
current: 1,
|
size: 20,
|
total: 0,
|
});
|
|
// 返回上一页
|
const goBack = () => {
|
uni.navigateBack();
|
};
|
|
// 切换日期类型
|
const handleDateTypeChange = index => {
|
currentDateTypeIndex.value = index.index;
|
if (currentDateType.value === "day") {
|
selectedDate.value = dayjs().format("YYYY-MM-DD");
|
} else {
|
selectedDate.value = dayjs().startOf("month").format("YYYY-MM-DD");
|
}
|
reloadData();
|
};
|
|
// 确认日期选择
|
const handleDateConfirm = e => {
|
selectedDate.value = dayjs(e.value).format("YYYY-MM-DD");
|
showDatePicker.value = false;
|
reloadData();
|
};
|
|
// 格式化合格率
|
const formatOutputRate = val => {
|
if (val == null || val === "") return "-";
|
return parseFloat(val).toFixed(2) + "%";
|
};
|
|
// 加载汇总列表
|
const getSummaryList = () => {
|
uni.showLoading({ title: "加载中..." });
|
const params = {
|
dateType: currentDateType.value,
|
entryDate: currentDateType.value === "day" ? selectedDate.value : undefined,
|
entryDateStart:
|
currentDateType.value === "month"
|
? dayjs(selectedDate.value).startOf("month").format("YYYY-MM-DD")
|
: undefined,
|
entryDateEnd:
|
currentDateType.value === "month"
|
? dayjs(selectedDate.value).endOf("month").format("YYYY-MM-DD")
|
: undefined,
|
pageNum: page.current,
|
pageSize: page.size,
|
};
|
|
salesLedgerProductionAccountingList(params)
|
.then(res => {
|
summaryList.value = res.data.records || [];
|
page.total = res.data.total || 0;
|
})
|
.finally(() => {
|
uni.hideLoading();
|
});
|
};
|
|
// 加载明细列表
|
const getDetailList = (isLoadMore = false) => {
|
if (!isLoadMore) {
|
page1.current = 1;
|
detailList.value = [];
|
}
|
loadStatus.value = "loading";
|
|
const params = {
|
schedulingUserName: currentUserName.value,
|
dateType: currentDateType.value,
|
entryDate: currentDateType.value === "day" ? selectedDate.value : undefined,
|
entryDateStart:
|
currentDateType.value === "month"
|
? dayjs(selectedDate.value).startOf("month").format("YYYY-MM-DD")
|
: undefined,
|
entryDateEnd:
|
currentDateType.value === "month"
|
? dayjs(selectedDate.value).endOf("month").format("YYYY-MM-DD")
|
: undefined,
|
pageNum: page1.current,
|
pageSize: page1.size,
|
};
|
|
salesLedgerProductionAccountingListProductionDetails(params)
|
.then(res => {
|
const records = res.data.records || [];
|
detailList.value = isLoadMore
|
? [...detailList.value, ...records]
|
: records;
|
page1.total = res.data.total || 0;
|
|
if (detailList.value.length >= page1.total) {
|
loadStatus.value = "nomore";
|
} else {
|
loadStatus.value = "loadmore";
|
page1.current++;
|
}
|
})
|
.catch(() => {
|
loadStatus.value = "loadmore";
|
});
|
};
|
|
// 点击汇总行
|
const handleRowClick = item => {
|
currentUserName.value = item.schedulingUserName;
|
showDetail.value = true;
|
getDetailList();
|
};
|
|
// 重新加载数据
|
const reloadData = () => {
|
page.current = 1;
|
showDetail.value = false;
|
getSummaryList();
|
};
|
|
onMounted(() => {
|
getSummaryList();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.production-accounting {
|
background-color: #f5f7fa;
|
min-height: 100vh;
|
padding-bottom: 30rpx;
|
|
.filter-section {
|
background-color: #ffffff;
|
padding: 20rpx 30rpx;
|
margin-bottom: 20rpx;
|
|
.date-type-selector {
|
margin-bottom: 20rpx;
|
}
|
|
.date-picker-bar {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
background-color: #f0f4ff;
|
padding: 16rpx 24rpx;
|
border-radius: 8rpx;
|
|
.date-display {
|
display: flex;
|
align-items: center;
|
gap: 12rpx;
|
|
.date-text {
|
font-size: 28rpx;
|
color: #2979ff;
|
font-weight: bold;
|
}
|
}
|
}
|
}
|
|
.section-header {
|
padding: 20rpx 30rpx;
|
|
.section-title {
|
font-size: 30rpx;
|
font-weight: bold;
|
color: #333;
|
border-left: 8rpx solid #2979ff;
|
padding-left: 16rpx;
|
}
|
|
&.back-bar {
|
display: flex;
|
align-items: center;
|
gap: 10rpx;
|
background-color: #ffffff;
|
margin-bottom: 20rpx;
|
|
.back-text {
|
font-size: 28rpx;
|
color: #2979ff;
|
}
|
}
|
}
|
|
.ledger-list {
|
padding: 0 20rpx;
|
|
.ledger-item {
|
background-color: #ffffff;
|
border-radius: 16rpx;
|
padding: 24rpx;
|
margin-bottom: 20rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
&:active {
|
background-color: #f9f9f9;
|
}
|
|
&.no-click:active {
|
background-color: #ffffff;
|
}
|
|
.item-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 16rpx;
|
|
.item-left {
|
display: flex;
|
align-items: center;
|
gap: 12rpx;
|
|
.user-icon,
|
.product-icon {
|
width: 48rpx;
|
height: 48rpx;
|
background: linear-gradient(135deg, #2979ff, #64a1ff);
|
border-radius: 8rpx;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.item-id {
|
font-size: 28rpx;
|
font-weight: bold;
|
color: #333;
|
}
|
}
|
|
.item-tag {
|
background-color: #f0f4ff;
|
padding: 4rpx 12rpx;
|
border-radius: 4rpx;
|
|
.tag-text {
|
font-size: 24rpx;
|
color: #2979ff;
|
}
|
}
|
}
|
|
.item-details {
|
padding-top: 10rpx;
|
|
.detail-grid {
|
display: flex;
|
justify-content: space-between;
|
|
.grid-item {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
flex: 1;
|
|
.grid-label {
|
font-size: 24rpx;
|
color: #999;
|
margin-bottom: 8rpx;
|
}
|
|
.grid-value {
|
font-size: 28rpx;
|
color: #333;
|
font-weight: 500;
|
|
&.highlight {
|
color: #ff5a5f;
|
}
|
}
|
}
|
}
|
|
.detail-row {
|
display: flex;
|
justify-content: space-between;
|
margin-bottom: 12rpx;
|
|
.detail-label {
|
font-size: 26rpx;
|
color: #999;
|
}
|
|
.detail-value {
|
font-size: 26rpx;
|
color: #333;
|
|
&.highlight {
|
color: #ff5a5f;
|
font-weight: bold;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
.no-data {
|
padding-top: 100rpx;
|
}
|
}
|
</style>
|