| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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.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">å·¥æ¶å®é¢</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> |