From f139849fb65e7264d173bc46f45d70409e559504 Mon Sep 17 00:00:00 2001
From: maven <2163098428@qq.com>
Date: 星期五, 08 八月 2025 15:26:59 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_ai' into dev_ai
---
src/views/personnelManagement/payrollManagement/components/formDia.vue | 315 +++++++++++++
src/views/personnelManagement/payrollManagement/index.vue | 291 ++++++++++++
src/assets/icons/png/2.png | 0
src/assets/icons/png/收入金额.png | 0
src/assets/icons/png/支出.png | 0
src/api/financialManagement/financialStatements.js | 31 +
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue | 127 +++++
src/api/personnelManagement/payrollManagement.js | 35 +
src/assets/icons/png/收入收款.png | 0
src/components/Echarts/echarts.vue | 9
src/assets/icons/png/5.png | 0
src/assets/icons/png/1.png | 0
src/views/equipmentManagement/ledger/index.vue | 73 ++
src/views/financialManagement/financialStatements/index.vue | 511 +++++++++++++++++++++
src/assets/icons/png/4.png | 0
src/assets/icons/png/支出金额.png | 0
src/assets/fonts/font.css | 7
src/assets/styles/element-ui.scss | 2
src/views/financialManagement/revenueManagement/index.vue | 2
src/assets/fonts/DIN Alternate Bold.ttf | 0
src/views/financialManagement/expenseManagement/index.vue | 2
src/main.js | 1
src/assets/icons/png/3.png | 0
src/assets/icons/png/收入列帐.png | 0
package.json | 2
25 files changed, 1,373 insertions(+), 35 deletions(-)
diff --git a/package.json b/package.json
index 15334fa..2ffdd34 100644
--- a/package.json
+++ b/package.json
@@ -33,10 +33,12 @@
"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"
},
diff --git a/src/api/financialManagement/financialStatements.js b/src/api/financialManagement/financialStatements.js
new file mode 100644
index 0000000..537d36f
--- /dev/null
+++ b/src/api/financialManagement/financialStatements.js
@@ -0,0 +1,31 @@
+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,
+ });
+};
diff --git a/src/api/personnelManagement/payrollManagement.js b/src/api/personnelManagement/payrollManagement.js
new file mode 100644
index 0000000..c29a6b1
--- /dev/null
+++ b/src/api/personnelManagement/payrollManagement.js
@@ -0,0 +1,35 @@
+// 钖叕绠$悊
+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,
+ });
+}
\ No newline at end of file
diff --git a/src/assets/fonts/DIN Alternate Bold.ttf b/src/assets/fonts/DIN Alternate Bold.ttf
new file mode 100644
index 0000000..81f2f7a
--- /dev/null
+++ b/src/assets/fonts/DIN Alternate Bold.ttf
Binary files differ
diff --git a/src/assets/fonts/font.css b/src/assets/fonts/font.css
new file mode 100644
index 0000000..1a3894a
--- /dev/null
+++ b/src/assets/fonts/font.css
@@ -0,0 +1,7 @@
+@font-face {
+ font-family: "MyCustomFont";
+ src: url("./DIN Alternate Bold.ttf") format("truetype");
+ font-weight: 700; /* 绮椾綋 */
+ font-style: normal;
+ font-display: swap;
+}
diff --git a/src/assets/icons/png/1.png b/src/assets/icons/png/1.png
new file mode 100644
index 0000000..1acfa67
--- /dev/null
+++ b/src/assets/icons/png/1.png
Binary files differ
diff --git a/src/assets/icons/png/2.png b/src/assets/icons/png/2.png
new file mode 100644
index 0000000..cebdf2c
--- /dev/null
+++ b/src/assets/icons/png/2.png
Binary files differ
diff --git a/src/assets/icons/png/3.png b/src/assets/icons/png/3.png
new file mode 100644
index 0000000..719912b
--- /dev/null
+++ b/src/assets/icons/png/3.png
Binary files differ
diff --git a/src/assets/icons/png/4.png b/src/assets/icons/png/4.png
new file mode 100644
index 0000000..b5f5861
--- /dev/null
+++ b/src/assets/icons/png/4.png
Binary files differ
diff --git a/src/assets/icons/png/5.png b/src/assets/icons/png/5.png
new file mode 100644
index 0000000..5467146
--- /dev/null
+++ b/src/assets/icons/png/5.png
Binary files differ
diff --git "a/src/assets/icons/png/\346\224\257\345\207\272.png" "b/src/assets/icons/png/\346\224\257\345\207\272.png"
new file mode 100644
index 0000000..fc253ae
--- /dev/null
+++ "b/src/assets/icons/png/\346\224\257\345\207\272.png"
Binary files differ
diff --git "a/src/assets/icons/png/\346\224\257\345\207\272\351\207\221\351\242\235.png" "b/src/assets/icons/png/\346\224\257\345\207\272\351\207\221\351\242\235.png"
new file mode 100644
index 0000000..b0db95a
--- /dev/null
+++ "b/src/assets/icons/png/\346\224\257\345\207\272\351\207\221\351\242\235.png"
Binary files differ
diff --git "a/src/assets/icons/png/\346\224\266\345\205\245\345\210\227\345\270\220.png" "b/src/assets/icons/png/\346\224\266\345\205\245\345\210\227\345\270\220.png"
new file mode 100644
index 0000000..782bd2f
--- /dev/null
+++ "b/src/assets/icons/png/\346\224\266\345\205\245\345\210\227\345\270\220.png"
Binary files differ
diff --git "a/src/assets/icons/png/\346\224\266\345\205\245\346\224\266\346\254\276.png" "b/src/assets/icons/png/\346\224\266\345\205\245\346\224\266\346\254\276.png"
new file mode 100644
index 0000000..a1d3272
--- /dev/null
+++ "b/src/assets/icons/png/\346\224\266\345\205\245\346\224\266\346\254\276.png"
Binary files differ
diff --git "a/src/assets/icons/png/\346\224\266\345\205\245\351\207\221\351\242\235.png" "b/src/assets/icons/png/\346\224\266\345\205\245\351\207\221\351\242\235.png"
new file mode 100644
index 0000000..b83863a
--- /dev/null
+++ "b/src/assets/icons/png/\346\224\266\345\205\245\351\207\221\351\242\235.png"
Binary files differ
diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss
index 4eae8b2..8c741af 100644
--- a/src/assets/styles/element-ui.scss
+++ b/src/assets/styles/element-ui.scss
@@ -67,7 +67,7 @@
}
.el-dialog__body {
padding: 16px 40px 0 40px;
- max-height: 90vh;
+ max-height: 74vh;
overflow-y: auto;
}
.el-dialog__footer {
diff --git a/src/components/Echarts/echarts.vue b/src/components/Echarts/echarts.vue
index 2c51ace..d8264ad 100644
--- a/src/components/Echarts/echarts.vue
+++ b/src/components/Echarts/echarts.vue
@@ -1,6 +1,7 @@
<template>
- <div>
+ <div style="position: relative;">
<div ref="chartRef" :style="chartStyle"></div>
+ <slot></slot>
</div>
</template>
@@ -89,7 +90,7 @@
// 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) => {
@@ -118,7 +119,7 @@
series: props.series,
grid: props.grid,
legend: props.legend,
- tooltip: props.tooltip
+ tooltip: props.tooltip,
}
chartInstance.clear()
@@ -147,7 +148,7 @@
// Watch all reactive props that affect the chart
watch(
- () => [props.xAxis, props.series],
+ () => [props.xAxis, props.series, props.legend, props.tooltip],
() => {
if (chartInstance) {
renderChart()
diff --git a/src/main.js b/src/main.js
index 014954c..00205af 100644
--- a/src/main.js
+++ b/src/main.js
@@ -22,6 +22,7 @@
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
diff --git a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
index 847f4c4..9b92361 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -102,6 +102,10 @@
<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'"
@@ -117,16 +121,33 @@
<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>
+ <!-- 鐢靛瓙绛惧悕寮圭獥锛坴ue3-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,
@@ -135,6 +156,7 @@
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()
@@ -156,6 +178,22 @@
},
});
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 '缁撴潫';
@@ -190,6 +228,11 @@
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) {
@@ -205,17 +248,84 @@
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);
+ // 灏哹ase64杞崲涓轰簩杩涘埗
+ 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();
+ // 鍙湁閫氳繃鏃舵墠浼犻�掔鍚嶆枃浠禝D
+ 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");
@@ -253,4 +363,9 @@
height: 30px;
border-radius: 50px;
}
+.signImg {
+ cursor: pointer;
+ width: 200px;
+ height: 60px;
+}
</style>
\ No newline at end of file
diff --git a/src/views/equipmentManagement/ledger/index.vue b/src/views/equipmentManagement/ledger/index.vue
index ba17f7e..16fbbc6 100644
--- a/src/views/equipmentManagement/ledger/index.vue
+++ b/src/views/equipmentManagement/ledger/index.vue
@@ -79,22 +79,17 @@
@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>
@@ -106,6 +101,8 @@
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: "璁惧鍙拌处",
@@ -115,6 +112,10 @@
const multipleList = ref([]);
const { proxy } = getCurrentInstance();
const modalRef = ref();
+const qrDialogVisible = ref(false);
+const qrCodeUrl = ref("");
+const qrRowData = ref(null);
+
const {
filters,
columns,
@@ -184,14 +185,29 @@
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)
+ },
+ },
+ ],
+ },
]
);
@@ -253,6 +269,21 @@
});
};
+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"),
diff --git a/src/views/financialManagement/expenseManagement/index.vue b/src/views/financialManagement/expenseManagement/index.vue
index 0c6cbde..32d906b 100644
--- a/src/views/financialManagement/expenseManagement/index.vue
+++ b/src/views/financialManagement/expenseManagement/index.vue
@@ -69,7 +69,7 @@
</PIMTable>
</div>
<Modal ref="modalRef" @success="getTableData"></Modal>
- <files-dia ref="filesDia" @close="handleQuery"></files-dia>
+ <files-dia ref="filesDia"></files-dia>
</div>
</template>
diff --git a/src/views/financialManagement/financialStatements/index.vue b/src/views/financialManagement/financialStatements/index.vue
index c272707..430fe6a 100644
--- a/src/views/financialManagement/financialStatements/index.vue
+++ b/src/views/financialManagement/financialStatements/index.vue
@@ -1,4 +1,513 @@
<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>
\ No newline at end of file
+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>
diff --git a/src/views/financialManagement/revenueManagement/index.vue b/src/views/financialManagement/revenueManagement/index.vue
index 984d9de..35f3075 100644
--- a/src/views/financialManagement/revenueManagement/index.vue
+++ b/src/views/financialManagement/revenueManagement/index.vue
@@ -69,7 +69,7 @@
</PIMTable>
</div>
<Modal ref="modalRef" @success="getTableData"></Modal>
- <files-dia ref="filesDia" @close="handleQuery"></files-dia>
+ <files-dia ref="filesDia"></files-dia>
</div>
</template>
diff --git a/src/views/personnelManagement/payrollManagement/components/formDia.vue b/src/views/personnelManagement/payrollManagement/components/formDia.vue
new file mode 100644
index 0000000..6dbc326
--- /dev/null
+++ b/src/views/personnelManagement/payrollManagement/components/formDia.vue
@@ -0,0 +1,315 @@
+<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="鐥呭亣鎵f锛�" 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="浜嬪亣鎵f锛�" 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="蹇樿鎵撳崱鎵f锛�" 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>
\ No newline at end of file
diff --git a/src/views/personnelManagement/payrollManagement/index.vue b/src/views/personnelManagement/payrollManagement/index.vue
new file mode 100644
index 0000000..1de23e4
--- /dev/null
+++ b/src/views/personnelManagement/payrollManagement/index.vue
@@ -0,0 +1,291 @@
+<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: "鐥呭亣鎵f",
+ prop: "sickLeaveDeductions",
+ width:100
+ },
+ {
+ label: "浜嬪亣鎵f",
+ prop: "deductionPersonalLeave",
+ width:100
+ },
+ {
+ label: "蹇樿鎵撳崱鎵f",
+ 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>
--
Gitblit v1.9.3