| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <FormDialog |
| | | v-model="dialogVisible" |
| | | :title="operationType === 'add' ? 'æ°å»ºå·¥èµè¡¨' : 'ç¼è¾å·¥èµè¡¨'" |
| | | width="90%" |
| | | @close="closeDia" |
| | | > |
| | | <template #footer> |
| | | <el-button type="info" @click="saveDraft">ä¿åè稿</el-button> |
| | | <el-button type="primary" @click="submitForm">确认æäº¤</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </template> |
| | | <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="salaryTitle"> |
| | | <el-input |
| | | v-model="form.salaryTitle" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | maxlength="20" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="éæ©é¨é¨" prop="deptIds"> |
| | | <el-select |
| | | v-model="form.deptIds" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | multiple |
| | | collapse-tags-tooltip |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in deptOptions" |
| | | :key="item.deptId" |
| | | :label="item.deptName" |
| | | :value="item.deptId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="éæ©å·¥èµæä»½" prop="salaryMonth"> |
| | | <el-date-picker |
| | | v-model="form.salaryMonth" |
| | | type="month" |
| | | value-format="YYYY-MM" |
| | | format="YYYY-MM" |
| | | placeholder="è¯·éæ©å·¥èµæä»½" |
| | | style="width: 100%" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <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-row :gutter="24"> |
| | | <el-col :span="6"> |
| | | <el-form-item label="æ¯ä»é¶è¡" prop="payBank"> |
| | | <el-select |
| | | v-model="form.payBank" |
| | | placeholder="è¯·éæ©" |
| | | clearable |
| | | filterable |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="b in bankOptions" |
| | | :key="b" |
| | | :label="b" |
| | | :value="b" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="å®¡æ ¸äºº" prop="auditUserId"> |
| | | <el-select |
| | | v-model="form.auditUserId" |
| | | placeholder="è¯·éæ©å®¡æ ¸äºº" |
| | | clearable |
| | | filterable |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in userList" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- æä½æé® --> |
| | | <div class="toolbar"> |
| | | <el-button type="primary" @click="handleGenerate">çæå·¥èµè¡¨</el-button> |
| | | <el-button @click="handleClear">æ¸
空</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="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.pieceSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.pieceSalary = parseNum(row.pieceSalary)" |
| | | /> |
| | | </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.socialPersonal" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.socialPersonal = parseNum(row.socialPersonal)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¬ç§¯é个人" minWidth="120"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.fundPersonal" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.fundPersonal = parseNum(row.fundPersonal)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å
¶ä»æ¯åº" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.otherDeduct" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.otherDeduct = parseNum(row.otherDeduct)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å·¥èµä¸ªç¨" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.salaryTax" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.salaryTax = parseNum(row.salaryTax)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºåå·¥èµ" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.grossSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.grossSalary = parseNum(row.grossSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åºæ£å·¥èµ" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.deductSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.deductSalary = parseNum(row.deductSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å®åå·¥èµ" minWidth="110"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model.number="row.netSalary" |
| | | type="number" |
| | | placeholder="0" |
| | | size="small" |
| | | @input="row.netSalary = parseNum(row.netSalary)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="夿³¨" minWidth="120"> |
| | | <template #default="{ row }"> |
| | | <el-input |
| | | v-model="row.remark" |
| | | placeholder="请è¾å
¥" |
| | | size="small" |
| | | /> |
| | | </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 v-else class="salary-total"> |
| | | <span class="total-label">å·¥èµæ»é¢ï¼</span> |
| | | <span class="total-value">Â¥ {{ totalSalary.toFixed(2) }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | |
| | | |
| | | <!-- æ°å¢äººåå¼¹çª --> |
| | | <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> |
| | | </FormDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, computed, getCurrentInstance, nextTick } from "vue"; |
| | | import { ArrowUp } from "@element-plus/icons-vue"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | import { listDept } from "@/api/system/dept.js"; |
| | | import { staffOnJobList } from "@/api/personnelManagement/monthlyStatistics.js"; |
| | | import { bankList } from "@/api/personnelManagement/bank.js"; |
| | | import { |
| | | staffSalaryMainAdd, |
| | | staffSalaryMainUpdate, |
| | | staffSalaryMainCalculateSalary, |
| | | } from "@/api/personnelManagement/staffSalaryMain.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | |
| | | const emit = defineEmits(["update:modelValue", "close"]); |
| | | const props = defineProps({ |
| | | modelValue: { type: Boolean, default: false }, |
| | | operationType: { type: String, default: "add" }, |
| | | row: { type: Object, default: () => ({}) }, |
| | | }); |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const dialogVisible = computed({ |
| | | get: () => props.modelValue, |
| | | set: (val) => emit("update:modelValue", val), |
| | | }); |
| | | |
| | | 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 bankOptions = ref([]); |
| | | const userList = 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 }, |
| | | ]); |
| | | |
| | | function parseNum(v) { |
| | | if (v === "" || v == null) return 0; |
| | | const n = Number(v); |
| | | return isNaN(n) ? 0 : n; |
| | | } |
| | | |
| | | // åºç¡èµæè¡¨å |
| | | const data = reactive({ |
| | | form: { |
| | | id: undefined, |
| | | salaryTitle: "", |
| | | deptIds: [], |
| | | salaryMonth: "", |
| | | remark: "", |
| | | payBank: "", |
| | | auditUserId: undefined, |
| | | }, |
| | | rules: { |
| | | salaryTitle: [{ required: true, message: "请è¾å
¥å·¥èµä¸»é¢", trigger: "blur" }], |
| | | deptIds: [{ required: true, message: "è¯·éæ©é¨é¨", trigger: "change" }], |
| | | salaryMonth: [{ required: true, message: "è¯·éæ©å·¥èµæä»½", trigger: "change" }], |
| | | auditUserId: [{ required: true, message: "è¯·éæ©å®¡æ ¸äºº", trigger: "change" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | |
| | | // 计ç®å·¥èµæ»é¢ï¼ææåå·¥å®åå·¥èµä¹åï¼ |
| | | const totalSalary = computed(() => { |
| | | return employeeList.value.reduce((sum, e) => sum + parseNum(e.netSalary), 0); |
| | | }); |
| | | |
| | | // æ ¹æ®å®¡æ ¸äººIDè·åå®¡æ ¸äººåç§° |
| | | const auditUserName = computed(() => { |
| | | if (!form.value.auditUserId) return ""; |
| | | const user = userList.value.find(u => u.userId === form.value.auditUserId); |
| | | return user ? user.nickName : ""; |
| | | }); |
| | | |
| | | const loadBankOptions = () => { |
| | | return bankList().then((res) => { |
| | | const list = Array.isArray(res?.data) ? res.data : []; |
| | | bankOptions.value = list |
| | | .map((b) => (b?.bankName == null ? "" : String(b.bankName).trim())) |
| | | .filter((v) => v !== ""); |
| | | }); |
| | | }; |
| | | |
| | | const loadUserList = () => { |
| | | return userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data || []; |
| | | }); |
| | | }; |
| | | |
| | | // æå¹³åé¨é¨æ ä¾ä¸æä½¿ç¨ |
| | | 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 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) => { |
| | | nextTick(() => { |
| | | loadDeptOptions(); |
| | | loadBankOptions(); |
| | | loadUserList(); |
| | | employeeList.value = []; |
| | | Object.assign(form.value, { |
| | | id: undefined, |
| | | salaryTitle: "", |
| | | deptIds: [], |
| | | salaryMonth: "", |
| | | remark: "", |
| | | payBank: "", |
| | | auditUserId: undefined, |
| | | }); |
| | | // ç¼è¾ï¼å表页已è¿åä¸»è¡¨åæ®µï¼è¿éåªååæ¾ï¼æç»ç±âçæå·¥èµè¡¨/计ç®å·¥èµâå¾å°ï¼ |
| | | if (type === "edit" && row?.id) { |
| | | form.value.id = row.id; |
| | | form.value.salaryTitle = row.salaryTitle ?? ""; |
| | | // deptIds å端æ¯å符串ï¼å¤ä¸ªç¨éå·åéï¼ï¼å½å表å仿¯åé deptId |
| | | form.value.deptIds = row.deptIds |
| | | ? String(row.deptIds).split(",").map((id) => Number(id.trim())).filter(Boolean) |
| | | : []; |
| | | form.value.salaryMonth = row.salaryMonth ?? ""; |
| | | 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) => ({ |
| | | staffOnJobId: e.staffOnJobId ?? e.staffId ?? e.id, |
| | | id: e.staffOnJobId ?? e.staffId ?? e.id, |
| | | staffName: e.staffName ?? "", |
| | | postName: e.postName ?? "", |
| | | deptName: e.deptName ?? "", |
| | | basicSalary: parseNum(e.basicSalary), |
| | | pieceSalary: parseNum(e.pieceSalary), |
| | | hourlySalary: parseNum(e.hourlySalary), |
| | | otherIncome: parseNum(e.otherIncome), |
| | | socialPersonal: parseNum(e.socialPersonal), |
| | | fundPersonal: parseNum(e.fundPersonal), |
| | | otherDeduct: parseNum(e.otherDeduct), |
| | | salaryTax: parseNum(e.salaryTax), |
| | | grossSalary: parseNum(e.grossSalary), |
| | | deductSalary: parseNum(e.deductSalary), |
| | | netSalary: parseNum(e.netSalary), |
| | | remark: e.remark ?? "", |
| | | })); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | 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({ |
| | | staffOnJobId: id, |
| | | id, |
| | | staffName: node.label, |
| | | postName: node.postName ?? node.post ?? "", |
| | | deptName: node.deptName ?? "", |
| | | basicSalary: 0, |
| | | pieceSalary: 0, |
| | | hourlySalary: 0, |
| | | otherIncome: 0, |
| | | socialPersonal: 0, |
| | | fundPersonal: 0, |
| | | otherDeduct: 0, |
| | | salaryTax: 0, |
| | | grossSalary: 0, |
| | | deductSalary: 0, |
| | | netSalary: 0, |
| | | remark: "", |
| | | }); |
| | | }); |
| | | addPersonVisible.value = false; |
| | | }; |
| | | |
| | | const removeEmployee = (row) => { |
| | | employeeList.value = employeeList.value.filter( |
| | | (e) => (e.staffOnJobId || e.id) !== (row.staffOnJobId || 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.staffOnJobId || e.id)); |
| | | employeeList.value = employeeList.value.filter( |
| | | (e) => !ids.has(e.staffOnJobId || e.id) |
| | | ); |
| | | }; |
| | | |
| | | const handleGenerate = () => { |
| | | if (!form.value.deptIds?.length) { |
| | | proxy.$modal.msgWarning("请å
éæ©é¨é¨"); |
| | | return; |
| | | } |
| | | if (!form.value.salaryMonth) { |
| | | proxy.$modal.msgWarning("请å
éæ©å·¥èµæä»½"); |
| | | return; |
| | | } |
| | | const payload = { |
| | | ids: form.value.deptIds, |
| | | date: form.value.salaryMonth, |
| | | }; |
| | | staffSalaryMainCalculateSalary(payload).then((res) => { |
| | | const list = Array.isArray(res?.data) ? res.data : []; |
| | | if (!list.length) { |
| | | proxy.$modal.msgWarning("æªè®¡ç®å°å·¥èµæ°æ®"); |
| | | return; |
| | | } |
| | | employeeList.value = list.map((e) => ({ |
| | | ...e, |
| | | staffOnJobId: e.staffOnJobId ?? e.staffId ?? e.id, |
| | | staffName: e.staffName, |
| | | postName: e.postName, |
| | | deptName: e.deptName, |
| | | basicSalary: parseNum(e.basicSalary), |
| | | pieceSalary: parseNum(e.pieceSalary), |
| | | hourlySalary: parseNum(e.hourlySalary), |
| | | otherIncome: parseNum(e.otherIncome), |
| | | socialPersonal: parseNum(e.socialPersonal), |
| | | fundPersonal: parseNum(e.fundPersonal), |
| | | otherDeduct: parseNum(e.otherDeduct), |
| | | salaryTax: parseNum(e.salaryTax), |
| | | grossSalary: parseNum(e.grossSalary), |
| | | deductSalary: parseNum(e.deductSalary), |
| | | netSalary: parseNum(e.netSalary), |
| | | remark: e.remark ?? "", |
| | | })); |
| | | proxy.$modal.msgSuccess("çææå"); |
| | | }); |
| | | }; |
| | | |
| | | const handleClear = () => { |
| | | proxy.$modal.confirm("ç¡®å®æ¸
空å½ååå·¥å表åï¼").then(() => { |
| | | employeeList.value = []; |
| | | }).catch(() => {}); |
| | | }; |
| | | |
| | | const handleTaxForm = () => { |
| | | taxDialogVisible.value = true; |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value?.validate((valid) => { |
| | | if (!valid) return; |
| | | saveData(3); // 确认æäº¤ï¼ç¶æä¸º3ï¼å¾
å®¡æ ¸ï¼ |
| | | }); |
| | | }; |
| | | |
| | | const saveDraft = () => { |
| | | formRef.value?.validate((valid) => { |
| | | if (!valid) return; |
| | | saveData(1); // ä¿åè稿ï¼ç¶æä¸º1ï¼èç¨¿ï¼ |
| | | }); |
| | | }; |
| | | |
| | | const saveData = (status) => { |
| | | const payload = { |
| | | id: form.value.id, |
| | | salaryTitle: form.value.salaryTitle, |
| | | deptIds: form.value.deptIds?.length ? form.value.deptIds.join(",") : "", |
| | | salaryMonth: form.value.salaryMonth, |
| | | remark: form.value.remark, |
| | | payBank: form.value.payBank, |
| | | auditUserId: form.value.auditUserId, |
| | | auditUserName: auditUserName.value, |
| | | totalSalary: totalSalary.value, |
| | | staffSalaryDetailList: employeeList.value.map((e) => ({ |
| | | staffOnJobId: e.staffOnJobId ?? e.staffId ?? e.id, |
| | | staffName: e.staffName, |
| | | postName: e.postName ?? "", |
| | | deptName: e.deptName ?? "", |
| | | basicSalary: parseNum(e.basicSalary), |
| | | pieceSalary: parseNum(e.pieceSalary), |
| | | hourlySalary: parseNum(e.hourlySalary), |
| | | otherIncome: parseNum(e.otherIncome), |
| | | socialPersonal: parseNum(e.socialPersonal), |
| | | fundPersonal: parseNum(e.fundPersonal), |
| | | otherDeduct: parseNum(e.otherDeduct), |
| | | salaryTax: parseNum(e.salaryTax), |
| | | grossSalary: parseNum(e.grossSalary), |
| | | deductSalary: parseNum(e.deductSalary), |
| | | netSalary: parseNum(e.netSalary), |
| | | remark: e.remark ?? "", |
| | | })), |
| | | }; |
| | | if (props.operationType === "add") { |
| | | staffSalaryMainAdd({ ...payload, status }).then(() => { |
| | | proxy.$modal.msgSuccess(status === 1 ? "è稿ä¿åæå" : "æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | } else { |
| | | staffSalaryMainUpdate({ ...payload, status }).then(() => { |
| | | proxy.$modal.msgSuccess(status === 1 ? "è稿ä¿åæå" : "æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | const closeDia = () => { |
| | | dialogVisible.value = false; |
| | | emit("close"); |
| | | }; |
| | | |
| | | 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; |
| | | } |
| | | .salary-total { |
| | | margin-top: 16px; |
| | | padding: 12px 16px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | text-align: right; |
| | | font-size: 16px; |
| | | } |
| | | .salary-total .total-label { |
| | | color: #606266; |
| | | margin-right: 8px; |
| | | } |
| | | .salary-total .total-value { |
| | | color: #f56c6c; |
| | | font-weight: bold; |
| | | font-size: 18px; |
| | | } |
| | | </style> |