This commit is contained in:
磷叶 2025-01-02 09:44:55 +08:00
parent d520288bca
commit 92d22a29b6
5 changed files with 427 additions and 5 deletions

View File

@ -4,7 +4,9 @@
<i class="el-icon-arrow-left"/>
</div>
<div class="title">
{{title}}
<slot name="title">
{{title}}
</slot>
</div>
<div class="content">
<slot/>

View File

@ -120,6 +120,11 @@
:user-query="userQuery"
/>
<user-prod-preview
:show.sync="showUserProdPreview"
:data="currentPreviewData"
/>
</div>
</template>
@ -140,6 +145,7 @@ import HoverShow from "@/components/HoverShow/index.vue";
import ReportProductRowEdit from "@/views/yh/report/edit/components/ReportProductRowEdit.vue";
import PriceDrawer from "@/components/Business/Price/PriceDrawer.vue";
import {calcTotalAmount} from "@/views/yh/report/utils";
import UserProdPreview from './UserProdPreview.vue'
export default {
name: "ReportProductEdit",
@ -151,7 +157,7 @@ export default {
Dict,
ReportProductUserListEdit,
ReportProductOrderListEdit,
UserProductBatchDialog, FormCol, PriceInput, PriceDialog, UserInput},
UserProductBatchDialog, FormCol, PriceInput, PriceDialog, UserInput, UserProdPreview},
props: {
value: {
type: Array,
@ -226,6 +232,8 @@ export default {
showMore: false,
editingIndex: -1, //
isAllSelected: false,
showUserProdPreview: false,
currentPreviewData: null,
}
},
watch: {
@ -418,6 +426,10 @@ export default {
item.selected = !item.selected;
this.handleSelect(item, item.selected);
},
handlePreview(row) {
this.currentPreviewData = row;
this.showUserProdPreview = true;
},
}
}
</script>
@ -496,6 +508,20 @@ export default {
margin-left: 0;
}
.preview-icon {
color: #909399;
cursor: pointer;
padding: 4px;
font-size: 16px;
border-radius: 4px;
transition: all 0.2s;
&:hover {
color: #409EFF;
background-color: rgba(64, 158, 255, 0.1);
}
}
.delete-icon {
color: #909399;
cursor: pointer;
@ -618,5 +644,19 @@ export default {
}
}
.preview-icon {
color: #909399;
cursor: pointer;
padding: 4px;
font-size: 16px;
border-radius: 4px;
transition: all 0.2s;
&:hover {
color: #409EFF;
background-color: rgba(64, 158, 255, 0.1);
}
}
</style>

View File

