| src/api/personnelManagement/monthlyStatistics.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/humanResources/attendance/checkin.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/humanResources/attendance/report.vue | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/humanResources/monthlyStatistics/detail.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/humanResources/monthlyStatistics/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/personnelManagement/monthlyStatistics.js
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,65 @@ import request from "@/utils/request"; // 人åèªèµå°è´¦å表 export function monthlyStatisticsListPage(query) { return request({ url: "/compensationPerformance/listPage", method: "get", params: query, }); } // 人åèªèµå°è´¦è¯¦æ export function monthlyStatisticsGet(id) { return request({ url: "/monthlyStatistics/get", method: "get", params: { id }, }); } // æ°å¢äººåèªèµå°è´¦ export function monthlyStatisticsAdd(data) { return request({ url: "/compensationPerformance/add", method: "post", data, }); } // ç¼è¾äººåèªèµå°è´¦ export function monthlyStatisticsUpdate(data) { return request({ url: "/compensationPerformance/update", method: "post", data, }); } // å é¤äººåèªèµå°è´¦ export function monthlyStatisticsDelete(ids) { return request({ url: "/compensationPerformance/delete", method: "delete", data: ids, }); } // 导åºäººåèªèµå°è´¦ export function monthlyStatisticsExport(query) { return request({ url: "/compensationPerformance/export", method: "get", params: query, responseType: "blob", }); } // 人åå表 export function staffOnJobList(query) { return request({ url: "/staff/staffOnJob/list", method: "get", params: query, }); } src/pages.json
@@ -858,18 +858,32 @@ } }, { "path": "pages/attendance/checkin", "path": "pages/humanResources/attendance/checkin", "style": { "navigationBarTitleText": "æå¡ç¾å°", "navigationStyle": "custom" } }, { "path": "pages/attendance/report", "path": "pages/humanResources/attendance/report", "style": { "navigationBarTitleText": "è夿¥æ¥", "navigationStyle": "custom" } }, { "path": "pages/humanResources/monthlyStatistics/index", "style": { "navigationBarTitleText": "èªèµå°è´¦", "navigationStyle": "custom" } }, { "path": "pages/humanResources/monthlyStatistics/detail", "style": { "navigationBarTitleText": "èªèµå°è´¦è¯¦æ ", "navigationStyle": "custom" } } ], "subPackages": [ src/pages/humanResources/attendance/checkin.vue
ÎļþÃû´Ó src/pages/attendance/checkin.vue ÐÞ¸Ä @@ -186,7 +186,7 @@ // 导èªå°è¯¦ç»æ¥åé¡µé¢ const navigateToReport = () => { uni.navigateTo({ url: "/pages/attendance/report", url: "/pages/humanResources/attendance/report", }); }; src/pages/humanResources/attendance/report.vue
src/pages/humanResources/monthlyStatistics/detail.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,357 @@ <template> <view class="hazard-source-detail"> <PageHeader :title="isEdit ? 'ç¼è¾èªèµå°è´¦' : 'æ°å¢èªèµå°è´¦'" @back="goBack" /> <u-form @submit="handleSubmit" ref="formRef" label-width="110"> <!-- èªèµä¿¡æ¯ --> <u-cell-group title="èªèµä¿¡æ¯"> <u-form-item label="ç»è®¡æä»½" prop="payDate" required border-bottom> <u-input v-model="form.payDate" placeholder="è¯·éæ©æä»½" @click="showDatePicker" readonly /> <template #right> <up-icon name="arrow-right" @click="showDatePicker"></up-icon> </template> </u-form-item> <u-form-item label="åå·¥å§å" prop="staffId" required border-bottom> <u-input v-model="form.staffName" placeholder="è¯·éæ©åå·¥" @click="showStaffSheet" readonly /> <template #right> <up-icon name="arrow-right" @click="showStaffSheet"></up-icon> </template> </u-form-item> <u-form-item label="åºæ¬å·¥èµ" prop="basicSalary" required border-bottom> <u-input v-model="form.basicSalary" type="number" placeholder="请è¾å ¥åºæ¬å·¥èµ" /> </u-form-item> <u-form-item label="计件工èµ" prop="pieceworkSalary" border-bottom> <u-input v-model="form.pieceworkSalary" type="number" placeholder="请è¾å ¥è®¡ä»¶å·¥èµ" /> </u-form-item> <u-form-item label="计æ¶å·¥èµ" prop="hourlySalary" border-bottom> <u-input v-model="form.hourlySalary" type="number" placeholder="请è¾å ¥è®¡æ¶å·¥èµ" /> </u-form-item> <u-form-item label="å ¶ä»æ¶å ¥" prop="otherIncome" border-bottom> <u-input v-model="form.otherIncome" type="number" placeholder="请è¾å ¥å ¶ä»æ¶å ¥" /> </u-form-item> <u-form-item label="社ä¿ä¸ªäºº" prop="socialSecurityIndividuals" border-bottom> <u-input v-model="form.socialSecurityIndividuals" type="number" placeholder="请è¾å ¥ç¤¾ä¿ä¸ªäºº" /> </u-form-item> <u-form-item label="å ¬ç§¯é个人" prop="providentFundIndividuals" border-bottom> <u-input v-model="form.providentFundIndividuals" type="number" placeholder="请è¾å ¥å ¬ç§¯é个人" /> </u-form-item> <u-form-item label="个人æå¾ç¨" prop="personalIncomeTax" border-bottom> <u-input v-model="form.personalIncomeTax" type="number" placeholder="请è¾å ¥ä¸ªäººæå¾ç¨" /> </u-form-item> <u-form-item label="å ¶ä»æ£æ¬¾" prop="otherDeductions" border-bottom> <u-input v-model="form.otherDeductions" type="number" placeholder="请è¾å ¥å ¶ä»æ£æ¬¾" /> </u-form-item> <u-form-item label="夿³¨" prop="remark" border-bottom> <u-textarea v-model="form.remark" placeholder="请è¾å ¥å¤æ³¨" :rows="3" :autoHeight="true" /> </u-form-item> </u-cell-group> <!-- æäº¤æé® --> <view class="footer-btns"> <u-button class="cancel-btn" @click="goBack">åæ¶</u-button> <u-button class="sign-btn" type="primary" @click="handleSubmit" :loading="loading">{{ isEdit ? 'æ´æ°' : 'ä¿å' }}</u-button> </view> </u-form> <!-- åå·¥éæ©å¨ --> <up-action-sheet :show="staffSheetVisible" :actions="staffOptions" @select="handleStaffSelect" @close="staffSheetVisible = false" title="éæ©åå·¥" /> <!-- æ¥æéæ©å¨ --> <up-datetime-picker :show="datePickerVisible" v-model="currentDate" @confirm="onDateConfirm" @cancel="datePickerVisible = false" mode="month" /> </view> </template> <script setup> // æ¿æ¢ toast æ¹æ³ defineOptions({ name: "monthly-statistics-detail" }); const showToast = message => { uni.showToast({ title: message, icon: "none", }); }; import { ref, onMounted } from "vue"; import PageHeader from "@/components/PageHeader.vue"; import { monthlyStatisticsAdd, monthlyStatisticsUpdate, staffOnJobList, } from "@/api/personnelManagement/monthlyStatistics"; import dayjs from "dayjs"; import { onLoad } from "@dcloudio/uni-app"; // è¡¨åæ°æ® const form = ref({ id: "", payDate: "", staffId: "", staffName: "", basicSalary: 0, pieceworkSalary: 0, hourlySalary: 0, otherIncome: 0, socialSecurityIndividuals: 0, providentFundIndividuals: 0, personalIncomeTax: 0, otherDeductions: 0, payableWages: 0, deductibleWages: 0, actualWages: 0, remark: "", }); // 页é¢ç¶æ const loading = ref(false); const formRef = ref(null); const isEdit = ref(false); // åå·¥éæ©å¨ const staffSheetVisible = ref(false); const staffOptions = ref([]); const staffList = ref([]); const showStaffSheet = () => { if (staffOptions.value.length === 0) { loadStaffList(); } else { staffSheetVisible.value = true; } }; const handleStaffSelect = item => { const staff = staffList.value.find(s => s.id === item.value); if (staff) { form.value.staffId = staff.id; form.value.staffName = staff.staffName; } staffSheetVisible.value = false; }; const loadStaffList = () => { staffOnJobList().then(res => { if (res.code === 200) { staffList.value = res.data || []; staffOptions.value = staffList.value.map(item => ({ value: item.id, name: item.staffName, subname: `å·¥å·: ${item.staffNo}`, })); staffSheetVisible.value = true; } }); }; // æ¥æéæ©å¨ const datePickerVisible = ref(false); const currentDate = ref(Date.now()); const showDatePicker = () => { datePickerVisible.value = true; }; const onDateConfirm = e => { form.value.payDate = dayjs(e.value).format("YYYY-MM"); currentDate.value = e.value; datePickerVisible.value = false; }; // è¿åä¸ä¸é¡µ const goBack = () => { // è¿åæ¶æ¸ 餿¬å°åå¨çæ°æ® uni.removeStorageSync("monthlyStatistics"); uni.navigateBack(); }; // æäº¤è¡¨å const handleSubmit = async () => { if (!form.value.payDate) { showToast("è¯·éæ©ç»è®¡æä»½"); return; } if (!form.value.staffId) { showToast("è¯·éæ©åå·¥"); return; } if (!form.value.basicSalary) { showToast("请è¾å ¥åºæ¬å·¥èµ"); return; } // 计ç®åºåå·¥èµãåºæ£å·¥èµåå®åå·¥èµ const payableWages = Number(form.value.basicSalary) + Number(form.value.pieceworkSalary) + Number(form.value.hourlySalary) + Number(form.value.otherIncome); const deductibleWages = Number(form.value.socialSecurityIndividuals) + Number(form.value.providentFundIndividuals) + Number(form.value.personalIncomeTax) + Number(form.value.otherDeductions); const actualWages = payableWages - deductibleWages; const submitData = { ...form.value, payableWages, deductibleWages, actualWages, }; try { loading.value = true; if (isEdit.value) { const { code } = await monthlyStatisticsUpdate(submitData); if (code === 200) { showToast("æ´æ°æå"); setTimeout(() => { goBack(); }, 500); } else { loading.value = false; showToast("æ´æ°å¤±è´¥ï¼è¯·éè¯"); } } else { const { code } = await monthlyStatisticsAdd(submitData); if (code === 200) { showToast("ä¿åæå"); setTimeout(() => { goBack(); }, 500); } else { loading.value = false; showToast("ä¿å失败ï¼è¯·éè¯"); } } } catch (e) { loading.value = false; console.error("æäº¤å¤±è´¥:", e); showToast("æäº¤å¤±è´¥ï¼è¯·éè¯"); } }; onLoad(() => { // ç¼è¾èªèµå°è´¦æ¶ï¼ä»æ¬å°åå¨è·åæ°æ® const monthlyStatistics = uni.getStorageSync("monthlyStatistics"); if (monthlyStatistics.id) { form.value = monthlyStatistics; isEdit.value = true; } else { isEdit.value = false; } }); onMounted(() => { // 设置é»è®¤æ¶é´ if (!isEdit.value) { form.value.payDate = dayjs().format("YYYY-MM"); currentDate.value = Date.now(); } }); </script> <style scoped lang="scss"> @import "@/static/scss/form-common.scss"; .hazard-source-detail { min-height: 100vh; background: #f8f9fa; padding-bottom: 5rem; } .footer-btns { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; display: flex; justify-content: space-around; align-items: center; padding: 0.75rem 0; box-shadow: 0 -0.125rem 0.5rem rgba(0, 0, 0, 0.05); z-index: 1000; } .cancel-btn { font-weight: 400; font-size: 1rem; color: #666; background: #f5f5f5; border: 1px solid #ddd; width: 45%; height: 2.5rem; border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; } .sign-btn { font-weight: 500; font-size: 1rem; color: #fff; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; width: 45%; height: 2.5rem; border-radius: 2.5rem 2.5rem 2.5rem 2.5rem; } </style> src/pages/humanResources/monthlyStatistics/index.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,247 @@ <template> <view class="sales-accoun"> <!-- 使ç¨éç¨é¡µé¢å¤´é¨ç»ä»¶ --> <PageHeader title="èªèµå°è´¦" @back="goBack" /> <!-- æç´¢åçéåºå --> <view class="search-section"> <view class="search-bar"> <view class="search-input"> <up-input class="search-text" placeholder="请è¾å ¥åå·¥å§å" v-model="searchKeyword" @blur="getList" clearable /> </view> <view class="filter-button" @click="getList"> <u-icon name="search" size="24" color="#999"></u-icon> </view> </view> </view> <!-- èªèµå°è´¦å表 --> <view class="ledger-list" v-if="ledgerList.length > 0"> <view v-for="(item, index) in ledgerList" :key="index"> <view class="ledger-item"> <view class="item-header"> <view class="item-left"> <view class="document-icon"> <up-icon name="file-text" size="16" color="#ffffff"></up-icon> </view> <text class="item-id">èªèµæä»½ï¼{{ item.payDate }}</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.staffName || '-' }}</text> </view> <view class="detail-row"> <text class="detail-label">åºæ¬å·¥èµ</text> <text class="detail-value">{{ item.basicSalary || 0 }}</text> </view> <view class="detail-row"> <text class="detail-label">计件工èµ</text> <text class="detail-value">{{ item.pieceworkSalary || 0 }}</text> </view> <view class="detail-row"> <text class="detail-label">计æ¶å·¥èµ</text> <text class="detail-value">{{ item.hourlySalary || 0 }}</text> </view> <view class="detail-row"> <text class="detail-label">å ¶ä»æ¶å ¥</text> <text class="detail-value">{{ item.otherIncome || 0 }}</text> </view> <view class="detail-row"> <text class="detail-label">åºåå·¥èµ</text> <text class="detail-value">{{ item.payableWages || 0 }}</text> </view> <view class="detail-row"> <text class="detail-label">åºæ£å·¥èµ</text> <text class="detail-value">{{ item.deductibleWages || 0 }}</text> </view> <view class="detail-row"> <text class="detail-label">å®åå·¥èµ</text> <text class="detail-value">{{ item.actualWages || 0 }}</text> </view> </view> <!-- æé®åºå --> <view class="action-buttons"> <u-button type="primary" size="small" class="action-btn" @click="editItem(item)"> ç¼è¾ </u-button> <u-button type="error" size="small" class="action-btn" @click="deleteItem(item)"> å é¤ </u-button> </view> </view> </view> </view> <view v-else class="no-data"> <text>ææ èªèµå°è´¦æ°æ®</text> </view> <!-- æµ®å¨æ°å¢æé® --> <view class="fab-button" @click="addItem"> <up-icon name="plus" size="24" color="#ffffff"></up-icon> </view> </view> </template> <script setup> import { ref, onMounted } from "vue"; import { onShow } from "@dcloudio/uni-app"; import PageHeader from "@/components/PageHeader.vue"; import { monthlyStatisticsListPage, monthlyStatisticsDelete, } from "@/api/personnelManagement/monthlyStatistics"; import useUserStore from "@/store/modules/user"; // æ¿æ¢ toast æ¹æ³ defineOptions({ name: "monthly-statistics-index" }); const showToast = message => { uni.showToast({ title: message, icon: "none", }); }; const userStore = useUserStore(); // æç´¢å ³é®è¯ const searchKeyword = ref(""); // èªèµå°è´¦æ°æ® const ledgerList = ref([]); // è¿åä¸ä¸é¡µ const goBack = () => { uni.navigateBack(); }; // æ¥è¯¢å表 const getList = () => { showLoadingToast("å è½½ä¸..."); const params = { current: -1, size: -1, staffName: searchKeyword.value, }; monthlyStatisticsListPage(params) .then(res => { ledgerList.value = res.records || res.data?.records || []; closeToast(); }) .catch(() => { closeToast(); showToast("è·åæ°æ®å¤±è´¥"); }); }; // æ¾ç¤ºå è½½æç¤º const showLoadingToast = message => { uni.showLoading({ title: message, mask: true, }); }; // å ³éæç¤º const closeToast = () => { uni.hideLoading(); }; // æ°å¢èªèµå°è´¦ const addItem = () => { uni.setStorageSync("monthlyStatistics", {}); uni.navigateTo({ url: "/pages/humanResources/monthlyStatistics/detail", }); }; // ç¼è¾èªèµå°è´¦ const editItem = item => { uni.setStorageSync("monthlyStatistics", item); uni.navigateTo({ url: "/pages/humanResources/monthlyStatistics/detail", }); }; // å é¤èªèµå°è´¦ const deleteItem = item => { uni.showModal({ title: "å é¤ç¡®è®¤", content: `ç¡®å®è¦å é¤è¯¥èªèµå°è´¦è®°å½åï¼`, success: res => { if (res.confirm) { deleteItemById(item.id); } }, }); }; // å é¤èªèµå°è´¦è®°å½ const deleteItemById = id => { showLoadingToast("å é¤ä¸..."); monthlyStatisticsDelete([id]) .then(() => { closeToast(); showToast("å 餿å"); getList(); }) .catch(() => { closeToast(); showToast("å é¤å¤±è´¥"); }); }; onMounted(() => { getList(); }); onShow(() => { getList(); }); </script> <style scoped lang="scss"> @import "../../../styles/sales-common.scss"; // 页é¢ç¹å®çæ ·å¼è¦ç .sales-accoun { min-height: 100vh; background: #f8f9fa; position: relative; padding-bottom: 80px; } // ç¹å®ç徿 æ ·å¼ .document-icon { background: #667eea; // ä¿æé¡µé¢ç¹æçèæ¯è² } // ç¹ææ ·å¼ .detail-value { word-break: break-all; // ä¿ç页é¢ç¹æçææ¬æ¢è¡æ ·å¼ } // ç¹å®çæµ®å¨æé®æ ·å¼ .fab-button { background: #667eea; // ä¿æé¡µé¢ç¹æçèæ¯è² box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3); // ä¿æé¡µé¢ç¹æçé´å½±ææ } </style> src/pages/index.vue
@@ -454,29 +454,29 @@ label: "设å¤ä¿å »", }, { icon: "/static/images/icon/guzhangfenxi@2x.png", label: "åæè¿½æº¯", bgColor: "#ff9800", }, { icon: "/static/images/icon/zhinengpaidan@2x.png", label: "æºè½æ´¾å", bgColor: "#ff6b35", }, { icon: "/static/images/icon/zuoyezhidao@2x.png", label: "ä½ä¸æå¯¼", bgColor: "#4caf50", }, { icon: "/static/images/icon/jieguoyanzheng@2x.png", label: "ç»æéªè¯", bgColor: "#9c27b0", }, { icon: "/static/images/icon/xunjianshangchuan@2x.png", label: "å·¡æ£ä¸ä¼ ", }, // { // icon: "/static/images/icon/guzhangfenxi@2x.png", // label: "åæè¿½æº¯", // bgColor: "#ff9800", // }, // { // icon: "/static/images/icon/zhinengpaidan@2x.png", // label: "æºè½æ´¾å", // bgColor: "#ff6b35", // }, // { // icon: "/static/images/icon/zuoyezhidao@2x.png", // label: "ä½ä¸æå¯¼", // bgColor: "#4caf50", // }, // { // icon: "/static/images/icon/jieguoyanzheng@2x.png", // label: "ç»æéªè¯", // bgColor: "#9c27b0", // }, ]); // å¤ç常ç¨åè½ç¹å» @@ -769,7 +769,12 @@ break; case "æå¡ç¾å°": uni.navigateTo({ url: "/pages/attendance/checkin", url: "/pages/humanResources/attendance/checkin", }); break; case "人åèªèµ": uni.navigateTo({ url: "/pages/humanResources/monthlyStatistics/index", }); break; default: