From 1ed366885433dfdec1241312356535b868c39eee Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 26 二月 2026 16:21:04 +0800
Subject: [PATCH] 合同管理模块开发
---
src/pages/index.vue | 10
src/pages.json | 7
src/api/humanResources/contractManagement.js | 16 +
src/pages/humanResources/contractManagement/index.vue | 630 ++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 663 insertions(+), 0 deletions(-)
diff --git a/src/api/humanResources/contractManagement.js b/src/api/humanResources/contractManagement.js
new file mode 100644
index 0000000..5555fe7
--- /dev/null
+++ b/src/api/humanResources/contractManagement.js
@@ -0,0 +1,16 @@
+import request from '@/utils/request'
+// 鏌ヨ鍦ㄨ亴鍛樺伐鍙拌处
+export function staffOnJobListPage(query) {
+ return request({
+ url: '/staff/staffOnJob/listPage',
+ method: 'get',
+ params: query,
+ })
+}
+export function findStaffContractListPage(query) {
+ return request({
+ url: "/staff/staffContract/listPage",
+ method: "get",
+ params: query,
+ });
+}
diff --git a/src/pages.json b/src/pages.json
index 0772b2e..c39d3b9 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -886,6 +886,13 @@
}
},
{
+ "path": "pages/humanResources/contractManagement/index",
+ "style": {
+ "navigationBarTitleText": "鍚堝悓绠$悊",
+ "navigationStyle": "custom"
+ }
+ },
+ {
"path": "pages/message",
"style": {
"navigationBarTitleText": "娑堟伅涓績"
diff --git a/src/pages/humanResources/contractManagement/index.vue b/src/pages/humanResources/contractManagement/index.vue
new file mode 100644
index 0000000..b036ed6
--- /dev/null
+++ b/src/pages/humanResources/contractManagement/index.vue
@@ -0,0 +1,630 @@
+<template>
+ <view class="contract-management-page">
+ <PageHeader title="鍚堝悓绠$悊"
+ @back="goBack" />
+ <!-- 鎼滅储鏍� -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ュ憳宸ュ鍚嶆悳绱�"
+ v-model="searchValue"
+ @blur="handleSearch"
+ @change="handleSearch"
+ clearable />
+ </view>
+ <view class="filter-button"
+ @click="handleSearch">
+ <u-icon name="search"
+ size="24"
+ color="#999"></u-icon>
+ </view>
+ </view>
+ </view>
+ <!-- 鍚堝悓淇℃伅鍗$墖 -->
+ <view class="contract-card">
+ <!-- 寰幆灞曠ず鐢ㄦ埛鍒楄〃 -->
+ <view v-for="(userItem, index) in userList"
+ :key="index"
+ class="user-card">
+ <!-- 鍩烘湰淇℃伅鍗$墖 -->
+ <view class="basic-info-card">
+ <view class="card-header">
+ <text class="card-title">{{ userItem.staffName || '-' }}</text>
+ <text class="card-subtitle"><u-tag :type="userItem.staffState == '1' ? 'primary' : 'error'"
+ :text="userItem.staffState == '1' ? ' 鍦ㄨ亴 ' : ' 绂昏亴 '"></u-tag></text>
+ </view>
+ <view class="card-content">
+ <view class="info-row">
+ <view class="info-item">
+ <text class="info-label">鍛樺伐缂栧彿</text>
+ <text class="info-value">{{ userItem.staffNo || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鎬у埆</text>
+ <text class="info-value">{{ userItem.sex || '-' }}</text>
+ </view>
+ </view>
+ <view class="info-row">
+ <view class="info-item">
+ <text class="info-label">宀椾綅</text>
+ <text class="info-value">{{ userItem.postJob || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">绗竴瀛﹀巻</text>
+ <text class="info-value">{{ userItem.firstStudy || '-' }}</text>
+ </view>
+ </view>
+ <view class="info-row">
+ <view class="info-item">
+ <text class="info-label">涓撲笟</text>
+ <text class="info-value">{{ userItem.profession || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">骞撮緞</text>
+ <text class="info-value">{{ userItem.age || '-' }}</text>
+ </view>
+ </view>
+ <view class="info-row">
+ <view class="info-item">
+ <text class="info-label">鑱旂郴鐢佃瘽</text>
+ <text class="info-value">{{ userItem.phone || '-' }}</text>
+ </view>
+ </view>
+ <!-- <view class="info-row">
+ <view class="info-item">
+ <text class="info-label">绱ф�ヨ仈绯讳汉</text>
+ <text class="info-value">{{ userItem.emergencyContact || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">绱ф�ヨ仈绯讳汉鐢佃瘽</text>
+ <text class="info-value">{{ userItem.emergencyContactPhone || '-' }}</text>
+ </view>
+ </view> -->
+ <!-- <view class="info-row">
+ <view class="info-item">
+ <text class="info-label">鎴风睄浣忓潃</text>
+ <text class="info-value">{{ userItem.nativePlace || '-' }}</text>
+ </view>
+ </view> -->
+ <view class="info-row">
+ <view class="info-item">
+ <text class="info-label">鐜颁綇鍧�</text>
+ <text class="info-value">{{ userItem.adress || '-' }}</text>
+ </view>
+ </view>
+ </view>
+ </view>
+ <!-- 鍚堝悓鍒楄〃鍗$墖 -->
+ <view class="contract-list-card">
+ <view class="card-header">
+ <up-button size="small"
+ :type="userItem.contractExpanded ? 'default' : 'primary'"
+ :loading="userItem.contractLoading"
+ @click="toggleContractExpanded(index)">
+ {{ userItem.contractExpanded ? '闅愯棌鍚堝悓鍒楄〃' : '鍚堝悓鍒楄〃' }}<u-icon v-if="userItem.contractExpanded"
+ name="arrow-down"
+ size="12"
+ style="margin-left: 4px;"
+ color="#000"></u-icon>
+ </up-button>
+ </view>
+ <view v-if="(userItem.contractExpanded || userItem.contractLoading) && userItem.contractList.length > 0"
+ class="card-content">
+ <view v-if="userItem.contractLoading"
+ class="list-loading">
+ <u-icon name="loading"
+ size="24"
+ color="#348fe2"></u-icon>
+ <text>鍔犺浇涓�...</text>
+ </view>
+ <template v-else>
+ <view v-for="contract in userItem.contractList"
+ :key="contract.id"
+ class="contract-item">
+ <view class="contract-item-header">
+ <text class="contract-name">{{ contract.contractName }}</text>
+ <view class="contract-status"
+ :class="getContractStatusClass(contract.status)">
+ <text>{{ contract.status }}</text>
+ </view>
+ </view>
+ <view class="contract-item-content">
+ <view class="contract-detail">
+ <text class="detail-label">鍚堝悓骞撮檺锛�</text>
+ <text class="detail-value">{{ contract.contractTerm || '-' }}</text>
+ </view>
+ <view class="contract-detail">
+ <text class="detail-label">鍚堝悓寮�濮嬫棩鏈燂細</text>
+ <text class="detail-value">{{ contract.contractStartTime || '-' }}</text>
+ </view>
+ <view class="contract-detail">
+ <text class="detail-label">鍚堝悓缁撴潫鏃ユ湡锛�</text>
+ <text class="detail-value">{{ contract.contractEndTime || '-' }}</text>
+ </view>
+ </view>
+ </view>
+ </template>
+ </view>
+ </view>
+ </view>
+ <!-- 绌虹姸鎬� -->
+ <view v-if="!loading && userList.length === 0"
+ class="empty-state">
+ <u-icon name="document"
+ size="60"
+ color="#999"></u-icon>
+ <text class="empty-text">鏆傛棤鍚堝悓淇℃伅</text>
+ </view>
+ </view>
+ <!-- 鍔犺浇鐘舵�� -->
+ <view v-if="loading"
+ class="loading-state">
+ <u-icon name="loading"
+ size="40"
+ color="#348fe2"></u-icon>
+ <text class="loading-text">鍔犺浇涓�...</text>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, onMounted } from "vue";
+ import PageHeader from "@/components/PageHeader.vue";
+ import useUserStore from "@/store/modules/user";
+ import {
+ staffOnJobListPage,
+ findStaffContractListPage,
+ } from "@/api/humanResources/contractManagement";
+
+ // 鐢ㄦ埛鍚堝悓淇℃伅鍒楄〃
+ const userList = ref([]);
+
+ // 鍔犺浇鐘舵��
+ const loading = ref(false);
+
+ // 鎼滅储鍊�
+ const searchValue = ref("");
+
+ // 鏂囦欢鍒楄〃
+ const fileList = ref([
+ {
+ id: 1,
+ fileName: "鍔冲姩鍚堝悓.pdf",
+ fileSize: "2.5MB",
+ uploadDate: "2026-02-01",
+ },
+ {
+ id: 2,
+ fileName: "淇濆瘑鍗忚.pdf",
+ fileSize: "1.8MB",
+ uploadDate: "2026-02-01",
+ },
+ {
+ id: 3,
+ fileName: "绔炰笟闄愬埗鍗忚.pdf",
+ fileSize: "1.2MB",
+ uploadDate: "2026-02-01",
+ },
+ ]);
+
+ // 鐢ㄦ埛瀛樺偍
+ const userStore = useUserStore();
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ // 鑾峰彇鐢ㄦ埛鍚堝悓淇℃伅
+ const getUserContractInfo = (searchName = "") => {
+ loading.value = true;
+ const params = {
+ current: -1,
+ size: -1,
+ staffName: searchName || "",
+ };
+
+ staffOnJobListPage(params)
+ .then(response => {
+ // 涓烘瘡涓敤鎴锋坊鍔燾ontractList鍜宑ontractLoading灞炴��
+ userList.value = response.data.records.map(user => ({
+ ...user,
+ contractList: [],
+ contractLoading: false,
+ contractExpanded: false,
+ }));
+ })
+ .catch(error => {
+ console.error("鑾峰彇鍚堝悓淇℃伅澶辫触:", error);
+ })
+ .finally(() => {
+ loading.value = false;
+ });
+ };
+
+ // 鍔犺浇鍚堝悓鍒楄〃
+ const loadContractList = (id, index) => {
+ // 妫�鏌ョ敤鎴锋槸鍚﹀瓨鍦�
+ console.log(userList.value[index], "userList.value[index]");
+ if (!userList.value[index]) return;
+
+ const userItem = userList.value[index];
+ console.log(userItem.contractList.length, "userItem.contractList.length");
+ // 濡傛灉宸茬粡鍔犺浇杩囷紝涓嶅啀閲嶅鍔犺浇
+ if (userItem.contractList.length > 0) return;
+
+ // 璁剧疆鍔犺浇鐘舵��
+ userItem.contractLoading = true;
+
+ // 璋冪敤璇︽儏鎺ュ彛
+ findStaffContractListPage({ staffOnJobId: id })
+ .then(res => {
+ if (res.data && res.data.records) {
+ userItem.contractList = res.data.records;
+ } else {
+ userItem.contractList = [];
+ }
+ })
+ .finally(() => {
+ // 閲嶇疆鍔犺浇鐘舵��
+ userItem.contractLoading = false;
+ });
+ };
+
+ // 鑾峰彇鍚堝悓鐘舵�佹牱寮�
+ const getContractStatusClass = status => {
+ switch (status) {
+ case "鐢熸晥涓�":
+ return "status-active";
+ case "宸茶繃鏈�":
+ return "status-expired";
+ case "宸茬粓姝�":
+ return "status-terminated";
+ default:
+ return "status-default";
+ }
+ };
+
+ // 鍒囨崲鍚堝悓璇︽儏灞曞紑/鍏抽棴鐘舵��
+ const toggleContractExpanded = index => {
+ if (!userList.value[index]) return;
+
+ const userItem = userList.value[index];
+
+ // 濡傛灉杩樻病鏈夊姞杞藉悎鍚屽垪琛紝鍏堝姞杞芥暟鎹�
+ if (userItem.contractList.length === 0) {
+ loadContractList(userItem.id, index);
+ // 鍔犺浇瀹屾垚鍚庤嚜鍔ㄥ睍寮�
+ userItem.contractExpanded = true;
+ } else {
+ // 鍒囨崲灞曞紑/鍏抽棴鐘舵��
+ userItem.contractExpanded = !userItem.contractExpanded;
+ }
+ };
+
+ // 澶勭悊鎼滅储
+ const handleSearch = () => {
+ getUserContractInfo(searchValue.value);
+ };
+
+ // 椤甸潰鍔犺浇鏃惰幏鍙栧悎鍚屼俊鎭�
+ onMounted(() => {
+ getUserContractInfo("");
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "../../../styles/sales-common.scss";
+ // 鍏ㄥ眬鍙橀噺
+ $primary-color: #2c7be5;
+ $primary-light: #4a90e2;
+ $primary-lightest: #e8f0fe;
+ $text-primary: #333333;
+ $text-secondary: #666666;
+ $text-tertiary: #999999;
+ $bg-color: #f8f9fa;
+ $card-bg: #ffffff;
+ $border-color: #e8e8e8;
+ $shadow-sm: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
+ $shadow-md: 0 4rpx 16rpx rgba(0, 0, 0, 0.12);
+ $border-radius: 16rpx;
+
+ .contract-management-page {
+ min-height: 100vh;
+ background-color: $bg-color;
+ padding-bottom: 40rpx;
+ }
+
+ /* 鍚堝悓鍗$墖 */
+ .contract-card {
+ margin: 24rpx;
+ display: flex;
+ flex-direction: column;
+ gap: 24rpx;
+ }
+
+ /* 鍩烘湰淇℃伅鍗$墖 */
+ .basic-info-card {
+ background-color: $card-bg;
+ border-radius: $border-radius $border-radius 0 0;
+ box-shadow: $shadow-md;
+ overflow: hidden;
+ transition: all 0.3s ease;
+ }
+
+ /* 鍚堝悓鍒楄〃鍗$墖 */
+ .contract-list-card {
+ background-color: $card-bg;
+ border-radius: 0 0 $border-radius $border-radius;
+ box-shadow: $shadow-md;
+ overflow: hidden;
+ border-top: 1rpx solid $border-color;
+ transition: all 0.3s ease;
+ }
+
+ /* 鐢ㄦ埛鍗$墖 */
+ .user-card {
+ margin-bottom: 0;
+ border-radius: $border-radius;
+ overflow: hidden;
+ box-shadow: $shadow-md;
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+ }
+
+ .user-card:hover {
+ transform: translateY(-4rpx);
+ box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
+ }
+
+ /* 鍗$墖澶撮儴 */
+ .card-header {
+ padding: 24rpx;
+ border-bottom: 1rpx solid $border-color;
+ background-color: $primary-lightest;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .card-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: $primary-color;
+ }
+
+ .card-subtitle {
+ font-size: 13px;
+ color: $text-secondary;
+ padding: 4rpx 12rpx;
+ border-radius: 12rpx;
+ }
+
+ /* 鍗$墖鍐呭 */
+ .card-content {
+ padding: 24rpx;
+ }
+
+ /* 淇℃伅琛� */
+ .info-row {
+ display: flex;
+ margin-bottom: 20rpx;
+ gap: 20rpx;
+ }
+
+ .info-row:last-child {
+ margin-bottom: 0;
+ }
+
+ /* 淇℃伅椤� */
+ .info-item {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ padding: 12rpx;
+ background-color: #f9fafb;
+ border-radius: 12rpx;
+ border: 1rpx solid $border-color;
+ }
+
+ /* 淇℃伅鏍囩 */
+ .info-label {
+ font-size: 12px;
+ color: $text-secondary;
+ margin-bottom: 6rpx;
+ font-weight: 500;
+ }
+
+ /* 淇℃伅鍊� */
+ .info-value {
+ font-size: 14px;
+ color: $text-primary;
+ font-weight: 500;
+ line-height: 1.4;
+ }
+
+ /* 鏂囦欢琛ㄦ牸 */
+ .file-table {
+ width: 100%;
+ }
+
+ /* 琛ㄦ牸澶撮儴 */
+ .table-header {
+ display: flex;
+ padding: 12rpx 0;
+ border-bottom: 1rpx solid $border-color;
+ font-weight: 600;
+ font-size: 13px;
+ color: $text-secondary;
+ }
+
+ /* 琛ㄦ牸琛� */
+ .table-row {
+ display: flex;
+ padding: 16rpx 0;
+ border-bottom: 1rpx solid $border-color;
+ font-size: 13px;
+ color: $text-primary;
+ }
+
+ .table-row:last-child {
+ border-bottom: none;
+ }
+
+ /* 琛ㄦ牸鍒� */
+ .table-col {
+ flex: 1;
+ text-align: center;
+ }
+
+ /* 鏂囦欢鍚嶇О鍒� */
+ .file-name {
+ flex: 2;
+ text-align: left;
+ }
+
+ /* 琛ㄦ牸绌虹姸鎬� */
+ .table-empty {
+ padding: 40rpx 0;
+ text-align: center;
+ color: $text-tertiary;
+ font-size: 13px;
+ }
+
+ /* 鍚堝悓鍒楄〃 */
+ .contract-list {
+ padding: 16rpx;
+ }
+
+ /* 鍚堝悓椤� */
+ .contract-item {
+ background-color: #f9fafb;
+ border-radius: 12rpx;
+ padding: 16rpx;
+
+ border-left: 4px solid $primary-color;
+ }
+
+ .contract-item:last-child {
+ margin-bottom: 0;
+ }
+
+ /* 鍚堝悓椤瑰ご閮� */
+ .contract-item-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12rpx;
+ }
+
+ /* 鍚堝悓鍚嶇О */
+ .contract-name {
+ font-size: 14px;
+ font-weight: 600;
+ color: $text-primary;
+ }
+
+ /* 鍚堝悓鐘舵�� */
+ .contract-status {
+ padding: 4rpx 12rpx;
+ border-radius: 12rpx;
+ font-size: 12px;
+ font-weight: 500;
+ }
+
+ /* 鍚堝悓鐘舵�佹牱寮� */
+ .status-active {
+ background-color: #e6f7ee;
+ color: #389e75;
+ }
+
+ .status-expired {
+ background-color: #fff5f5;
+ color: #dc3545;
+ }
+
+ .status-terminated {
+ background-color: #f8f9fa;
+ color: #6c757d;
+ }
+
+ .status-default {
+ background-color: #e9ecef;
+ color: #495057;
+ }
+
+ /* 鍚堝悓椤瑰唴瀹� */
+ .contract-item-content {
+ display: flex;
+ flex-direction: column;
+ gap: 8rpx;
+ }
+
+ /* 鍚堝悓璇︽儏 */
+ .contract-detail {
+ display: flex;
+ font-size: 13px;
+ }
+
+ /* 璇︽儏鏍囩 */
+ .detail-label {
+ color: $text-secondary;
+ margin-right: 8rpx;
+ }
+
+ /* 璇︽儏鍊� */
+ .detail-value {
+ color: $text-primary;
+ flex: 1;
+ }
+
+ /* 鍒楄〃绌虹姸鎬� */
+ .list-empty {
+ padding: 40rpx 0;
+ text-align: center;
+ color: $text-tertiary;
+ font-size: 13px;
+ }
+
+ /* 鍒楄〃鍔犺浇鐘舵�� */
+ .list-loading {
+ padding: 40rpx 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12rpx;
+ color: $text-secondary;
+ font-size: 13px;
+ }
+
+ /* 鍔犺浇鐘舵�� */
+ .loading-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 100rpx 0;
+ }
+
+ .loading-text {
+ font-size: 14px;
+ color: $text-tertiary;
+ margin-top: 20rpx;
+ }
+
+ /* 绌虹姸鎬� */
+ .empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 120rpx 0;
+ text-align: center;
+ }
+
+ .empty-text {
+ font-size: 14px;
+ color: $text-tertiary;
+ margin-top: 24rpx;
+ }
+</style>
\ No newline at end of file
diff --git a/src/pages/index.vue b/src/pages/index.vue
index 1b20903..d1f8f1a 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -309,6 +309,10 @@
icon: "/static/images/icon/caigoutaizhang@2x.png",
label: "浜哄憳钖祫",
},
+ {
+ icon: "/static/images/icon/caigoutaizhang@2x.png",
+ label: "鍚堝悓绠$悊",
+ },
]);
const safetyItems = reactive([
{
@@ -716,6 +720,11 @@
url: "/pages/humanResources/monthlyStatistics/index",
});
break;
+ case "鍚堝悓绠$悊":
+ uni.navigateTo({
+ url: "/pages/humanResources/contractManagement/index",
+ });
+ break;
default:
uni.showToast({
title: `鐐瑰嚮浜�${item.label}`,
@@ -981,6 +990,7 @@
const originalHumanResources = [
{ icon: "/static/images/icon/caigoutaizhang@2x.png", label: "鎵撳崱绛惧埌" },
{ icon: "/static/images/icon/caigoutaizhang@2x.png", label: "浜哄憳钖祫" },
+ { icon: "/static/images/icon/caigoutaizhang@2x.png", label: "鍚堝悓绠$悊" },
];
const filteredHumanResources = originalHumanResources.filter(item => {
return allowedMenuTitles.has(item.label);
--
Gitblit v1.9.3