| | |
| | | }); |
| | | } |
| | | |
| | | export function staffSalaryMainExportWorkHours(params) { |
| | | return request({ |
| | | url: "/staffSalaryMain/exportWorkHours", |
| | | method: "post", |
| | | params, |
| | | responseType: "blob", |
| | | }); |
| | | } |
| | | |
| | | export function staffSalaryMainDelete(ids) { |
| | | return request({ |
| | | url: "/staffSalaryMain/delete", |
| | |
| | | method: "get", |
| | | params: { salaryMonth }, |
| | | }); |
| | | } |
| | | } |
| | |
| | | |
| | | <!-- 报工审批详情 --> |
| | | <template v-else-if="row.businessType === 19"> |
| | | <div v-if="formResolved.fields.length" class="report-detail"> |
| | | <div v-if="formResolved.fields.length || reportParamList.length" class="report-detail"> |
| | | <el-divider content-position="left">报工填报内容</el-divider> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions v-if="formResolved.fields.length" :column="2" border> |
| | | <el-descriptions-item |
| | | v-for="field in formResolved.fields" |
| | | :key="field.key" |
| | |
| | | {{ formatFieldDisplayValue(field, formResolved.formPayload?.[field.key]) }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <div v-if="reportParamList.length" class="report-param-block"> |
| | | <el-divider content-position="left">报工参数</el-divider> |
| | | <el-table :data="reportParamList" border size="small" style="width: 100%"> |
| | | <el-table-column label="参数名称" prop="paramName" min-width="180" show-overflow-tooltip /> |
| | | <el-table-column label="参数值" prop="inputValue" min-width="180" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.inputValue || '—' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="单位" prop="unit" min-width="100" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.unit || '—' }} |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | <el-empty v-else description="暂无报工填报内容" /> |
| | | </template> |
| | |
| | | return props.detailData || {}; |
| | | }); |
| | | |
| | | const reportParamList = computed(() => { |
| | | const payload = formResolved.value?.formPayload || {}; |
| | | const sources = [ |
| | | payload.productionOperationParamList, |
| | | payload.reportParamList, |
| | | payload.paramList, |
| | | props.row?.productionOperationParamList, |
| | | props.row?.reportParamList, |
| | | props.row?.paramList, |
| | | ]; |
| | | const list = sources.find(item => Array.isArray(item) && item.length) || []; |
| | | return list.map((param, index) => ({ |
| | | id: param?.id ?? index, |
| | | paramName: param?.paramName || param?.parameterName || param?.name || `参数${index + 1}`, |
| | | inputValue: param?.inputValue ?? param?.value ?? param?.paramValue ?? '', |
| | | unit: param?.unit || '', |
| | | })); |
| | | }); |
| | | |
| | | const attachmentList = computed(() => { |
| | | const list = props.row.storageBlobVOList || props.row.storageBlobDTOs || []; |
| | | return Array.isArray(list) ? list : []; |
| | |
| | | color: var(--el-color-danger); |
| | | } |
| | | |
| | | .report-param-block { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="24"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="全勤奖金" prop="attendanceBonus"> |
| | | <el-input-number |
| | | v-model="form.attendanceBonus" |
| | | :min="0" |
| | | :max="999999" |
| | | :precision="2" |
| | | :step="10" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | </template> |
| | | |
| | |
| | | margin-right: 4px; |
| | | } |
| | | </style> |
| | | |
| | |
| | | contactAddress: "", |
| | | }); |
| | | |
| | | const createDefaultForm = () => ({ |
| | | const createDefaultForm = () => ({ |
| | | id: undefined, |
| | | // 基本信息 |
| | | staffNo: "", |
| | |
| | | contractEndTime: "", |
| | | proTerm: undefined, |
| | | positiveDate: "", |
| | | sysDeptId: undefined, |
| | | sysPostId: undefined, |
| | | basicSalary: undefined, |
| | | // 银行卡信息 |
| | | bankName: "", |
| | | sysDeptId: undefined, |
| | | sysPostId: undefined, |
| | | basicSalary: undefined, |
| | | attendanceBonus: 100, |
| | | // 银行卡信息 |
| | | bankName: "", |
| | | bankCardNo: "", |
| | | // 教育经历 |
| | | staffEducationList: [createEmptyEducation()], |
| | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | @input="handleSalaryInput(row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="全勤奖" |
| | | minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.attendanceBonus" |
| | | type="number" |
| | | placeholder="100" |
| | | size="small" |
| | | disabled /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="月时间合计" |
| | | minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.monthTimeTotal" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="月标准工时" |
| | | minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.standardHours" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="折合倍数" |
| | | minWidth="100"> |
| | | <template #default="{ row }"> |
| | | <el-input v-model.number="row.conversionRatio" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="计件工资" |
| | | minWidth="110"> |
| | | <template #default="{ row }"> |
| | |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.pieceSalary = parseNum(row.pieceSalary)" /> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.hourlySalary = parseNum(row.hourlySalary)" /> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | disabled |
| | | @input="row.otherDeduct = parseNum(row.otherDeduct)" /> |
| | | @input="handleSalaryInput(row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="社保补缴" |
| | |
| | | postName: "", |
| | | deptName: "", |
| | | basicSalary: 0, |
| | | attendanceBonus: 100, |
| | | pieceSalary: 0, |
| | | hourlySalary: 0, |
| | | actualOutput: 0, |
| | | standardQuantity: 0, |
| | | conversionFactor: 1, |
| | | convertedWorkHours: 0, |
| | | monthTimeTotal: 0, |
| | | standardHours: 0, |
| | | conversionRatio: 0, |
| | | otherIncome: 0, |
| | | socialPersonal: 0, |
| | | fundPersonal: 0, |
| | |
| | | postName: source?.postName ?? fallback.postName ?? "", |
| | | deptName: source?.deptName ?? fallback.deptName ?? "", |
| | | basicSalary: parseNum(source?.basicSalary ?? fallback.basicSalary), |
| | | attendanceBonus: parseNum(source?.attendanceBonus ?? fallback.attendanceBonus ?? 100) || 100, |
| | | pieceSalary: parseNum(source?.pieceSalary ?? fallback.pieceSalary), |
| | | hourlySalary: parseNum(source?.hourlySalary ?? fallback.hourlySalary), |
| | | actualOutput: parseNum(source?.actualOutput ?? fallback.actualOutput), |
| | | standardQuantity: parseNum(source?.standardQuantity ?? fallback.standardQuantity), |
| | | conversionFactor: parseNum(source?.conversionFactor ?? fallback.conversionFactor) || 1, |
| | | convertedWorkHours: parseNum(source?.convertedWorkHours ?? fallback.convertedWorkHours), |
| | | monthTimeTotal: parseNum(source?.monthTimeTotal ?? fallback.monthTimeTotal), |
| | | standardHours: parseNum(source?.standardHours ?? fallback.standardHours), |
| | | conversionRatio: parseNum(source?.conversionRatio ?? fallback.conversionRatio), |
| | | otherIncome: parseNum(source?.otherIncome ?? fallback.otherIncome), |
| | | socialPersonal: parseNum(source?.socialPersonal ?? fallback.socialPersonal), |
| | | fundPersonal: parseNum(source?.fundPersonal ?? fallback.fundPersonal), |
| | |
| | | const version = (recalcVersions.get(key) || 0) + 1; |
| | | recalcVersions.set(key, version); |
| | | clearTimeout(recalcTimers.get(key)); |
| | | const payload = { |
| | | const payload = { |
| | | staffOnJobId: row.staffOnJobId ?? row.id, |
| | | salaryMonth: form.value.salaryMonth, |
| | | staffName: row.staffName ?? "", |
| | | postName: row.postName ?? "", |
| | | deptName: row.deptName ?? "", |
| | | basicSalary: parseNum(row.basicSalary), |
| | | attendanceBonus: parseNum(row.attendanceBonus), |
| | | pieceSalary: parseNum(row.pieceSalary), |
| | | hourlySalary: parseNum(row.hourlySalary), |
| | | actualOutput: parseNum(row.actualOutput), |
| | | standardQuantity: parseNum(row.standardQuantity), |
| | | conversionFactor: parseNum(row.conversionFactor) || 1, |
| | | convertedWorkHours: parseNum(row.convertedWorkHours), |
| | | monthTimeTotal: parseNum(row.monthTimeTotal), |
| | | standardHours: parseNum(row.standardHours), |
| | | conversionRatio: parseNum(row.conversionRatio), |
| | | otherIncome: parseNum(row.otherIncome), |
| | | otherDeduct: parseNum(row.otherDeduct), |
| | | socialSupplementAmount: parseNullableNum(row.socialSupplementAmount), |
| | |
| | | const handleSalaryInput = row => { |
| | | row.basicSalary = parseNum(row.basicSalary); |
| | | row.otherIncome = parseNum(row.otherIncome); |
| | | row.actualOutput = parseNum(row.actualOutput); |
| | | row.standardQuantity = parseNum(row.standardQuantity); |
| | | row.conversionFactor = parseNum(row.conversionFactor) || 1; |
| | | requestEmployeeRecalculate(row); |
| | | }; |
| | | |
| | |
| | | form.value.remark = row.remark ?? ""; |
| | | form.value.payBank = row.payBank ?? ""; |
| | | form.value.auditUserId = row.auditUserId ?? undefined; |
| | | |
| | | // 如果有员工明细数据,直接反显 |
| | | if (row.staffSalaryDetailList && row.staffSalaryDetailList.length > 0) { |
| | | employeeList.value = row.staffSalaryDetailList.map(e => normalizeEmployeeRow(e)); |
| | |
| | | deptName: node.deptName ?? "", |
| | | remark: "", |
| | | socialSupplementAmount: null, |
| | | attendanceBonus: node.attendanceBonus, |
| | | }); |
| | | employeeList.value.push(newRow); |
| | | requestEmployeeRecalculate(newRow); |
| | |
| | | postName: e.postName ?? "", |
| | | deptName: e.deptName ?? "", |
| | | basicSalary: parseNum(e.basicSalary), |
| | | attendanceBonus: parseNum(e.attendanceBonus), |
| | | pieceSalary: parseNum(e.pieceSalary), |
| | | hourlySalary: parseNum(e.hourlySalary), |
| | | actualOutput: parseNum(e.actualOutput), |
| | | standardQuantity: parseNum(e.standardQuantity), |
| | | conversionFactor: parseNum(e.conversionFactor) || 1, |
| | | convertedWorkHours: parseNum(e.convertedWorkHours), |
| | | monthTimeTotal: parseNum(e.monthTimeTotal), |
| | | standardHours: parseNum(e.standardHours), |
| | | conversionRatio: parseNum(e.conversionRatio), |
| | | otherIncome: parseNum(e.otherIncome), |
| | | socialPersonal: parseNum(e.socialPersonal), |
| | | fundPersonal: parseNum(e.fundPersonal), |
| | |
| | | <el-button type="primary" @click="openForm('add')">新建工资表</el-button> |
| | | <el-button @click="handleDelete">删除</el-button> |
| | | <el-button @click="openBankSetting">设置银行</el-button> |
| | | <el-button @click="handleExport">导出</el-button> |
| | | <el-button @click="handleExportSalary">导出工资表</el-button> |
| | | <el-button @click="handleExportWorkHours">导出工时表</el-button> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | const handleExport = () => { |
| | | const handleExportSalary = () => { |
| | | proxy.download( |
| | | "/staffSalaryMain/export", |
| | | { ...searchForm.value }, |
| | | "工资表.xlsx" |
| | | ); |
| | | }; |
| | | |
| | | const handleExportWorkHours = () => { |
| | | proxy.download( |
| | | "/staffSalaryMain/exportWorkHours", |
| | | { salaryMonth: searchForm.value.salaryMonth }, |
| | | "工时表.xlsx" |
| | | ); |
| | | }; |
| | | |
| | |
| | | .table_list { |
| | | margin-top: 20px; |
| | | } |
| | | </style> |
| | | </style> |
| | |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: '请选择工序类型', |
| | | message: '工序仅支持计件', |
| | | } |
| | | ]" |
| | | > |
| | | <el-select v-model="formState.type" placeholder="请选择工序类型"> |
| | | <el-option label="计时" :value="0" /> |
| | | <el-select v-model="formState.type" placeholder="工序仅支持计件" disabled> |
| | | <el-option label="计件" :value="1" /> |
| | | </el-select> |
| | | <div style="margin-top: 6px; color: #909399; font-size: 12px;"> |
| | | 工序仅支持计件 |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="工资定额" prop="salaryQuota"> |
| | | <el-input v-model="formState.salaryQuota" type="number" :step="0.001"> |
| | |
| | | // 响应式数据(替代选项式的 data) |
| | | const formState = ref({ |
| | | name: '', |
| | | type: undefined, |
| | | type: 1, |
| | | remark: '', |
| | | salaryQuota: '', |
| | | isQuality: false, |
| | |
| | | const handleSubmit = () => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | add(formState.value).then(res => { |
| | | formState.value.type = 1; |
| | | add(formState.value).then(res => { |
| | | // 关闭模态框 |
| | | isShow.value = false; |
| | | // 告知父组件已完成 |
| | |
| | | <!-- <div class="process-name">{{ process.name }}</div> --> |
| | | <div class="process-desc">{{ process.remark || '暂无描述' }}</div> |
| | | <div class="process-device">关联设备: {{ (deviceOptions.find(item => item.id === Number(process.deviceLedgerId))?.deviceName) || '未关联' }}</div> |
| | | <div class="process-device">标准数量: {{ process.standardQuantity || 0 }}</div> |
| | | <div class="process-device">折算系数: {{ process.conversionFactor ?? 1 }}</div> |
| | | </div> |
| | | <div class="card-footer"> |
| | | <div class="status-tag"> |
| | |
| | | </el-tag> |
| | | <el-tag v-if="process.type !== null && process.type !== undefined" |
| | | size="small" |
| | | :type="process.type == 1 ? 'primary' : 'success'" |
| | | type="primary" |
| | | style="margin-left: 8px"> |
| | | {{ process.type == 0 ? '计时' : '计件' }} |
| | | 计件 |
| | | </el-tag> |
| | | </div> |
| | | <span class="param-count">工资定额: ¥{{ process.salaryQuota || 0 }}</span> |
| | |
| | | type="number" |
| | | :step="0.001" /> |
| | | </el-form-item> |
| | | <el-form-item label="标准数量" |
| | | prop="standardQuantity"> |
| | | <el-input v-model="processForm.standardQuantity" |
| | | type="number" |
| | | :step="0.001" |
| | | placeholder="请输入工序标准数量" /> |
| | | </el-form-item> |
| | | <el-form-item label="折算系数" |
| | | prop="conversionFactor"> |
| | | <el-input v-model="processForm.conversionFactor" |
| | | type="number" |
| | | :step="0.01" |
| | | placeholder="默认 1" /> |
| | | </el-form-item> |
| | | <el-form-item label="是否质检" |
| | | prop="isQuality"> |
| | | <el-switch v-model="processForm.isQuality" /> |
| | |
| | | </el-form-item> |
| | | <el-form-item label="计费类型" |
| | | prop="type"> |
| | | <el-radio-group v-model="processForm.type"> |
| | | <el-radio :label="0">计时</el-radio> |
| | | <el-radio-group v-model="processForm.type" disabled> |
| | | <el-radio :label="1">计件</el-radio> |
| | | </el-radio-group> |
| | | <div style="margin-top: 6px; color: #909399; font-size: 12px;"> |
| | | 工序仅支持计件 |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="关联设备" |
| | | prop="deviceLedgerId"> |
| | |
| | | no: "", |
| | | name: "", |
| | | salaryQuota: null, |
| | | standardQuantity: null, |
| | | conversionFactor: 1, |
| | | isQuality: false, |
| | | isProduction: false, |
| | | remark: "", |
| | | deviceLedgerId: null, |
| | | type: 0, |
| | | type: 1, |
| | | }); |
| | | const processRules = { |
| | | no: [{ required: true, message: "请输入工序编码", trigger: "blur" }], |
| | |
| | | }, |
| | | }, |
| | | ], |
| | | standardQuantity: [ |
| | | { |
| | | required: false, |
| | | message: "请输入标准数量", |
| | | trigger: "blur", |
| | | validator: (rule, value, callback) => { |
| | | if (value == null || value === "") { |
| | | callback(); |
| | | return; |
| | | } |
| | | if (isNaN(value) || value < 0) { |
| | | callback(new Error("标准数量必须是非负数字")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | conversionFactor: [ |
| | | { |
| | | required: false, |
| | | message: "请输入折算系数", |
| | | trigger: "blur", |
| | | validator: (rule, value, callback) => { |
| | | if (value == null || value === "") { |
| | | callback(); |
| | | return; |
| | | } |
| | | if (isNaN(value) || value <= 0) { |
| | | callback(new Error("折算系数必须是大于 0 的数字")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | deviceLedgerId: [ |
| | | { required: false, message: "请选择设备", trigger: "change" }, |
| | | ], |
| | | type: [{ required: false, message: "请选择计费类型", trigger: "change" }], |
| | | type: [ |
| | | { |
| | | required: true, |
| | | message: "工序仅支持计件", |
| | | trigger: "change", |
| | | validator: (rule, value, callback) => { |
| | | if (value !== 1) { |
| | | callback(new Error("工序仅支持计件")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | // 参数对话框 |
| | |
| | | processForm.no = ""; |
| | | processForm.name = ""; |
| | | processForm.salaryQuota = null; |
| | | processForm.standardQuantity = null; |
| | | processForm.conversionFactor = 1; |
| | | processForm.isQuality = false; |
| | | processForm.isProduction = false; |
| | | processForm.remark = ""; |
| | | processForm.deviceLedgerId = null; |
| | | processForm.type = 0; |
| | | processForm.type = 1; |
| | | processDialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | processForm.no = process.no; |
| | | processForm.name = process.name; |
| | | processForm.salaryQuota = process.salaryQuota; |
| | | processForm.standardQuantity = process.standardQuantity; |
| | | processForm.conversionFactor = process.conversionFactor ?? 1; |
| | | processForm.isQuality = !!process.isQuality; |
| | | processForm.isProduction = !!process.isProduction; |
| | | processForm.remark = process.remark || ""; |