Merge remote-tracking branch 'origin/dev_ai' into dev_ai
| | |
| | | "jsencrypt": "3.3.2", |
| | | "nprogress": "0.2.0", |
| | | "pinia": "2.1.7", |
| | | "qrcode": "^1.5.4", |
| | | "sortablejs": "^1.15.6", |
| | | "splitpanes": "3.1.5", |
| | | "vue": "3.4.31", |
| | | "vue-cropper": "1.1.1", |
| | | "vue-esign": "^1.1.4", |
| | | "vue-router": "4.4.0", |
| | | "vuedraggable": "4.1.0" |
| | | }, |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ ¹æ®æ¥ææ¥è¯¢ |
| | | export const reportForms = (params) => { |
| | | console.log(params); |
| | | return request({ |
| | | url: "/account/accountExpense/report/forms", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢æ¯ææ°æ®-æ¶å
¥ |
| | | export const reportIncome = (params) => { |
| | | console.log(params); |
| | | return request({ |
| | | url: "/account/accountExpense/report/income", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢æ¯ææ°æ®-æ¯åº |
| | | export const reportExpense = (params) => { |
| | | console.log(params); |
| | | return request({ |
| | | url: "/account/accountExpense/report/expense", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
¶Ô±ÈÐÂÎļþ |
| | |
| | | // èªé
¬ç®¡ç |
| | | import request from "@/utils/request"; |
| | | |
| | | // æ¥è¯¢å表 |
| | | export function compensationListPage(query) { |
| | | return request({ |
| | | url: "/compensationPerformance/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // æ°å¢ |
| | | export function compensationAdd(query) { |
| | | return request({ |
| | | url: "/compensationPerformance/add", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // ä¿®æ¹ |
| | | export function compensationUpdate(query) { |
| | | return request({ |
| | | url: "/compensationPerformance/update", |
| | | method: "post", |
| | | data: query, |
| | | }); |
| | | } |
| | | // å é¤ |
| | | export function compensationDelete(query) { |
| | | return request({ |
| | | url: "/compensationPerformance/delete", |
| | | method: "delete", |
| | | data: query, |
| | | }); |
| | | } |
¶Ô±ÈÐÂÎļþ |
| | |
| | | @font-face { |
| | | font-family: "MyCustomFont"; |
| | | src: url("./DIN Alternate Bold.ttf") format("truetype"); |
| | | font-weight: 700; /* ç²ä½ */ |
| | | font-style: normal; |
| | | font-display: swap; |
| | | } |
| | |
| | | }
|
| | | .el-dialog__body {
|
| | | padding: 16px 40px 0 40px;
|
| | | max-height: 90vh;
|
| | | max-height: 74vh;
|
| | | overflow-y: auto;
|
| | | }
|
| | | .el-dialog__footer {
|
| | |
| | | <template> |
| | | <div> |
| | | <div style="position: relative;"> |
| | | <div ref="chartRef" :style="chartStyle"></div> |
| | | <slot></slot> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | |
| | | // Methods |
| | | function generateChart(option) { |
| | | const copiedOption = JSON.parse(JSON.stringify(option)) // â
æ·±æ·è´ |
| | | const copiedOption = option |
| | | |
| | | if (copiedOption.series && copiedOption.series.length > 0) { |
| | | copiedOption.series.forEach((s, index) => { |
| | |
| | | series: props.series, |
| | | grid: props.grid, |
| | | legend: props.legend, |
| | | tooltip: props.tooltip |
| | | tooltip: props.tooltip, |
| | | } |
| | | |
| | | chartInstance.clear() |
| | |
| | | |
| | | // Watch all reactive props that affect the chart |
| | | watch( |
| | | () => [props.xAxis, props.series], |
| | | () => [props.xAxis, props.series, props.legend, props.tooltip], |
| | | () => { |
| | | if (chartInstance) { |
| | | renderChart() |
| | |
| | | import "virtual:svg-icons-register";
|
| | | import SvgIcon from "@/components/SvgIcon";
|
| | | import elementIcons from "@/components/SvgIcon/svgicon";
|
| | | import "./assets/fonts/font.css";
|
| | |
|
| | | import "./permission"; // permission control
|
| | |
|
| | |
| | | <div v-if="!activity.isShen" class="node-reason"> |
| | | <span>å®¡æ¹æè§ï¼</span>{{ activity.approveNodeReason }} |
| | | </div> |
| | | <div v-if="!activity.isShen" class="node-reason"> |
| | | <span>ç¾åï¼</span> |
| | | <img :src="activity.urlTem" class="signImg" alt="" v-if="activity.urlTem"/> |
| | | </div> |
| | | <div v-else-if="activity.isShen"> |
| | | <el-form-item |
| | | :prop="'activities.' + index + '.approveNodeReason'" |
| | |
| | | <template #footer v-if="operationType === 'approval'"> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm(2)">ä¸éè¿</el-button> |
| | | <el-button type="primary" @click="submitForm(1)">éè¿</el-button> |
| | | <el-button type="primary" @click="openSignatureDialog(1)">éè¿</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- çµåç¾åå¼¹çªï¼vue3-signature-padï¼ --> |
| | | <el-dialog v-model="signatureDialogVisible" title="çµåç¾å" width="600px" append-to-body> |
| | | <vueEsign |
| | | ref="esign" |
| | | class="mySign" |
| | | :width="800" |
| | | :height="300" |
| | | :isCrop="isCrop" |
| | | :lineWidth="lineWidth" |
| | | :lineColor="lineColor" |
| | | /> |
| | | <div style="margin-top:10px;"> |
| | | <el-button @click="clearSignature">æ¸
é¤</el-button> |
| | | <el-button type="primary" @click="confirmSignature">ç¡®å®</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {getCurrentInstance, reactive, ref, toRefs} from "vue"; |
| | | import { getCurrentInstance, reactive, ref, toRefs } from "vue"; |
| | | import vueEsign from "vue-esign"; |
| | | import { |
| | | approveProcessDetails, |
| | | getDept, |
| | |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import {userListNoPageByTenantId} from "@/api/system/user.js"; |
| | | import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue' |
| | | import { getToken } from "@/utils/auth"; |
| | | const emit = defineEmits(['close']) |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | |
| | | }, |
| | | }); |
| | | const { form } = toRefs(data); |
| | | const signatureDialogVisible = ref(false); |
| | | const signatureImg = ref(''); |
| | | let submitStatus = null; // 临æ¶åå¨éè¿/ä¸éè¿ç¶æ |
| | | const isCrop = ref(""); |
| | | const esign = ref(null); |
| | | const lineWidth = ref(0); |
| | | const lineColor = ref("#000000"); |
| | | |
| | | // ä¸ä¼ é
ç½® |
| | | const upload = reactive({ |
| | | // ä¸ä¼ çå°å |
| | | url: import.meta.env.VITE_APP_BASE_API + "/file/upload", |
| | | // 设置ä¸ä¼ ç请æ±å¤´é¨ |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | }); |
| | | |
| | | // èç¹æ é¢ |
| | | const getNodeTitle = (index, len) => { |
| | | if (index === len - 1) return 'ç»æ'; |
| | |
| | | activities.value = res.data |
| | | // å¢å isApprovalåæ®µ |
| | | activities.value.forEach(item => { |
| | | if (item.url.includes('word')) { |
| | | item.urlTem = item.url.replaceAll('word', 'img') |
| | | } else { |
| | | item.urlTem = item.url |
| | | } |
| | | if (item.approveNodeStatus === 2) { |
| | | item.isApproval = '已驳å'; |
| | | } else if (item.approveNodeStatus === 1) { |
| | |
| | | productOptions.value = res.data; |
| | | }); |
| | | }; |
| | | // æå¼ç¾åå¼¹çª |
| | | const openSignatureDialog = (status) => { |
| | | submitStatus = status; |
| | | signatureDialogVisible.value = true; |
| | | }; |
| | | // æ¸
é¤ç¾å |
| | | const clearSignature = () => { |
| | | esign.value.reset(); |
| | | }; |
| | | // 确认ç¾å |
| | | const confirmSignature = () => { |
| | | esign.value.generate().then((res) => { |
| | | console.log(res); |
| | | // å°base64转æ¢ä¸ºäºè¿å¶ |
| | | const base64Data = res.split(',')[1]; // ç§»é¤data:image/png;base64,åç¼ |
| | | const binaryString = atob(base64Data); |
| | | const bytes = new Uint8Array(binaryString.length); |
| | | for (let i = 0; i < binaryString.length; i++) { |
| | | bytes[i] = binaryString.charCodeAt(i); |
| | | } |
| | | signatureImg.value = bytes; |
| | | |
| | | // å建æä»¶å¯¹è±¡ç¨äºä¸ä¼ |
| | | const blob = new Blob([bytes], { type: 'image/png' }); |
| | | const file = new File([blob], 'signature.png', { type: 'image/png' }); |
| | | |
| | | // å建FormData |
| | | const formData = new FormData(); |
| | | formData.append('file', file); |
| | | |
| | | // ä¸ä¼ ç¾åå¾ç |
| | | fetch(upload.url, { |
| | | method: 'POST', |
| | | headers: upload.headers, |
| | | body: formData |
| | | }) |
| | | .then(response => response.json()) |
| | | .then(data => { |
| | | if (data.code === 200) { |
| | | console.log('data---', data) |
| | | let tempFileIds = []; |
| | | tempFileIds.push(data.data.tempId); |
| | | signatureDialogVisible.value = false; |
| | | clearSignature(); |
| | | // åªæéè¿æ¶æä¼ éç¾åæä»¶ID |
| | | if (submitStatus === 1) { |
| | | submitForm(submitStatus, tempFileIds); |
| | | } else { |
| | | submitForm(submitStatus); |
| | | } |
| | | } else { |
| | | proxy.$modal.msgError("ç¾åå¾çä¸ä¼ 失败ï¼" + data.msg); |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | console.error('ä¸ä¼ 失败:', error); |
| | | proxy.$modal.msgError("ç¾åå¾çä¸ä¼ 失败"); |
| | | }); |
| | | }).catch((err) => { |
| | | console.log(err); |
| | | proxy.$modal.msgWarning("请å
ç¾åï¼"); |
| | | }) |
| | | }; |
| | | // æäº¤å®¡æ¹ |
| | | const submitForm = (status) => { |
| | | const submitForm = (status, tempFileIds) => { |
| | | const filteredActivities = activities.value.filter(activity => activity.isShen); |
| | | filteredActivities[0].approveNodeStatus = status |
| | | filteredActivities[0].approveNodeStatus = status; |
| | | // åªæéè¿æ¶æéè¦ç¾å |
| | | if (status === 1 && tempFileIds) { |
| | | filteredActivities[0].tempFileIds = tempFileIds; |
| | | } |
| | | // 夿æ¯å¦ä¸ºæå䏿¥ |
| | | const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1; |
| | | updateApproveNode({ ...filteredActivities[0], isLast }).then(() => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } |
| | | }); |
| | | }; |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | |
| | | height: 30px; |
| | | border-radius: 50px; |
| | | } |
| | | .signImg { |
| | | cursor: pointer; |
| | | width: 200px; |
| | | height: 60px; |
| | | } |
| | | </style> |
| | |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="changePage" |
| | | > |
| | | <template #operation="{ row }"> |
| | | <el-button type="primary" text @click="edit(row.id)" icon="editPen"> |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button |
| | | type="danger" |
| | | text |
| | | icon="delete" |
| | | @click="deleteRow(row.id)" |
| | | > |
| | | å é¤ |
| | | </el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <Modal ref="modalRef" @success="getTableData"></Modal> |
| | | <el-dialog v-model="qrDialogVisible" title="äºç»´ç " width="300px"> |
| | | <div style="text-align:center;"> |
| | | <img :src="qrCodeUrl" alt="äºç»´ç " style="width:200px;height:200px;" /> |
| | | <div style="margin:10px 0;"> |
| | | <el-button type="primary" @click="downloadQRCode">ä¸è½½äºç»´ç å¾ç</el-button> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import Modal from "./Modal.vue"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import QRCode from "qrcode"; |
| | | import { ref } from "vue"; |
| | | |
| | | defineOptions({ |
| | | name: "设å¤å°è´¦", |
| | |
| | | const multipleList = ref([]); |
| | | const { proxy } = getCurrentInstance(); |
| | | const modalRef = ref(); |
| | | const qrDialogVisible = ref(false); |
| | | const qrCodeUrl = ref(""); |
| | | const qrRowData = ref(null); |
| | | |
| | | const { |
| | | filters, |
| | | columns, |
| | |
| | | align: "center", |
| | | prop: "createTime", |
| | | }, |
| | | { |
| | | fixed: "right", |
| | | label: "æä½", |
| | | dataType: "slot", |
| | | slot: "operation", |
| | | align: "center", |
| | | width: "200px", |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: 'right', |
| | | width: 140, |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | edit(row.id) |
| | | }, |
| | | }, |
| | | { |
| | | name: "çæäºç»´ç ", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | showQRCode(row) |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ] |
| | | ); |
| | | |
| | |
| | | }); |
| | | }; |
| | | |
| | | const showQRCode = async (row) => { |
| | | // ä½ å¯ä»¥èªå®ä¹äºç»´ç å
å®¹ï¼æ¯å¦ row.id æ row.deviceName |
| | | const qrContent = JSON.stringify(row); // æ `${row.id}` |
| | | qrCodeUrl.value = await QRCode.toDataURL(qrContent); |
| | | qrRowData.value = row; |
| | | qrDialogVisible.value = true; |
| | | }; |
| | | |
| | | const downloadQRCode = () => { |
| | | const a = document.createElement("a"); |
| | | a.href = qrCodeUrl.value; |
| | | a.download = `${qrRowData.value.deviceName || "äºç»´ç "}.png`; |
| | | a.click(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | filters.entryDate = [ |
| | | dayjs().format("YYYY-MM-DD"), |
| | |
| | | </PIMTable> |
| | | </div> |
| | | <Modal ref="modalRef" @success="getTableData"></Modal> |
| | | <files-dia ref="filesDia" @close="handleQuery"></files-dia> |
| | | <files-dia ref="filesDia"></files-dia> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | <template> |
| | | <div style="padding: 20px;"> |
| | | <!-- 页颿 é¢åæ¥æçé --> |
| | | <div class="w-full md:w-auto flex items-center gap-3" style="margin-bottom: 20px;"> |
| | | <el-date-picker |
| | | v-model="dateRange" |
| | | type="daterange" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | :default-value="[new Date(firstDayOfMonth), new Date()]" |
| | | @change="handleDateChange" |
| | | class="w-full md:w-auto" |
| | | style="margin-right: 30px;" |
| | | /> |
| | | |
| | | <el-button |
| | | type="primary" |
| | | icon="Refresh" |
| | | @click="resetDateRange" |
| | | size="default" |
| | | > |
| | | éç½® |
| | | </el-button> |
| | | </div> |
| | | |
| | | <main class="container mx-auto px-4 pb-10"> |
| | | <!-- è´¢å¡ææ å¡ç --> |
| | | <div class="grid-container"> |
| | | <!-- æ»æ¶å
¥ --> |
| | | <el-card class="bg1"> |
| | | <p>æ»æ¶å
¥</p> |
| | | <h3> |
| | | ¥{{ pageInfo.totalIncome }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- æ¶å
¥ç¬æ° --> |
| | | <el-card class="bg2"> |
| | | <p>æ¶å
¥ç¬æ°</p> |
| | | <h3> |
| | | {{ pageInfo.incomeNumber }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- æ»æ¯åº --> |
| | | <el-card class="bg3"> |
| | | <p>æ»æ¯åº</p> |
| | | <h3> |
| | | ¥{{ pageInfo.totalExpense }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- æ¯åºç¬æ° --> |
| | | <el-card class="bg4"> |
| | | <p>æ¯åºç¬æ°</p> |
| | | <h3> |
| | | {{ pageInfo.expenseNumber }} |
| | | </h3> |
| | | </el-card> |
| | | |
| | | <!-- åæ¶å
¥ --> |
| | | <el-card class="bg5"> |
| | | <p>åæ¶å
¥</p> |
| | | <h3> |
| | | ¥{{ pageInfo.netRevenue }} |
| | | </h3> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- æ¶å
¥ç»è®¡å¾è¡¨ --> |
| | | <div class="grid-layout"> |
| | | <el-card style="margin-bottom: 20px;"> |
| | | <h2 class="section-title">æ¶å
¥ç»è®¡(å
)</h2> |
| | | <div class="echarts"> |
| | | <Echarts :legend="pieLegend0" :chartStyle="chartStylePie" |
| | | :series="materialPieSeries0" |
| | | :tooltip="pieTooltip" style="height: 260px;width: 35%;"> |
| | | <div class="chart-num"> |
| | | <span style="font-size: 22px;">æ¶å
¥</span> |
| | | <span style="font-size: 36px; |
| | | font-weight: 500; |
| | | font-family: 'MyCustomFont', sans-serif;">{{ pageInfo.totalIncome }}</span> |
| | | </div> |
| | | </Echarts> |
| | | <Echarts ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="lineSeries0" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis0" |
| | | :yAxis="yAxis0" |
| | | style="height: 260px;width: 64%;"></Echarts> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- æ¯åºç»è®¡å¾è¡¨ --> |
| | | <el-card> |
| | | <h2 class="section-title">æ¯åºç»è®¡(å
)</h2> |
| | | <div class="echarts"> |
| | | <Echarts ref="chart" |
| | | :legend="pieLegend1" |
| | | :chartStyle="chartStylePie" |
| | | :series="materialPieSeries1" |
| | | :tooltip="pieTooltip" |
| | | style="height: 260px;width: 35%;"> |
| | | <div class="chart-num"> |
| | | <span style="font-size: 22px;">æ¯åº</span> |
| | | <span style="font-size: 36px; |
| | | font-weight: 500; |
| | | font-family: 'MyCustomFont', sans-serif;">{{ pageInfo.totalExpense }}</span> |
| | | </div></Echarts> |
| | | <Echarts ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="lineSeries1" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis1" |
| | | :yAxis="yAxis1" |
| | | style="height: 260px;width: 64%;"></Echarts> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </main> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | </script> |
| | | import { ref, computed, onMounted, reactive } from 'vue'; |
| | | import 'element-plus/dist/index.css'; |
| | | import Echarts from "@/components/Echarts/echarts.vue"; |
| | | import { reportForms,reportIncome,reportExpense } from "@/api/financialManagement/financialStatements"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | // æ¥æèå´ |
| | | const dateRange = ref([]); |
| | | const firstDayOfMonth = ref(null); |
| | | const chartStyle = { |
| | | width: '100%', |
| | | height: '100%', // 设置å¾è¡¨å®¹å¨çé«åº¦ |
| | | position:'relative', |
| | | } |
| | | const grid = { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | } |
| | | const lineLegend = { |
| | | show: false, |
| | | } |
| | | // æçº¿å¾æç¤ºæ¡ |
| | | const tooltip = reactive({ |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'line', |
| | | lineStyle: { color: '#aaa' } |
| | | }, |
| | | // èªå®ä¹å
容 |
| | | formatter: function (params) { |
| | | if (!params || !params.length) return '' |
| | | const axisLabel = params[0].axisValueLabel || params[0].axisValue || '' |
| | | const rows = params |
| | | .map(p => { |
| | | const colorDot = `<span style="display:inline-block;margin-right:6px;width:8px;height:8px;border-radius:50%;background:${p.color}"></span>` |
| | | return `${colorDot}${p.seriesName}: ${p.value}` |
| | | }) |
| | | .join('<br/>') |
| | | return `<div>${axisLabel}</div><div>${rows}</div>` |
| | | } |
| | | }) |
| | | const months = ['1æ','2æ','3æ','4æ','5æ','6æ','7æ','8æ','9æ','10æ','11æ','12æ']; |
| | | const lineSeries0 = ref([]) |
| | | const lineSeries1 = ref([]) |
| | | |
| | | const xAxis0 = ref([ |
| | | { |
| | | type: 'category', |
| | | axisTick: { show: true, alignWithLabel: true }, |
| | | data: months, |
| | | }, |
| | | ]); |
| | | const xAxis1 = ref([ |
| | | { |
| | | type: 'category', |
| | | axisTick: { show: true, alignWithLabel: true }, |
| | | data: months, |
| | | }, |
| | | ]); |
| | | const yAxis0 = [ |
| | | { |
| | | type: 'value', |
| | | name: 'æ¶å
¥ç»è®¡', // 左侧yè½´ |
| | | position: 'left', |
| | | min: 0, |
| | | // åæ è½´åç§°æ ·å¼ |
| | | nameTextStyle: { |
| | | color: '#000', |
| | | fontSize: 14, |
| | | }, |
| | | } |
| | | ] |
| | | |
| | | const yAxis1 = [ |
| | | { |
| | | type: 'value', |
| | | name: 'æ¯åºç»è®¡', // 左侧yè½´ |
| | | position: 'left', |
| | | min: 0, |
| | | // åæ è½´åç§°æ ·å¼ |
| | | nameTextStyle: { |
| | | color: '#000', |
| | | fontSize: 14, |
| | | }, |
| | | } |
| | | ] |
| | | |
| | | const chartStylePie = { |
| | | width: '100%', |
| | | height: '100%' // 设置å¾è¡¨å®¹å¨çé«åº¦ |
| | | } |
| | | const pieColors = ['#F04864','#FACC14', '#8543E0', '#1890FF', '#13C2C2','#2FC25B']; // 坿 ¹æ®å®é
è°æ´ |
| | | const pieData0 = ref([]); |
| | | const pieData1 = ref([]); |
| | | |
| | | const pieLegend0 = computed(() => ({ |
| | | show: true, |
| | | top: 'center', |
| | | left: '60%', |
| | | orient: 'vertical', |
| | | icon: 'circle', |
| | | data: pieData0.value.map(item => item.name), |
| | | formatter: function(name) { |
| | | const item = pieData0.value.find(i => i.name === name); |
| | | if (!item) return name; |
| | | return `${name} | ${item.percent} ${item.amount}`; |
| | | }, |
| | | textStyle: { |
| | | color: '#333', |
| | | fontSize: 14, |
| | | lineHeight: 26, |
| | | } |
| | | })); |
| | | const pieLegend1 = computed(() => ({ |
| | | show: true, |
| | | top: 'center', |
| | | left: '60%', |
| | | orient: 'vertical', |
| | | icon: 'circle', |
| | | data: pieData1.value.map(item => item.name), |
| | | formatter: function(name) { |
| | | const item = pieData1.value.find(i => i.name === name); |
| | | if (!item) return name; |
| | | return `${name} | ${item.percent} ${item.amount}`; |
| | | }, |
| | | textStyle: { |
| | | color: '#333', |
| | | fontSize: 14, |
| | | lineHeight: 26, |
| | | } |
| | | })); |
| | | |
| | | const materialPieSeries0 = computed(() => [ |
| | | { |
| | | type: 'pie', |
| | | radius: ['50%', '65%'], |
| | | center: ['25%', '50%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | data: pieData0.value, |
| | | color: pieColors |
| | | } |
| | | ]); |
| | | const materialPieSeries1 = computed(() => [ |
| | | { |
| | | type: 'pie', |
| | | radius: ['50%', '65%'], |
| | | center: ['25%', '50%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | data: pieData1.value, |
| | | color: pieColors |
| | | } |
| | | ]); |
| | | const pieTooltip = reactive({ |
| | | trigger: 'item', |
| | | formatter: function(params) { |
| | | // æ£æ¥æ°æ®æ¯å¦åå¨ |
| | | if (!params.data) return params.name; |
| | | // æ¼æ¥å®æ´å
容 |
| | | return ` |
| | | <div> |
| | | <div style="color:${params.color};font-size:16px;">â</div> |
| | | <div>${params.name}</div> |
| | | <div>å æ¯ï¼${params.data.percent}</div> |
| | | <div>éé¢ï¼${params.data.amount}</div> |
| | | </div> |
| | | `; |
| | | } |
| | | }) |
| | | |
| | | |
| | | const pageInfo = ref({ |
| | | }) |
| | | |
| | | const getData = async () => { |
| | | try { |
| | | const {code,data} = await reportForms({entryDateStart:dateRange.value[0], entryDateEnd:dateRange.value[1]}); |
| | | if(code === 200) { |
| | | pageInfo.value = data |
| | | pieData0.value = data.incomeType.map(item=>({ |
| | | name:item.typeName, |
| | | value:item.account, |
| | | percent:`${item.proportion*100}%`, |
| | | amount:`Â¥${item.account}` |
| | | })) |
| | | pieData1.value = data.expenseType.map(item=>({ |
| | | name:item.typeName, |
| | | value:item.account, |
| | | percent:`${item.proportion*100}%`, |
| | | amount:`Â¥${item.account}` |
| | | })) |
| | | |
| | | } |
| | | } catch (error) { |
| | | console.error('è·åè´¢å¡ææ æ°æ®å¤±è´¥ï¼', error); |
| | | } |
| | | try{ |
| | | const {code,data} = await reportIncome(); |
| | | if(code==200){ |
| | | lineSeries0.value = data.map(item=>({ |
| | | name:item.typeName, |
| | | type: 'line', |
| | | data:item.account.map(item=>Number(item)) |
| | | })) |
| | | |
| | | } |
| | | }catch (error) { |
| | | console.error('è·åè´¢å¡ææ æ°æ®å¤±è´¥ï¼', error); |
| | | } |
| | | try{ |
| | | const {code,data} = await reportExpense(); |
| | | if(code==200){ |
| | | lineSeries1.value = data.map(item=>({ |
| | | name:item.typeName, |
| | | type: 'line', |
| | | data:item.account.map(item=>Number(item)) |
| | | })) |
| | | |
| | | } |
| | | }catch (error) { |
| | | console.error('è·åè´¢å¡ææ æ°æ®å¤±è´¥ï¼', error); |
| | | } |
| | | }; |
| | | |
| | | |
| | | // åå§åæ¥æèå´ï¼é»è®¤å½æï¼ |
| | | onMounted(() => { |
| | | const today = new Date(); |
| | | const firstDay = new Date(today.getFullYear(), today.getMonth(), 1); |
| | | firstDayOfMonth.value = firstDay; |
| | | dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")]; |
| | | getData() |
| | | |
| | | }); |
| | | |
| | | // å¤çæ¥æèå´åå |
| | | const handleDateChange = (newRange) => { |
| | | if (newRange && newRange.length === 2) { |
| | | dateRange.value = newRange; |
| | | getData() |
| | | } |
| | | }; |
| | | |
| | | // éç½®æ¥æèå´ |
| | | const resetDateRange = () => { |
| | | const today = new Date(); |
| | | const firstDay = new Date(today.getFullYear(), today.getMonth(), 1); |
| | | dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")]; |
| | | getData() |
| | | }; |
| | | |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | /* åºç¡æ ·å¼è¡¥å
*/ |
| | | :root { |
| | | --el-color-primary: #4f46e5; |
| | | } |
| | | .el-card{ |
| | | position: relative; |
| | | border-radius: 12px; |
| | | padding: 14px 10px 10px 10px; |
| | | box-shadow: 0 2px 8px #eee; |
| | | :deep(.el-card__body){ |
| | | padding: 10px 20px !important; |
| | | } |
| | | &.bg1{ |
| | | background: url(@/assets/icons/png/1.png) no-repeat 100% 100% !important; |
| | | } |
| | | &.bg2{ |
| | | background: url(@/assets/icons/png/2.png) no-repeat 100% 100% !important; |
| | | } |
| | | &.bg3{ |
| | | background: url(@/assets/icons/png/3.png) no-repeat 100% 100% !important; |
| | | } |
| | | &.bg4{ |
| | | background: url(@/assets/icons/png/4.png) no-repeat 100% 100% !important; |
| | | } |
| | | &.bg5{ |
| | | background: url(@/assets/icons/png/5.png) no-repeat 100% 100% !important; |
| | | } |
| | | } |
| | | |
| | | .grid-container { |
| | | /* grid 容å¨åºç¡æ ·å¼ */ |
| | | display: grid; |
| | | gap: 1rem; /* gap-4 å¯¹åº 1rem (16px) */ |
| | | margin-bottom: 2rem; /* mb-8 å¯¹åº 2rem (32px) */ |
| | | |
| | | p{ |
| | | font-size: 22px; |
| | | margin-top: 0px; |
| | | color: #fff; |
| | | } |
| | | h3{ |
| | | font-size: 36px; |
| | | font-weight: 500; |
| | | font-family: 'MyCustomFont', sans-serif; |
| | | margin: 10px 0; |
| | | color: #fff; |
| | | } |
| | | |
| | | } |
| | | |
| | | /* ç§»å¨ç«¯é»è®¤æ ·å¼ (grid-cols-1) */ |
| | | .grid-container { |
| | | grid-template-columns: repeat(1, minmax(0, 1fr)); |
| | | } |
| | | |
| | | /* å°å±å¹åä»¥ä¸ (sm:grid-cols-2) */ |
| | | @media (min-width: 640px) { |
| | | .grid-container { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | /* 大å±å¹åä»¥ä¸ (lg:grid-cols-5) */ |
| | | @media (min-width: 1024px) { |
| | | .grid-container { |
| | | grid-template-columns: repeat(5, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | /* å¡çæ¬åææå¢å¼º */ |
| | | .el-card:hover { |
| | | transform: translateY(-2px); |
| | | } |
| | | .echarts{ |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | /* å¾è¡¨å®¹å¨æ ·å¼ */ |
| | | .el-chart { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | .section-title { |
| | | position: relative; |
| | | font-size: 18px; |
| | | color: #333; |
| | | padding-left: 10px; |
| | | margin-bottom: 10px; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .section-title::before { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0px; |
| | | content: ''; |
| | | width: 4px; |
| | | height: 18px; |
| | | background-color: #002FA7; |
| | | border-radius: 2px; |
| | | } |
| | | .chart-num{ |
| | | position: absolute; |
| | | z-index: 3; |
| | | top: 92px; |
| | | left: 92px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | } |
| | | </style> |
| | |
| | | </PIMTable> |
| | | </div> |
| | | <Modal ref="modalRef" @success="getTableData"></Modal> |
| | | <files-dia ref="filesDia" @close="handleQuery"></files-dia> |
| | | <files-dia ref="filesDia"></files-dia> |
| | | </div> |
| | | </template> |
| | | |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢å
¥è' : 'ç¼è¾äººå'" |
| | | width="50%" |
| | | @close="closeDia" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æä»½ï¼" prop="payDate"> |
| | | <el-date-picker |
| | | v-model="form.payDate" |
| | | type="month" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM" |
| | | placeholder="è¯·éæ©æä»½" |
| | | clearable |
| | | :disabled="operationType === 'edit'" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å§åï¼" prop="staffId"> |
| | | <el-select v-model="form.staffId" placeholder="è¯·éæ©äººå" style="width: 100%" @change="handleSelect" :disabled="operationType === 'edit'"> |
| | | <el-option |
| | | v-for="item in personList" |
| | | :key="item.id" |
| | | :label="item.staffName" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåºå¤å¤©æ°ï¼" prop="shouldAttendedNum"> |
| | | <el-input v-model="form.shouldAttendedNum" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å®é
åºå¤å¤©æ°ï¼" prop="actualAttendedNum"> |
| | | <el-input v-model="form.actualAttendedNum" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºæ¬å·¥èµï¼" prop="basicSalary"> |
| | | <el-input v-model="form.basicSalary" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å²ä½å·¥èµï¼" prop="postSalary"> |
| | | <el-input v-model="form.postSalary" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¥ç¦»èç¼ºå¤æ£æ¬¾ï¼" prop="deductionAbsenteeism"> |
| | | <el-input v-model="form.deductionAbsenteeism" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç
åæ£æ¬¾ï¼" prop="sickLeaveDeductions"> |
| | | <el-input v-model="form.sickLeaveDeductions" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="äºåæ£æ¬¾ï¼" prop="deductionPersonalLeave"> |
| | | <el-input v-model="form.deductionPersonalLeave" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¿è®°æå¡æ£æ¬¾ï¼" prop="forgetClockDeduct"> |
| | | <el-input v-model="form.forgetClockDeduct" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="绩æå¾åï¼" prop="performanceScore"> |
| | | <el-input v-model="form.performanceScore" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="绩æå·¥èµï¼" prop="performancePay"> |
| | | <el-input v-model="form.performancePay" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºåå计ï¼" prop="payableWages"> |
| | | <el-input v-model="form.payableWages" placeholder="请è¾å
¥" clearable type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="社ä¿ä¸ªäººï¼" prop="socialSecurityIndividuals"> |
| | | <el-input v-model="form.socialSecurityIndividuals" :precision="0" :step="1" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="社ä¿å
¬å¸ï¼" prop="socialSecurityCompanies"> |
| | | <el-input v-model="form.socialSecurityCompanies" :precision="0" :step="1" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="社ä¿å计ï¼" prop="socialSecurityTotal"> |
| | | <el-input v-model="form.socialSecurityTotal" :precision="0" :step="1" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬ç§¯é个人ï¼" prop="providentFundIndividuals"> |
| | | <el-input v-model="form.providentFundIndividuals" :precision="0" :step="1" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬ç§¯éå
¬å¸ï¼" prop="providentFundCompany"> |
| | | <el-input v-model="form.providentFundCompany" :precision="0" :step="1" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬ç§¯éå计ï¼" prop="providentFundTotal"> |
| | | <el-input v-model="form.providentFundTotal" :precision="0" :step="1" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºç¨å·¥èµï¼" prop="taxableWaget"> |
| | | <el-input v-model="form.taxableWaget" :precision="0" :step="1" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="个人æå¾ç¨ï¼" prop="personalIncomeTax"> |
| | | <el-input v-model="form.personalIncomeTax" :step="0.1" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å®åå·¥èµï¼" prop="actualWages"> |
| | | <el-input v-model="form.actualWages" style="width: 100%" type="number"/> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {getStaffJoinInfo, getStaffOnJob, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js"; |
| | | import {compensationAdd, compensationUpdate} from "@/api/personnelManagement/payrollManagement.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const data = reactive({ |
| | | form: { |
| | | payDate: "", |
| | | staffId: "", |
| | | name: "", |
| | | shouldAttendedNum: "", |
| | | actualAttendedNum: "", |
| | | basicSalary: "", |
| | | postSalary: "", |
| | | deductionAbsenteeism: "", |
| | | sickLeaveDeductions: "", |
| | | deductionPersonalLeave: "", |
| | | forgetClockDeduct: "", |
| | | performanceScore: "", |
| | | performancePay: "", |
| | | payableWages: "", |
| | | socialSecurityIndividuals: "", |
| | | socialSecurityCompanies: "", |
| | | socialSecurityTotal: "", |
| | | providentFundIndividuals: "", |
| | | providentFundCompany: "", |
| | | providentFundTotal: "", |
| | | taxableWaget: "", |
| | | personalIncomeTax: "", |
| | | actualWages: "", |
| | | }, |
| | | rules: { |
| | | payDate: [{ required: true, message: "è¯·éæ©", trigger: "change" },], |
| | | staffId: [{ required: true, message: "è¯·éæ©", trigger: "change" },], |
| | | staffName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | shouldAttendedNum: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | actualAttendedNum: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | basicSalary: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | postSalary: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | deductionAbsenteeism: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | sickLeaveDeductions: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | deductionPersonalLeave: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | forgetClockDeduct: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | performanceScore: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | performancePay: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | payableWages: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | socialSecurityIndividuals: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | socialSecurityCompanies: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | socialSecurityTotal: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | providentFundIndividuals: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | providentFundCompany: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | providentFundTotal: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | taxableWaget: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | personalIncomeTax: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | actualWages: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | }, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const personList = ref([]); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | getStaffOnJob().then(res => { |
| | | personList.value = res.data |
| | | }) |
| | | form.value = {} |
| | | if (operationType.value === 'edit') { |
| | | getStaffJoinInfo(row.id).then(res => { |
| | | form.value = {...row} |
| | | form.value.payDate = form.value.payDate + '-01' |
| | | }) |
| | | } |
| | | } |
| | | const handleSelect = (value) => { |
| | | console.log('value', value) |
| | | const index = personList.value.findIndex(row => row.id === value) |
| | | if (index > -1) { |
| | | form.value.name = personList.value[index].staffName |
| | | } |
| | | } |
| | | // æäº¤äº§å表å |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | form.value.staffState = 1 |
| | | if (operationType.value === "add") { |
| | | compensationAdd(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } else { |
| | | compensationUpdate(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | // 计ç®ååå¹´é |
| | | const calculateContractTerm = () => { |
| | | if (form.value.contractStartTime && form.value.contractEndTime) { |
| | | const startDate = new Date(form.value.contractStartTime); |
| | | const endDate = new Date(form.value.contractEndTime); |
| | | |
| | | if (endDate > startDate) { |
| | | // 计ç®å¹´ä»½å·® |
| | | const yearDiff = endDate.getFullYear() - startDate.getFullYear(); |
| | | const monthDiff = endDate.getMonth() - startDate.getMonth(); |
| | | const dayDiff = endDate.getDate() - startDate.getDate(); |
| | | |
| | | let years = yearDiff; |
| | | |
| | | // å¦æç»ææ¥æçææ¥å°äºå¼å§æ¥æçææ¥ï¼ååå»1å¹´ |
| | | if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) { |
| | | years = yearDiff - 1; |
| | | } |
| | | |
| | | form.value.contractTerm = Math.max(0, years); |
| | | } else { |
| | | form.value.contractTerm = 0; |
| | | } |
| | | } else { |
| | | form.value.contractTerm = 0; |
| | | } |
| | | }; |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | proxy.resetForm("formRef"); |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">å§åï¼</span> |
| | | <el-input |
| | | v-model="searchForm.name" |
| | | style="width: 240px" |
| | | placeholder="请è¾å
¥å§åæç´¢" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <span class="search_title ml10">æä»½ï¼</span> |
| | | <el-date-picker |
| | | v-model="searchForm.payDateStr" |
| | | type="month" |
| | | @change="handleQuery" |
| | | value-format="YYYY-MM" |
| | | format="YYYY-MM" |
| | | placeholder="è¯·éæ©æä»½" |
| | | style="width: 240px" |
| | | clearable |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">æ°å¢èªèµ</el-button> |
| | | <!-- <el-button @click="handleOut">导åº</el-button>--> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import {onMounted, ref} from "vue"; |
| | | import FormDia from "@/views/personnelManagement/payrollManagement/components/formDia.vue"; |
| | | import {staffJoinDel} from "@/api/personnelManagement/onboarding.js"; |
| | | import {ElMessageBox} from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import {compensationDelete, compensationListPage} from "@/api/personnelManagement/payrollManagement.js"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | name: "", |
| | | payDateStr: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "èªèµæä»½", |
| | | prop: "payDate", |
| | | }, |
| | | { |
| | | label: "å§å", |
| | | prop: "name", |
| | | }, |
| | | { |
| | | label: "åºåºå¤å¤©æ°", |
| | | prop: "shouldAttendedNum", |
| | | width:100 |
| | | }, |
| | | { |
| | | label: "å®é
åºå¤å¤©æ°", |
| | | prop: "actualAttendedNum", |
| | | width:110 |
| | | }, |
| | | { |
| | | label: "åºæ¬å·¥èµ", |
| | | prop: "basicSalary", |
| | | }, |
| | | { |
| | | label: "å²ä½å·¥èµ", |
| | | prop: "postSalary", |
| | | width:100 |
| | | }, |
| | | { |
| | | label: "å
¥ç¦»èç¼ºå¤æ£æ¬¾", |
| | | prop: "deductionAbsenteeism", |
| | | width:130 |
| | | }, |
| | | { |
| | | label: "ç
åæ£æ¬¾", |
| | | prop: "sickLeaveDeductions", |
| | | width:100 |
| | | }, |
| | | { |
| | | label: "äºåæ£æ¬¾", |
| | | prop: "deductionPersonalLeave", |
| | | width:100 |
| | | }, |
| | | { |
| | | label: "å¿è®°æå¡æ£æ¬¾", |
| | | prop: "forgetClockDeduct", |
| | | width:110 |
| | | }, |
| | | { |
| | | label: "绩æå¾å", |
| | | prop: "performanceScore", |
| | | width:150 |
| | | }, |
| | | { |
| | | label: "绩æå·¥èµ", |
| | | prop: "performancePay", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "åºåå计", |
| | | prop: "payableWages", |
| | | width:150 |
| | | }, |
| | | { |
| | | label: "社ä¿ä¸ªäºº", |
| | | prop: "socialSecurityIndividuals", |
| | | }, |
| | | { |
| | | label: "社ä¿å
¬å¸", |
| | | prop: "socialSecurityCompanies", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "社ä¿å计", |
| | | prop: "socialSecurityTotal", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "å
¬ç§¯é个人", |
| | | prop: "providentFundIndividuals", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "å
¬ç§¯éå
¬å¸", |
| | | prop: "providentFundCompany", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "å
¬ç§¯éå计", |
| | | prop: "providentFundTotal", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "åºç¨å·¥èµ", |
| | | prop: "taxableWaget", |
| | | }, |
| | | { |
| | | label: "个人æå¾ç¨", |
| | | prop: "personalIncomeTax", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "å®åå·¥èµ", |
| | | prop: "actualWages", |
| | | width: 120 |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: 'right', |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | const handleDateChange = (value,type) => { |
| | | searchForm.value.entryDateEnd = null |
| | | searchForm.value.entryDateStart = null |
| | | if(type === 1){ |
| | | if (value) { |
| | | searchForm.value.entryDateStart = dayjs(value).format("YYYY-MM-DD"); |
| | | } |
| | | }else{ |
| | | if (value) { |
| | | searchForm.value.entryDateEnd = dayjs(value).format("YYYY-MM-DD"); |
| | | } |
| | | } |
| | | getList(); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | compensationListPage({...page, ...searchForm.value, staffState: 1}).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = (selection) => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | |
| | | // å é¤ |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("è¯·éæ©æ°æ®"); |
| | | return; |
| | | } |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | compensationDelete(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/staff/staffJoinLeaveRecord/export", {staffState: 1}, "人åå
¥è.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped></style> |