@ -0,0 +1,345 @@
<template>
<el-dialog
title="员工产量明细"
:visible.sync="visible"
width="90%"
close-on-click-modal
append-to-body
>
<div class="preview-container" v-if="data != null">
<!-- 汇总信息 -->
<div class="summary-info">
<div class="info-item">
<span class="label">日期</span>
<span class="value">{{ data.reportDate | dv}}</span>
</div>
<div class="info-item">
<span class="label">总金额</span>
<span class="value highlight">{{ totalAmount | dv}} </span>
</div>
</div>
<!-- 透视表 -->
<el-table
v-if="tableData.length"
:data="tableData"
style="width: 100%"
size="small"
border
:header-cell-style="{ background: '#f5f7fa' }"
:cell-style="{ padding: '8px 4px' }"
>
<!-- 员工列 -->
<el-table-column
prop="userName"
label="员工"
fixed="left"
min-width="100"
align="center"
>
<template slot-scope="scope">
<span class="user-name">{{ scope.row.userName }}</span>
</template>
</el-table-column>
<!-- 动态生成工序列 -->
<el-table-column
v-for="process in processList"
:key="process.priceId"
:label="process.priceName"
align="center"
min-width="200"
>
<template slot="header" slot-scope="scope">
<div class="process-header">
<div class="process-title">
<div class="process-name" :title="process.priceName">{{ process.priceName }}</div>
<div class="process-code" :title="process.priceCode">{{ process.priceCode }}</div>
</div>
<div class="process-info">
<span class="info-item">
<i class="el-icon-box"></i>
<span>{{ process.num }}{{ process.priceUnit }}</span>
</span>
<span class="info-item">
<i class="el-icon-money"></i>
<span>{{ process.pricePrice }}/{{ process.priceUnit }}</span>
</span>
</div>
</div>
</template>
<template slot-scope="scope">
<template v-if="scope.row.products[process.priceId]">
<div class="prod-cell">
<div class="prod-num">
<span class="num">{{ scope.row.products[process.priceId].num }}</span>
<span class="unit">{{ process.priceUnit }}</span>
</div>
<div class="prod-amount">
<span class="amount">{{ calculateIncome(scope.row.products[process.priceId].num, process.pricePrice) }}</span>
<span class="unit"></span>
</div>
</div>
</template>
<span v-else>-</span>
</template>
</el-table-column>
<!-- 合计列 -->
<el-table-column
label="合计金额"
fixed="right"
min-width="120"
align="center"
class-name="total-column"
>
<template slot-scope="scope">
<span class="total-income">{{ scope.row.totalAmount }}</span>
</template>
</el-table-column>
</el-table>
<!-- 添加空状态 -->
<el-empty v-else description="暂无数据" />
</div>
<div slot="footer">
<el-button @click="visible = false"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { notNullDecimal } from "@/utils";
import Decimal from "decimal.js";
export default {
name: "UserProdPreview",
props: {
show: {
type: Boolean,
default: false
},
data: {
type: Object,
default: () => ({})
}
},
data() {
return {
visible: false
}
},
computed: {
//
processList() {
return (this.data?.productList || []).filter(item =>
item && item.userProdList && item.userProdList.length > 0
);
},
//
tableData() {
if (!this.data || !this.data.productList) {
return [];
}
const userMap = new Map();
//
this.processList.forEach(process => {
if (!process.userProdList) return;
//
process.userProdList.forEach(userProd => {
if (!userProd || !userProd.userId) {
return;
}
if (!userMap.has(userProd.userId)) {
userMap.set(userProd.userId, {
userId: userProd.userId,
userName: userProd.userName || '',
products: {},
totalAmount: 0
});
}
const userData = userMap.get(userProd.userId);
userData.products[process.priceId] = {
num: userProd.num || 0,
amount: this.calculateIncome(userProd.num, process.pricePrice)
};
userData.totalAmount = new Decimal(userData.totalAmount)
.plus(userData.products[process.priceId].amount)
.toNumber();
});
});
return Array.from(userMap.values());
},
//
totalAmount() {
if (!this.tableData.length) return '0.00';
return this.tableData.reduce((sum, row) => {
return new Decimal(sum).plus(row.totalAmount || 0).toNumber();
}, 0).toFixed(2);
}
},
watch: {
show: {
handler(val) {
this.visible = val;
},
immediate: true
},
visible(val) {
if (!val) {
this.$emit('update:show', false);
}
}
},
methods: {
//
calculateIncome(num, price) {
if (!num || !price) return '0.00';
return new Decimal(num || 0)
.mul(notNullDecimal(price || 0))
.toFixed(2);
}
}
}
</script>
<style lang="scss" scoped>
.preview-container {
.summary-info {
background-color: #f5f7fa;
border-radius: 4px;
padding: 16px;
margin-bottom: 16px;
display: flex;
gap: 24px;
.info-item {
.label {
color: #606266;
margin-right: 8px;
}
.value {
color: #303133;
font-weight: 500;
&.highlight {
color: #409EFF;
}
}
}
}
:deep(.el-table) {
.process-header {
padding: 4px;
.process-title {
margin-bottom: 4px;
.process-name {
font-weight: 500;
color: #303133;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.process-code {
font-size: 12px;
color: #909399;
margin-top: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.process-info {
display: flex;
justify-content: space-around;
font-size: 12px;
color: #909399;
margin-top: 4px;
padding-top: 4px;
border-top: 1px dashed #ebeef5;
.info-item {
display: flex;
align-items: center;
gap: 4px;
i {
font-size: 14px;
}
}
}
}
.prod-cell {
display: flex;
flex-direction: column;
gap: 4px;
.prod-num {
color: #303133;
font-weight: 500;
.unit {
font-size: 12px;
color: #909399;
margin-left: 2px;
}
}
.prod-amount {
font-size: 12px;
color: #67c23a;
.unit {
margin-left: 2px;
}
}
}
.total-column {
background-color: #fafafa;
}
.total-income {
color: #409EFF;
font-weight: 500;
}
}
}
//
@media screen and (max-width: 1024px) {
.preview-container {
:deep(.el-table) {
.process-header {
.process-title {
.process-name, .process-code {
text-align: center;
}
}
.process-info {
flex-direction: column;
align-items: center;
gap: 4px;
padding-top: 6px;
}
}
}
}
}
</style>

View File

@ -1,6 +1,10 @@
<template>
<div class="report-edit-container" v-loading="loading">
<edit-header class="edit-header" :title="title">
<edit-header class="edit-header">
<template #title>
{{title}}
<el-button type="text" @click="previewUserProd = true" style="margin-left: 16px" icon="el-icon-view" >员工产量汇总</el-button>
</template>
<el-button plain @click="cancel" icon="el-icon-close" size="small">取消</el-button>
<el-button type="primary" plain @click="submitForm(false)" icon="el-icon-check" size="small">仅保存</el-button>
<el-button type="primary" @click="submitForm(true)" icon="el-icon-s-check" size="small">保存并提交</el-button>
@ -54,6 +58,11 @@
:rules="rules"
/>
<!-- 添加员工产量预览组件 -->
<user-prod-preview
:show.sync="previewUserProd"
:data="form"
/>
</div>
</div>
</div>
@ -73,14 +82,24 @@ import {parseTime} from "@/utils/ruoyi";
import {$reportCheck} from "@/views/yh/report/mixins";
import {DatePickerOptions} from "@/utils/constants";
import ReportProductRowEdit from "@/views/yh/report/edit/components/ReportProductRowEdit.vue";
import UserProdPreview from './components/UserProdPreview.vue'
export default {
name: "ReportEdit",
mixins: [$reportCheck],
dicts: ['income_mode'],
components: {ReportProductEdit, EditHeader, DeptTreeSelect, PriceInput, FormCol, ReportProductRowEdit},
components: {
UserProdPreview,
ReportProductEdit,
EditHeader,
DeptTreeSelect,
PriceInput,
FormCol,
ReportProductRowEdit
},
data() {
return {
previewUserProd: false,
row: {},
index: null,
showMore: false,

View File

@ -1,6 +1,19 @@
<template>
<div :class="listConfig.containerClass">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<!-- 报表日期 -->
<el-form-item label="报表日期" prop="reportDateRange">
<el-date-picker
v-model="queryParams.reportDateRange"
type="daterange"
value-format="yyyy-MM-dd"
clearable
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="handleQuery"
/>
</el-form-item>
<el-form-item label="员工名称" prop="userName">
<el-input
v-model="queryParams.userName"
@ -179,10 +192,12 @@ export default {
//
columns: [
{key: 'id', visible: false, label: '明细ID', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
{key: 'reportDate', visible: true, label: '报表日期', minWidth: null, sortable: true, overflow: false, align: 'center', width: "180"},
{key: 'reportDate', visible: true, label: '报表日期', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
{key: 'deptName', visible: true, label: '部门', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'reportStatus', visible: true, label: '报表状态', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'userName', visible: true, label: '员工', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'priceName', visible: true, label: '工序', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'priceCode', visible: true, label: '工序代码', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'pricePrice', visible: true, label: '单价', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'num', visible: true, label: '产量', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
{key: 'totalPrice', visible: true, label: '工资', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
@ -217,6 +232,7 @@ export default {
id: null,
prodId: null,
userId: null,
reportDateRange: []
},
//
form: {},