| | |
| | | <template> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="title" |
| | | width="700px" |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="140px" |
| | | label-position="top"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="统计月份" |
| | | prop="payDate"> |
| | | <el-date-picker v-model="form.payDate" |
| | | type="month" |
| | | value-format="YYYY-MM" |
| | | format="YYYY-MM" |
| | | placeholder="请选择月份" |
| | | style="width: 100%" |
| | | :disabled="operationType === 'view'" /> |
| | | <el-dialog |
| | | v-model="dialogVisible" |
| | | :title="operationType === 'add' ? '新建工资表' : '编辑工资表'" |
| | | width="90%" |
| | | :close-on-click-modal="false" |
| | | destroy-on-close |
| | | @close="closeDia" |
| | | > |
| | | <div class="form-dia-body"> |
| | | <!-- 基础资料 --> |
| | | <el-card class="form-card" shadow="never"> |
| | | <template #header> |
| | | <span class="card-title"><span class="card-title-line">|</span> 基础资料</span> |
| | | <el-icon class="card-collapse"><ArrowUp /></el-icon> |
| | | </template> |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-position="top"> |
| | | <el-row :gutter="24"> |
| | | <el-col :span="6"> |
| | | <el-form-item label="工资主题" prop="title"> |
| | | <el-input |
| | | v-model="form.title" |
| | | placeholder="请输入" |
| | | clearable |
| | | maxlength="20" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="姓名" |
| | | prop="staffId"> |
| | | <el-select v-model="form.staffId" |
| | | placeholder="请选择员工" |
| | | <el-col :span="6"> |
| | | <el-form-item label="选择部门" prop="deptId"> |
| | | <el-select |
| | | v-model="form.deptId" |
| | | placeholder="请选择" |
| | | clearable |
| | | style="width: 100%" |
| | | :disabled="operationType === 'view'"> |
| | | <el-option v-for="item in userList" |
| | | :key="item.id" |
| | | :label="item.staffName" |
| | | :value="item.id" /> |
| | | > |
| | | <el-option |
| | | v-for="item in deptOptions" |
| | | :key="item.deptId" |
| | | :label="item.deptName" |
| | | :value="item.deptId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="基本工资" |
| | | prop="basicSalary"> |
| | | <el-input v-model="form.basicSalary" |
| | | type="number" |
| | | placeholder="请输入基本工资" |
| | | :disabled="operationType === 'view'" /> |
| | | <el-col :span="6"> |
| | | <el-form-item label="选择工资月份" prop="payMonth"> |
| | | <el-date-picker |
| | | v-model="form.payMonth" |
| | | type="month" |
| | | value-format="YYYY-MM" |
| | | format="YYYY-MM" |
| | | placeholder="请选择工资月份" |
| | | style="width: 100%" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="计件工资" |
| | | prop="pieceworkSalary"> |
| | | <el-input v-model="form.pieceworkSalary" |
| | | type="number" |
| | | placeholder="请输入计件工资" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="计时工资" |
| | | prop="hourlySalary"> |
| | | <el-input v-model="form.hourlySalary" |
| | | type="number" |
| | | placeholder="请输入计时工资" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="其他收入" |
| | | prop="otherIncome"> |
| | | <el-input v-model="form.otherIncome" |
| | | type="number" |
| | | placeholder="请输入其他收入" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="社保个人" |
| | | prop="socialSecurityIndividuals"> |
| | | <el-input v-model="form.socialSecurityIndividuals" |
| | | type="number" |
| | | placeholder="请输入社保个人" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="公积金个人" |
| | | prop="providentFundIndividuals"> |
| | | <el-input v-model="form.providentFundIndividuals" |
| | | type="number" |
| | | placeholder="请输入公积金个人" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="个人所得税" |
| | | prop="personalIncomeTax"> |
| | | <el-input v-model="form.personalIncomeTax" |
| | | type="number" |
| | | placeholder="请输入个人所得税" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="其他扣款" |
| | | prop="otherDeductions"> |
| | | <el-input v-model="form.otherDeductions" |
| | | type="number" |
| | | placeholder="请输入其他扣款" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | placeholder="请输入备注" |
| | | :rows="3" |
| | | :disabled="operationType === 'view'" /> |
| | | <el-col :span="6"> |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm" |
| | | v-if="operationType !== 'view'"> |
| | | 确定 |
| | | </el-button> |
| | | </span> |
| | | </el-card> |
| | | |
| | | <!-- 操作按钮 --> |
| | | <div class="toolbar"> |
| | | <el-button type="primary" @click="handleGenerate">生成工资表</el-button> |
| | | <el-button @click="handleExport">导出</el-button> |
| | | <el-button @click="handleImport">导入</el-button> |
| | | <el-button @click="handleClear">清空</el-button> |
| | | <el-button @click="openAddPerson">新增人员</el-button> |
| | | <el-button @click="handleBatchDelete">删除</el-button> |
| | | <el-button @click="handleTaxForm">个税表</el-button> |
| | | </div> |
| | | |
| | | <!-- 员工工资详情表格 --> |
| | | <div class="employee-table-wrap"> |
| | | <el-table |
| | | ref="employeeTableRef" |
| | | :data="employeeList" |
| | | border |
| | | max-height="400" |
| | | @selection-change="onEmployeeSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | <el-table-column label="员工姓名" prop="staffName" minWidth="100" /> |
| | | <el-table-column label="角色" prop="roleName" minWidth="100" /> |
| | | <el-table-column label="部门" prop="deptName" minWidth="100" /> |
| | | <el-table-column label="基本工资" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.basicSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.basicSalary = parseNum(row.basicSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="计件工资" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.pieceworkSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.pieceworkSalary = parseNum(row.pieceworkSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="计时工资" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.hourlySalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.hourlySalary = parseNum(row.hourlySalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="其他收入" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.otherIncome" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.otherIncome = parseNum(row.otherIncome)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="社保个人" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.socialSecurityIndividuals" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.socialSecurityIndividuals = parseNum(row.socialSecurityIndividuals)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="公积金个人" minWidth="120"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.providentFundIndividuals" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.providentFundIndividuals = parseNum(row.providentFundIndividuals)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="80" align="center" fixed="right"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" link @click="removeEmployee(row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div v-if="!employeeList.length" class="table-empty">暂无数据</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDia">取消</el-button> |
| | | <el-button type="primary" @click="submitForm">确定</el-button> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- 新增人员弹窗 --> |
| | | <el-dialog |
| | | v-model="addPersonVisible" |
| | | title="新增人员" |
| | | width="400px" |
| | | append-to-body |
| | | @close="addPersonClose" |
| | | > |
| | | <div class="add-person-tree"> |
| | | <el-tree |
| | | ref="personTreeRef" |
| | | :data="deptStaffTree" |
| | | show-checkbox |
| | | node-key="id" |
| | | :props="{ label: 'label', children: 'children' }" |
| | | default-expand-all |
| | | /> |
| | | </div> |
| | | <template #footer> |
| | | <el-button @click="addPersonVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="confirmAddPerson">确定</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 个税表弹窗 --> |
| | | <el-dialog |
| | | v-model="taxDialogVisible" |
| | | title="个税表" |
| | | width="700px" |
| | | append-to-body |
| | | > |
| | | <div class="tax-desc">个人所得税免征额:5000元</div> |
| | | <el-table :data="taxTableData" border style="width: 100%;margin-bottom: 20px;"> |
| | | <el-table-column prop="level" label="级数" width="80" align="center" /> |
| | | <el-table-column |
| | | prop="range" |
| | | label="全年应纳税所得额/元" |
| | | min-width="220" |
| | | /> |
| | | <el-table-column |
| | | prop="rate" |
| | | label="税率(%)" |
| | | width="100" |
| | | align="center" |
| | | /> |
| | | <el-table-column |
| | | prop="quickDeduction" |
| | | label="速算扣除数/元" |
| | | width="160" |
| | | align="center" |
| | | /> |
| | | </el-table> |
| | | </el-dialog> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { ref, reactive, toRefs, computed, getCurrentInstance, nextTick } from "vue"; |
| | | import { ArrowUp } from "@element-plus/icons-vue"; |
| | | import { listDept } from "@/api/system/dept.js"; |
| | | import { staffOnJobList } from "@/api/personnelManagement/monthlyStatistics.js"; |
| | | import { |
| | | monthlyStatisticsAdd, |
| | | monthlyStatisticsUpdate, |
| | | staffOnJobList, |
| | | monthlyStatisticsGet, |
| | | } from "@/api/personnelManagement/monthlyStatistics.js"; |
| | | |
| | | const emit = defineEmits(["update:modelValue", "close"]); |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | operationType: { |
| | | type: String, |
| | | default: "add", |
| | | }, |
| | | row: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | modelValue: { type: Boolean, default: false }, |
| | | operationType: { type: String, default: "add" }, |
| | | row: { type: Object, default: () => ({}) }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:modelValue", "close"]); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const dialogVisible = computed({ |
| | | get: () => props.modelValue, |
| | | set: val => emit("update:modelValue", val), |
| | | set: (val) => emit("update:modelValue", val), |
| | | }); |
| | | |
| | | const title = computed(() => { |
| | | if (props.operationType === "add") return "新增薪资台账"; |
| | | if (props.operationType === "edit") return "编辑薪资台账"; |
| | | return "查看薪资台账"; |
| | | }); |
| | | const formRef = ref(null); |
| | | const employeeTableRef = ref(null); |
| | | const personTreeRef = ref(null); |
| | | const addPersonVisible = ref(false); |
| | | const taxDialogVisible = ref(false); |
| | | const deptOptions = ref([]); |
| | | const deptStaffTree = ref([]); |
| | | const employeeList = ref([]); |
| | | const selectedEmployees = ref([]); |
| | | const taxTableData = ref([ |
| | | { level: 1, range: "不超过36000元", rate: 3, quickDeduction: 0 }, |
| | | { level: 2, range: "超过36000-144000元", rate: 10, quickDeduction: 2520 }, |
| | | { level: 3, range: "超过144000-300000元", rate: 20, quickDeduction: 16920 }, |
| | | { level: 4, range: "超过300000-420000元", rate: 25, quickDeduction: 31920 }, |
| | | { level: 5, range: "超过420000-660000元", rate: 30, quickDeduction: 52920 }, |
| | | { level: 6, range: "超过660000-960000元", rate: 35, quickDeduction: 85920 }, |
| | | { level: 7, range: "超过960000元", rate: 45, quickDeduction: 181920 }, |
| | | ]); |
| | | |
| | | const formRef = ref(); |
| | | const form = reactive({ |
| | | id: "", |
| | | payDate: "", |
| | | staffId: "", |
| | | basicSalary: 0, |
| | | pieceworkSalary: 0, |
| | | hourlySalary: 0, |
| | | otherIncome: 0, |
| | | socialSecurityIndividuals: 0, |
| | | providentFundIndividuals: 0, |
| | | personalIncomeTax: 0, |
| | | otherDeductions: 0, |
| | | payableWages: 0, |
| | | deductibleWages: 0, |
| | | actualWages: 0, |
| | | function parseNum(v) { |
| | | if (v === "" || v == null) return 0; |
| | | const n = Number(v); |
| | | return isNaN(n) ? 0 : n; |
| | | } |
| | | |
| | | // 基础资料表单 |
| | | const data = reactive({ |
| | | form: { |
| | | id: undefined, |
| | | title: "", |
| | | deptId: undefined, |
| | | payMonth: "", |
| | | remark: "", |
| | | }, |
| | | rules: { |
| | | title: [{ required: true, message: "请输入工资主题", trigger: "blur" }], |
| | | deptId: [{ required: true, message: "请选择部门", trigger: "change" }], |
| | | payMonth: [{ required: true, message: "请选择工资月份", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | |
| | | const rules = { |
| | | payDate: [{ required: true, message: "请选择统计月份", trigger: "change" }], |
| | | staffId: [{ required: true, message: "请选择员工", trigger: "change" }], |
| | | basicSalary: [{ required: true, message: "请输入基本工资", trigger: "blur" }], |
| | | // 扁平化部门树供下拉使用 |
| | | function flattenDept(tree, list = []) { |
| | | if (!tree?.length) return list; |
| | | tree.forEach((node) => { |
| | | list.push({ deptId: node.deptId, deptName: node.deptName }); |
| | | if (node.children?.length) flattenDept(node.children, list); |
| | | }); |
| | | return list; |
| | | } |
| | | |
| | | const loadDeptOptions = () => { |
| | | listDept().then((res) => { |
| | | const tree = res.data ?? []; |
| | | deptOptions.value = flattenDept(tree); |
| | | }); |
| | | }; |
| | | |
| | | const userList = ref([]); |
| | | |
| | | const loadUserList = () => { |
| | | // userListNoPage().then(res => { |
| | | // userList.value = res.data || []; |
| | | // }); |
| | | staffOnJobList().then(res => { |
| | | userList.value = res.data || []; |
| | | // 构建 部门-人员 树(用于新增人员弹窗) |
| | | const loadDeptStaffTree = () => { |
| | | Promise.all([listDept(), staffOnJobList()]).then(([deptRes, staffRes]) => { |
| | | const tree = deptRes.data ?? []; |
| | | const staffList = staffRes.data ?? []; |
| | | const deptMap = new Map(); |
| | | function walk(nodes) { |
| | | nodes.forEach((node) => { |
| | | deptMap.set(node.deptId, { |
| | | id: "dept_" + node.deptId, |
| | | deptId: node.deptId, |
| | | label: node.deptName, |
| | | type: "dept", |
| | | children: [], |
| | | }); |
| | | if (node.children?.length) walk(node.children); |
| | | }); |
| | | } |
| | | walk(tree); |
| | | staffList.forEach((s) => { |
| | | const deptId = s.deptId ?? s.dept_id; |
| | | const node = deptMap.get(deptId); |
| | | if (node) { |
| | | node.children.push({ |
| | | id: s.id ?? s.staffId, |
| | | staffId: s.id ?? s.staffId, |
| | | label: s.staffName ?? s.name, |
| | | type: "staff", |
| | | ...s, |
| | | }); |
| | | } |
| | | }); |
| | | deptStaffTree.value = Array.from(deptMap.values()).filter( |
| | | (n) => n.children && n.children.length > 0 |
| | | ); |
| | | }); |
| | | }; |
| | | |
| | | const openDialog = (type, row) => { |
| | | // 重置表单 |
| | | Object.assign(form, { |
| | | id: "", |
| | | payDate: "", |
| | | staffId: "", |
| | | nextTick(() => { |
| | | loadDeptOptions(); |
| | | employeeList.value = []; |
| | | Object.assign(form.value, { |
| | | id: undefined, |
| | | title: "", |
| | | deptId: undefined, |
| | | payMonth: "", |
| | | remark: "", |
| | | }); |
| | | if (type === "edit" && row?.id) { |
| | | monthlyStatisticsGet(row.id).then((res) => { |
| | | const d = res.data || {}; |
| | | form.value.id = d.id; |
| | | form.value.title = d.title ?? d.payDateStr ?? ""; |
| | | form.value.deptId = d.deptId; |
| | | form.value.payMonth = d.payMonth ?? d.payDate ?? d.payDateStr ?? ""; |
| | | form.value.remark = d.remark ?? ""; |
| | | employeeList.value = (d.detailList || d.employeeList || []).map((e) => ({ |
| | | ...e, |
| | | basicSalary: parseNum(e.basicSalary), |
| | | pieceworkSalary: parseNum(e.pieceworkSalary), |
| | | hourlySalary: parseNum(e.hourlySalary), |
| | | otherIncome: parseNum(e.otherIncome), |
| | | socialSecurityIndividuals: parseNum(e.socialSecurityIndividuals), |
| | | providentFundIndividuals: parseNum(e.providentFundIndividuals), |
| | | })); |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const openAddPerson = () => { |
| | | loadDeptStaffTree(); |
| | | addPersonVisible.value = true; |
| | | nextTick(() => { |
| | | personTreeRef.value?.setCheckedKeys([]); |
| | | }); |
| | | }; |
| | | |
| | | const addPersonClose = () => {}; |
| | | |
| | | const confirmAddPerson = () => { |
| | | const tree = personTreeRef.value; |
| | | if (!tree) { |
| | | addPersonVisible.value = false; |
| | | return; |
| | | } |
| | | const checked = tree.getCheckedNodes(); |
| | | const staffNodes = checked.filter((n) => n.type === "staff"); |
| | | const existIds = new Set(employeeList.value.map((e) => e.staffId || e.id)); |
| | | staffNodes.forEach((node) => { |
| | | const id = node.staffId ?? node.id; |
| | | if (existIds.has(id)) return; |
| | | existIds.add(id); |
| | | employeeList.value.push({ |
| | | staffId: id, |
| | | id: id, |
| | | staffName: node.label, |
| | | roleName: node.roleName ?? node.role ?? "", |
| | | deptName: node.deptName ?? "", |
| | | basicSalary: 0, |
| | | pieceworkSalary: 0, |
| | | hourlySalary: 0, |
| | | otherIncome: 0, |
| | | socialSecurityIndividuals: 0, |
| | | providentFundIndividuals: 0, |
| | | personalIncomeTax: 0, |
| | | otherDeductions: 0, |
| | | payableWages: 0, |
| | | deductibleWages: 0, |
| | | actualWages: 0, |
| | | remark: "", |
| | | }); |
| | | }); |
| | | addPersonVisible.value = false; |
| | | }; |
| | | |
| | | if (type === "add") { |
| | | dialogVisible.value = true; |
| | | } else if (type === "edit" || type === "view") { |
| | | if (row && row.id) { |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | const removeEmployee = (row) => { |
| | | employeeList.value = employeeList.value.filter( |
| | | (e) => (e.staffId || e.id) !== (row.staffId || row.id) |
| | | ); |
| | | }; |
| | | |
| | | const onEmployeeSelectionChange = (selection) => { |
| | | selectedEmployees.value = selection; |
| | | }; |
| | | |
| | | const handleBatchDelete = () => { |
| | | if (!selectedEmployees.value?.length) { |
| | | proxy.$modal.msgWarning("请先勾选要删除的员工"); |
| | | return; |
| | | } |
| | | } |
| | | const ids = new Set(selectedEmployees.value.map((e) => e.staffId || e.id)); |
| | | employeeList.value = employeeList.value.filter( |
| | | (e) => !ids.has(e.staffId || e.id) |
| | | ); |
| | | }; |
| | | |
| | | const handleGenerate = () => { |
| | | proxy.$modal.msgInfo("生成工资表功能需对接后端"); |
| | | }; |
| | | |
| | | const handleExport = () => { |
| | | proxy.$modal.msgInfo("导出功能需对接后端"); |
| | | }; |
| | | |
| | | const handleImport = () => { |
| | | proxy.$modal.msgInfo("导入功能需对接后端"); |
| | | }; |
| | | |
| | | const handleClear = () => { |
| | | proxy.$modal.confirm("确定清空当前员工列表吗?").then(() => { |
| | | employeeList.value = []; |
| | | }).catch(() => {}); |
| | | }; |
| | | |
| | | const handleTaxForm = () => { |
| | | taxDialogVisible.value = true; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | form.basicSalary = Number(form.basicSalary); |
| | | form.pieceworkSalary = Number(form.pieceworkSalary); |
| | | form.hourlySalary = Number(form.hourlySalary); |
| | | form.otherIncome = Number(form.otherIncome); |
| | | form.socialSecurityIndividuals = Number(form.socialSecurityIndividuals); |
| | | form.providentFundIndividuals = Number(form.providentFundIndividuals); |
| | | form.personalIncomeTax = Number(form.personalIncomeTax); |
| | | form.otherDeductions = Number(form.otherDeductions); |
| | | |
| | | // 计算应发工资、应扣工资和实发工资 |
| | | const payableWages = |
| | | form.basicSalary + |
| | | form.pieceworkSalary + |
| | | form.hourlySalary + |
| | | form.otherIncome; |
| | | const deductibleWages = |
| | | form.socialSecurityIndividuals + |
| | | form.providentFundIndividuals + |
| | | form.personalIncomeTax + |
| | | form.otherDeductions; |
| | | const actualWages = payableWages - deductibleWages; |
| | | |
| | | const submitData = { |
| | | ...form, |
| | | payableWages, |
| | | deductibleWages, |
| | | actualWages, |
| | | formRef.value?.validate((valid) => { |
| | | if (!valid) return; |
| | | const payload = { |
| | | ...form.value, |
| | | detailList: employeeList.value.map((e) => ({ |
| | | staffId: e.staffId ?? e.id, |
| | | staffName: e.staffName, |
| | | basicSalary: parseNum(e.basicSalary), |
| | | pieceworkSalary: parseNum(e.pieceworkSalary), |
| | | hourlySalary: parseNum(e.hourlySalary), |
| | | otherIncome: parseNum(e.otherIncome), |
| | | socialSecurityIndividuals: parseNum(e.socialSecurityIndividuals), |
| | | providentFundIndividuals: parseNum(e.providentFundIndividuals), |
| | | })), |
| | | }; |
| | | |
| | | if (props.operationType === "add") { |
| | | monthlyStatisticsAdd(submitData).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("新增成功"); |
| | | dialogVisible.value = false; |
| | | emit("close"); |
| | | } else { |
| | | ElMessage.error(res.msg || "新增失败"); |
| | | } |
| | | monthlyStatisticsAdd(payload).then(() => { |
| | | proxy.$modal.msgSuccess("新增成功"); |
| | | closeDia(); |
| | | }); |
| | | } else if (props.operationType === "edit") { |
| | | monthlyStatisticsUpdate(submitData).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("更新成功"); |
| | | dialogVisible.value = false; |
| | | emit("close"); |
| | | } else { |
| | | ElMessage.error(res.msg || "更新失败"); |
| | | } |
| | | monthlyStatisticsUpdate(payload).then(() => { |
| | | proxy.$modal.msgSuccess("修改成功"); |
| | | closeDia(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | loadUserList(); |
| | | }); |
| | | const closeDia = () => { |
| | | dialogVisible.value = false; |
| | | emit("close"); |
| | | }; |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | defineExpose({ openDialog }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .form-dia-body { |
| | | padding: 0; |
| | | } |
| | | .card-title-line { |
| | | color: #f56c6c; |
| | | margin-right: 4px; |
| | | } |
| | | .form-card { |
| | | margin-bottom: 16px; |
| | | } |
| | | .form-card :deep(.el-card__header) { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 12px 16px; |
| | | } |
| | | .card-title { |
| | | font-weight: 500; |
| | | } |
| | | .card-collapse { |
| | | color: #999; |
| | | cursor: pointer; |
| | | } |
| | | .toolbar { |
| | | margin-bottom: 16px; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | } |
| | | .employee-table-wrap { |
| | | position: relative; |
| | | min-height: 120px; |
| | | } |
| | | .table-empty { |
| | | text-align: center; |
| | | padding: 24px; |
| | | color: #999; |
| | | font-size: 14px; |
| | | } |
| | | .add-person-tree { |
| | | max-height: 360px; |
| | | overflow-y: auto; |
| | | padding: 8px 0; |
| | | } |
| | | .tax-desc { |
| | | margin-bottom: 12px; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |