已添加12个文件
已修改12个文件
已删除5个文件
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // è·åæå¡è§åå表 |
| | | export function getAttendanceRules(query) { |
| | | return request({ |
| | | url: "/personalAttendanceLocationConfig/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢æå¡è§å |
| | | export function addAttendanceRule(data) { |
| | | return request({ |
| | | url: "/personalAttendanceLocationConfig/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // æ´æ°æå¡è§å |
| | | export function updateAttendanceRule(data) { |
| | | return request({ |
| | | url: "/attendanceRules/update", |
| | | method: "put", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // å 餿å¡è§å |
| | | export function deleteAttendanceRule(ids) { |
| | | return request({ |
| | | url: `/personalAttendanceLocationConfig/del`, |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | } |
| | | |
| | | // è·åå个æå¡è§å详æ
|
| | | export function getAttendanceRuleDetail(id) { |
| | | return request({ |
| | | url: `/attendanceRules/detail/${id}`, |
| | | method: "get", |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request"; |
| | | |
| | | // 人åèªèµå°è´¦å表 |
| | | export function monthlyStatisticsListPage(query) { |
| | | return request({ |
| | | url: "/compensationPerformance/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | // 人åèªèµå°è´¦è¯¦æ
|
| | | export function monthlyStatisticsGet(id) { |
| | | return request({ |
| | | url: "/monthlyStatistics/get", |
| | | method: "get", |
| | | params: { id }, |
| | | }); |
| | | } |
| | | |
| | | // æ°å¢äººåèªèµå°è´¦ |
| | | export function monthlyStatisticsAdd(data) { |
| | | return request({ |
| | | url: "/compensationPerformance/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // ç¼è¾äººåèªèµå°è´¦ |
| | | export function monthlyStatisticsUpdate(data) { |
| | | return request({ |
| | | url: "/compensationPerformance/update", |
| | | method: "post", |
| | | data, |
| | | }); |
| | | } |
| | | |
| | | // å é¤äººåèªèµå°è´¦ |
| | | export function monthlyStatisticsDelete(ids) { |
| | | return request({ |
| | | url: "/compensationPerformance/delete", |
| | | method: "delete", |
| | | data: ids, |
| | | }); |
| | | } |
| | | |
| | | // 导åºäººåèªèµå°è´¦ |
| | | export function monthlyStatisticsExport(query) { |
| | | return request({ |
| | | url: "/compensationPerformance/export", |
| | | method: "get", |
| | | params: query, |
| | | responseType: "blob", |
| | | }); |
| | | } |
| | | |
| | | // 人åå表 |
| | | export function staffOnJobList(query) { |
| | | return request({ |
| | | url: "/staff/staffOnJob/list", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request.js"; |
| | | |
| | | export function createPersonalAttendanceRecord(params) { |
| | | return request({ |
| | | url: "/personalAttendanceRecords", |
| | | method: "post", |
| | | data: params, |
| | | }); |
| | | } |
| | | |
| | | export function findPersonalAttendanceRecords(query) { |
| | | return request({ |
| | | url: "/personalAttendanceRecords/listPage", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | |
| | | export function findTodayPersonalAttendanceRecord(query) { |
| | | return request({ |
| | | url: "/personalAttendanceRecords/today", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import request from "@/utils/request.js"; |
| | | |
| | | // 离èåå åæ |
| | | export function findStaffLeaveReasonAnalysis() { |
| | | return request({ |
| | | url: "/staff/analytics/reason", |
| | | method: "get" |
| | | }); |
| | | } |
| | | |
| | | // 12个æåå·¥æµå¨æµå¤±çåæ |
| | | export function findStaffAnalysisMonthlyTurnoverRateFor12Months() { |
| | | return request({ |
| | | url: "/staff/analytics/monthly_turnover_rate", |
| | | method: "get" |
| | | }); |
| | | } |
| | | |
| | | export function findStaffAnalysisTotalStatistic() { |
| | | return request({ |
| | | url: "/staff/analytics/total_statistic", |
| | | method: "get" |
| | | }); |
| | | } |
| | | |
| | | |
| | |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢åå·¥å
¥èä¿¡æ¯ |
| | | export function getStaffOnJobInfoByUserName(query) { |
| | | return request({ |
| | | url: '/staff/staffOnJob/byUserName', |
| | | method: 'get', |
| | | params: query, |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢åå·¥ |
| | | export function createStaffOnJob(params) { |
| | | return request({ |
| | |
| | | <template> |
| | | <div class="app-container analytics-container"> |
| | | <div class="app-container analytics-container" v-loading="loading"> |
| | | |
| | | <!-- å
³é®ææ å¡ç --> |
| | | <el-row :gutter="20" class="metrics-cards"> |
| | |
| | | <span v-else>{{ item.value }}{{ item.unit }}</span> |
| | | </div> |
| | | <div class="card-label">{{ item.label }}</div> |
| | | <div class="card-trend" :class="item.trend > 0 ? 'positive' : 'negative'" v-if="item.showTrend !== false"> |
| | | <el-icon> |
| | | <component :is="item.trend > 0 ? 'ArrowUp' : 'ArrowDown'" /> |
| | | </el-icon> |
| | | {{ Math.abs(item.trend) }}% |
| | | </div> |
| | | <!-- <div class="card-trend" :class="item.trend > 0 ? 'positive' : 'negative'" v-if="item.showTrend !== false">--> |
| | | <!-- <el-icon>--> |
| | | <!-- <component :is="item.trend > 0 ? 'ArrowUp' : 'ArrowDown'" />--> |
| | | <!-- </el-icon>--> |
| | | <!-- {{ Math.abs(item.trend) }}%--> |
| | | <!-- </div>--> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | |
| | | |
| | | <!-- 第äºè¡å¾è¡¨ --> |
| | | <el-row :gutter="20" class="charts-section"> |
| | | <!-- ç¼å¶è¾¾æç --> |
| | | <el-col :span="12"> |
| | | <el-card class="chart-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>ç¼å¶è¾¾æç</span> |
| | | <el-tag type="warning">åé¨é¨å¯¹æ¯</el-tag> |
| | | </div> |
| | | </template> |
| | | <div class="chart-container"> |
| | | <div ref="staffingChartRef" class="chart"></div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <!-- åå·¥æµå¤±åå åæ --> |
| | | <el-col :span="12"> |
| | | <el-card class="chart-card"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, onUnmounted } from 'vue' |
| | | import { ref, onMounted, onUnmounted, nextTick } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { |
| | | Refresh, |
| | | User, |
| | | TrendCharts, |
| | | DataAnalysis, |
| | | PieChart, |
| | | ArrowUp, |
| | | ArrowDown |
| | | } from '@element-plus/icons-vue' |
| | | import * as echarts from 'echarts' |
| | | import { staffOnJobListPage } from '@/api/personnelManagement/employeeRecord.js' |
| | | import {listDept} from "@/api/system/dept.js"; |
| | | import { |
| | | findStaffAnalysisMonthlyTurnoverRateFor12Months, |
| | | findStaffLeaveReasonAnalysis, |
| | | findStaffAnalysisTotalStatistic |
| | | } from "@/api/personnelManagement/staffAnalytics.js"; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const loading = ref(false) |
| | |
| | | trend: 0 |
| | | }, |
| | | { |
| | | label: 'ç¼å¶è¾¾æç', |
| | | value: 0, |
| | | unit: '%', |
| | | icon: 'DataAnalysis', |
| | | type: 'success', |
| | | trend: 0 |
| | | }, |
| | | { |
| | | label: 'å¨èåå·¥æ°', |
| | | value: 0, |
| | | unit: '人', |
| | |
| | | |
| | | // é¨é¨æ°æ® |
| | | const departmentData = ref([]) |
| | | // åå·¥æµå¤±åå åææ°æ® |
| | | const staffLeaveReasons = ref([]) |
| | | // 12个æåå·¥æµå¨æµå¤±çåææ°æ® |
| | | const turnoverRateStatistics = ref([]) |
| | | |
| | | // è·åå¨èåå·¥æ° |
| | | const getStaffCount = async () => { |
| | | // è·åé¨é¨æ°æ® |
| | | const getDepartmentData = async () => { |
| | | try { |
| | | const res = await staffOnJobListPage({ staffState: 1, current: 1, size: 1 }) |
| | | const res = await listDept() |
| | | if (res && res.data) { |
| | | keyMetrics.value[3].value = res.data.total || 0 |
| | | departmentData.value = res.data |
| | | } |
| | | } catch (error) { |
| | | console.error('è·åå¨èåå·¥æ°å¤±è´¥:', error) |
| | | console.error('è·åé¨é¨æ°æ®å¤±è´¥:', error) |
| | | } |
| | | } |
| | | |
| | | const getStaffLeaveReasonAnalysis = async () => { |
| | | try { |
| | | const res = await findStaffLeaveReasonAnalysis() |
| | | if (res && res.data) { |
| | | staffLeaveReasons.value = res.data || [] |
| | | } |
| | | } catch (error) { |
| | | console.error('è·ååå·¥æµå¤±åå åæå¤±è´¥:', error) |
| | | } |
| | | } |
| | | |
| | | // ä¿®æ¹ä¸ºè¿åPromiseç弿¥å½æ° |
| | | const getMonthlyTurnoverRateFor12Months = async () => { |
| | | try { |
| | | const res = await findStaffAnalysisMonthlyTurnoverRateFor12Months() |
| | | if (res && res.data) { |
| | | turnoverRateStatistics.value = res.data || [] |
| | | } |
| | | } catch (error) { |
| | | console.error('è·å12个æåå·¥æµå¨æµå¤±çåææ°æ®å¤±è´¥:', error) |
| | | } |
| | | } |
| | | |
| | | const getStaffAnalysisTotalStatistic = async () => { |
| | | try { |
| | | const res = await findStaffAnalysisTotalStatistic() |
| | | if (res && res.data) { |
| | | keyMetrics.value[0].value = res.data.totalFlowRate || 0 |
| | | keyMetrics.value[1].value = res.data.totalTurnoverRate || 0 |
| | | keyMetrics.value[2].value = res.data.currentOnJobCount || 0 |
| | | } |
| | | } catch (error) { |
| | | console.error('è·åå工忿»ç»è®¡æ°æ®å¤±è´¥:', error) |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | // çææ¨¡ææ°æ® |
| | | const generateMockData = () => { |
| | | // çæå
³é®ææ æ°æ® |
| | | keyMetrics.value[0].value = (Math.random() * 5 + 2).toFixed(1) |
| | | keyMetrics.value[0].trend = (Math.random() * 3 - 1.5).toFixed(1) |
| | | |
| | | keyMetrics.value[1].value = (Math.random() * 3 + 1).toFixed(1) |
| | | keyMetrics.value[1].trend = (Math.random() * 2 - 1).toFixed(1) |
| | | |
| | | keyMetrics.value[2].value = (Math.random() * 15 + 85).toFixed(1) |
| | | keyMetrics.value[2].trend = (Math.random() * 3 - 1.5).toFixed(1) |
| | | |
| | | // çæé¨é¨æ°æ® |
| | | const departments = ['ææ¯é¨', 'éå®é¨', '人äºé¨', 'è´¢å¡é¨', 'ç产é¨', 'å¸åºé¨'] |
| | | departmentData.value = departments.map(dept => ({ |
| | | department: dept, |
| | | currentStaff: Math.floor(Math.random() * 30 + 20), |
| | | plannedStaff: Math.floor(Math.random() * 10 + 35), |
| | | staffingRate: Math.floor(Math.random() * 20 + 80), |
| | | turnoverRate: (Math.random() * 4 + 1).toFixed(1), |
| | | attritionRate: (Math.random() * 2 + 0.5).toFixed(1), |
| | | newHires: Math.floor(Math.random() * 5 + 1), |
| | | resignations: Math.floor(Math.random() * 3 + 1), |
| | | status: Math.random() > 0.7 ? 'å¼å¸¸' : 'æ£å¸¸' |
| | | })) |
| | | } |
| | | |
| | | // å·æ°æ°æ® |
| | | // ä¿®æ¹ä¸ºå¼æ¥å½æ°ï¼ç¡®ä¿æ°æ®å è½½å®æå忏²æå¾è¡¨ |
| | | const refreshData = async () => { |
| | | loading.value = true |
| | | try { |
| | | // 模æAPIè°ç¨å»¶è¿ |
| | | await new Promise(resolve => setTimeout(resolve, 500)) |
| | | loading.value = true |
| | | |
| | | generateMockData() |
| | | // çå¾
æææ°æ®å è½½å®æ |
| | | await Promise.all([ |
| | | getDepartmentData(), |
| | | getStaffLeaveReasonAnalysis(), |
| | | getMonthlyTurnoverRateFor12Months(), |
| | | getStaffAnalysisTotalStatistic() |
| | | ]) |
| | | |
| | | await nextTick() |
| | | renderAllCharts() |
| | | |
| | | if (!autoRefreshEnabled.value) { |
| | | ElMessage.success('æ°æ®å·æ°æå') |
| | | } |
| | | } catch (error) { |
| | | console.error('å·æ°æ°æ®å¤±è´¥:', error) |
| | | ElMessage.error('å·æ°æ°æ®å¤±è´¥') |
| | | console.error('æ°æ®å·æ°å¤±è´¥:', error) |
| | | ElMessage.error('æ°æ®å·æ°å¤±è´¥') |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | |
| | | attritionChart = echarts.init(attritionChartRef.value) |
| | | } |
| | | |
| | | renderAllCharts() |
| | | // åå§åæ¶ä¹å
å è½½æ°æ®å渲æå¾è¡¨ |
| | | refreshData() |
| | | }, 300) |
| | | } |
| | | |
| | |
| | | renderAttritionChart() |
| | | } |
| | | |
| | | // 渲æåå·¥æµå¨çè¶å¿å¾ |
| | | // ä¿®æ¹ä¸ºä½¿ç¨APIè¿åçå®é
æ°æ® |
| | | const renderTurnoverChart = () => { |
| | | if (!turnoverChart) return |
| | | |
| | | const months = ['1æ', '2æ', '3æ', '4æ', '5æ', '6æ', '7æ', '8æ', '9æ', '10æ', '11æ', '12æ'] |
| | | const turnoverData = months.map(() => (Math.random() * 5 + 2).toFixed(1)) |
| | | const attritionData = months.map(() => (Math.random() * 3 + 1).toFixed(1)) |
| | | // 使ç¨APIè¿åçå®é
æ°æ® |
| | | const months = turnoverRateStatistics.value.map(item => item.month) |
| | | const turnoverData = turnoverRateStatistics.value.map(item => item.flowRate || 0) |
| | | const attritionData = turnoverRateStatistics.value.map(item => item.turnoverRate || 0) |
| | | |
| | | const option = { |
| | | title: { |
| | |
| | | if (!departmentChart) return |
| | | |
| | | const data = departmentData.value.map(item => ({ |
| | | name: item.department, |
| | | value: item.currentStaff |
| | | name: item.deptName, |
| | | value: item.staffCount |
| | | })) |
| | | |
| | | const option = { |
| | |
| | | const renderStaffingChart = () => { |
| | | if (!staffingChart) return |
| | | |
| | | const departments = departmentData.value.map(item => item.department) |
| | | const departments = departmentData.value.map(item => item.deptName) |
| | | const rates = departmentData.value.map(item => item.staffingRate) |
| | | |
| | | const option = { |
| | |
| | | const renderAttritionChart = () => { |
| | | if (!attritionChart) return |
| | | |
| | | const reasons = ['èªèµå¾
é', 'èä¸åå±', 'å·¥ä½ç¯å¢', '个人åå ', 'å
¶ä»'] |
| | | const data = reasons.map(() => Math.floor(Math.random() * 20 + 5)) |
| | | const reasons = staffLeaveReasons.value.map(item => item.reasonText) |
| | | const data = staffLeaveReasons.value.map(item => item.count) |
| | | |
| | | const option = { |
| | | title: { |
| | |
| | | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | generateMockData() |
| | | getStaffCount() |
| | | initCharts() |
| | | startAutoRefresh() |
| | | }) |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="dialogTitle" |
| | | width="700px" |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="120px" |
| | | class="mt8"> |
| | | <!-- é¨é¨éæ© --> |
| | | <el-form-item label="é¨é¨" |
| | | prop="sysDeptId"> |
| | | <el-tree-select v-model="form.sysDeptId" |
| | | :data="deptOptions" |
| | | :props="{ value: 'id', label: 'label', children: 'children' }" |
| | | value-key="id" |
| | | placeholder="è¯·éæ©é¨é¨" |
| | | check-strictly |
| | | style="width: 100%" |
| | | :disabled="['edit', 'view'].includes(operationType)" /> |
| | | </el-form-item> |
| | | <!-- å°ç¹ä¿¡æ¯ --> |
| | | <!-- <el-form-item label="å°ç¹åç§°" |
| | | prop="locationName"> |
| | | <el-input v-model="form.locationName" |
| | | placeholder="请è¾å
¥å°ç¹åç§°" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> --> |
| | | <!-- æå¡èå´ --> |
| | | <el-form-item label="æå¡èå´(m)" |
| | | prop="radius"> |
| | | <el-input-number v-model="form.radius" |
| | | :min="10" |
| | | :max="1000" |
| | | :step="10" |
| | | placeholder="请è¾å
¥æå¡èå´" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | <!-- é«å¾·å°å¾éæ© --> |
| | | <el-form-item label="æå¡ä½ç½®" |
| | | prop="longitude"> |
| | | <div class="map-container"> |
| | | <div class="map-header" |
| | | style="margin-bottom: 10px"> |
| | | <!-- <el-button @click="getCurrentLocation"> |
| | | <el-icon> |
| | | <Position /> |
| | | </el-icon> |
| | | å½åä½ç½® |
| | | </el-button> --> |
| | | <!-- <span style="margin-left: 10px; color: #909399;font-size: 12px;">ç¹å»å°å¾éæ©ä½ç½®</span> --> |
| | | </div> |
| | | <div id="map-container" |
| | | class="map" |
| | | ref="mapContainer"></div> |
| | | <div class="coordinates-info mt10"> |
| | | <el-input v-model="form.longitude" |
| | | readonly |
| | | placeholder="ç»åº¦" |
| | | style="width: 140px; margin-right: 10px" /> |
| | | <el-input v-model="form.latitude" |
| | | readonly |
| | | placeholder="纬度" |
| | | style="width: 140px; margin-right: 10px" /> |
| | | <!-- <el-input v-model="form.locationName" |
| | | placeholder="å°ç¹åç§°" |
| | | style="width: calc(100% - 290px)" /> --> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="å°ç¹åç§°" |
| | | prop="locationName"> |
| | | <el-input v-model="form.locationName" |
| | | :disabled="operationType === 'view'" |
| | | placeholder="请è¾å
¥å°ç¹åç§°" /> |
| | | </el-form-item> |
| | | <!-- ä¸ä¸çæ¶é´ --> |
| | | <el-form-item label="ä¸çæ¶é´" |
| | | prop="startAt"> |
| | | <el-time-picker v-model="form.startAt" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | | placeholder="è¯·éæ©ä¸çæ¶é´" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¸çæ¶é´" |
| | | prop="endAt"> |
| | | <el-time-picker v-model="form.endAt" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | | :picker-options="{ |
| | | minTime: form.startAt |
| | | }" |
| | | placeholder="è¯·éæ©ä¸çæ¶é´" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm" |
| | | v-if="operationType !== 'view'"> |
| | | ç¡®å® |
| | | </el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, watch, onMounted, nextTick } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { Position } from "@element-plus/icons-vue"; |
| | | import { deptTreeSelect } from "@/api/system/user.js"; |
| | | import { addAttendanceRule } from "@/api/personnelManagement/attendanceRules.js"; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | operationType: { |
| | | type: String, |
| | | default: "add", |
| | | }, |
| | | row: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | // const pickerOptions = ref({ minTime: form.value.startAt }); |
| | | |
| | | const emit = defineEmits(["update:modelValue", "close"]); |
| | | |
| | | const dialogVisible = computed({ |
| | | get: () => props.modelValue, |
| | | set: val => emit("update:modelValue", val), |
| | | }); |
| | | |
| | | const dialogTitle = computed(() => { |
| | | if (props.operationType === "add") return "æ°å¢æå¡è§å"; |
| | | if (props.operationType === "edit") return "ç¼è¾æå¡è§å"; |
| | | return "æ¥çæå¡è§å"; |
| | | }); |
| | | |
| | | // è¡¨åæ°æ® |
| | | const formRef = ref(); |
| | | const form = reactive({ |
| | | id: "", |
| | | sysDeptId: "", |
| | | locationName: "", |
| | | longitude: "", |
| | | latitude: "", |
| | | radius: 100, |
| | | startAt: "09:00", |
| | | endAt: "18:00", |
| | | }); |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | sysDeptId: [{ required: true, message: "è¯·éæ©é¨é¨", trigger: "change" }], |
| | | locationName: [ |
| | | { required: true, message: "请è¾å
¥å°ç¹åç§°", trigger: "blur" }, |
| | | ], |
| | | longitude: [{ required: true, message: "è¯·éæ©æå¡ä½ç½®", trigger: "blur" }], |
| | | latitude: [{ required: true, message: "è¯·éæ©æå¡ä½ç½®", trigger: "blur" }], |
| | | radius: [{ required: true, message: "请è¾å
¥æå¡èå´", trigger: "blur" }], |
| | | startAt: [{ required: true, message: "è¯·éæ©ä¸çæ¶é´", trigger: "change" }], |
| | | endAt: [ |
| | | { required: true, message: "è¯·éæ©ä¸çæ¶é´", trigger: "change" }, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | if (form.startAt && value) { |
| | | const startParts = form.startAt.split(":"); |
| | | const endParts = value.split(":"); |
| | | const startTime = |
| | | parseInt(startParts[0]) * 60 + parseInt(startParts[1]); |
| | | const endTime = parseInt(endParts[0]) * 60 + parseInt(endParts[1]); |
| | | if (endTime <= startTime) { |
| | | callback(new Error("ä¸çæ¶é´ä¸è½æ©äºä¸çæ¶é´")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } else { |
| | | callback(); |
| | | } |
| | | }, |
| | | trigger: "change", |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | // é¨é¨é项 |
| | | const deptOptions = ref([]); |
| | | |
| | | // å°å¾ç¸å
³ |
| | | const mapContainer = ref(null); |
| | | let map = null; |
| | | let marker = null; |
| | | let circle = null; |
| | | |
| | | // è·åé¨é¨å表 |
| | | const fetchDeptOptions = () => { |
| | | deptTreeSelect().then(response => { |
| | | deptOptions.value = filterDisabledDept( |
| | | JSON.parse(JSON.stringify(response.data)) |
| | | ); |
| | | }); |
| | | }; |
| | | |
| | | // è¿æ»¤ç¦ç¨çé¨é¨ |
| | | const filterDisabledDept = deptList => { |
| | | return deptList.filter(dept => { |
| | | if (dept.disabled) { |
| | | return false; |
| | | } |
| | | if (dept.children && dept.children.length) { |
| | | dept.children = filterDisabledDept(dept.children); |
| | | } |
| | | return true; |
| | | }); |
| | | }; |
| | | |
| | | // åå§åå°å¾ |
| | | const initMap = () => { |
| | | nextTick(() => { |
| | | if (window.AMap && mapContainer.value) { |
| | | // åå§åå°å¾ |
| | | map = new window.AMap.Map(mapContainer.value, { |
| | | zoom: 16, |
| | | center: [116.397428, 39.90923], // é»è®¤å京 |
| | | }); |
| | | |
| | | // æ·»å æ§ä»¶ |
| | | window.AMap.plugin(["AMap.ToolBar", "AMap.Scale"], function () { |
| | | map.addControl(new window.AMap.ToolBar()); |
| | | map.addControl(new window.AMap.Scale()); |
| | | }); |
| | | |
| | | // æ·»å æ è®° |
| | | marker = new window.AMap.Marker({ |
| | | position: [116.397428, 39.90923], |
| | | draggable: true, |
| | | cursor: "move", |
| | | title: "ææ½å®ä½", |
| | | }); |
| | | map.add(marker); |
| | | |
| | | // æ·»å åå½¢èå´ |
| | | circle = new window.AMap.Circle({ |
| | | center: [116.397428, 39.90923], |
| | | radius: form.radius, |
| | | strokeColor: "#3366FF", |
| | | strokeOpacity: 0.8, |
| | | strokeWeight: 2, |
| | | fillColor: "#3366FF", |
| | | fillOpacity: 0.2, |
| | | }); |
| | | map.add(circle); |
| | | |
| | | // ç嬿 è®°ææ½ |
| | | marker.on("dragend", e => { |
| | | const position = e.lnglat; |
| | | const lng = position.getLng(); |
| | | const lat = position.getLat(); |
| | | form.longitude = lng; |
| | | form.latitude = lat; |
| | | updateCircle(position); |
| | | }); |
| | | |
| | | // ç嬿 è®°ææ½å¼å§ |
| | | marker.on("dragstart", () => { |
| | | map.setDefaultCursor("move"); |
| | | }); |
| | | |
| | | // ç嬿 è®°ææ½ç»æ |
| | | marker.on("dragend", () => { |
| | | map.setDefaultCursor("default"); |
| | | }); |
| | | |
| | | // çå¬å°å¾ç¹å» |
| | | map.on("click", e => { |
| | | const position = e.lnglat; |
| | | const lng = position.getLng(); |
| | | const lat = position.getLat(); |
| | | form.longitude = lng; |
| | | form.latitude = lat; |
| | | updateMarker(position); |
| | | updateCircle(position); |
| | | }); |
| | | |
| | | // å°è¯è·åå½åä½ç½®å¹¶è®¾ç½®ä¸ºå°å¾ä¸å¿ |
| | | if (navigator.geolocation && !form.longitude && !form.latitude) { |
| | | navigator.geolocation.getCurrentPosition( |
| | | position => { |
| | | console.log("è·åå°å½åä½ç½®:", position); |
| | | const { longitude, latitude } = position.coords; |
| | | const currentPosition = [longitude, latitude]; |
| | | map.setCenter(currentPosition); |
| | | updateMarker(currentPosition); |
| | | updateCircle(currentPosition); |
| | | form.longitude = longitude; |
| | | form.latitude = latitude; |
| | | }, |
| | | error => { |
| | | console.log("è·åä½ç½®å¤±è´¥ï¼ä½¿ç¨é»è®¤ä½ç½®"); |
| | | } |
| | | ); |
| | | } else if (form.longitude && form.latitude) { |
| | | // å¦æææ°æ®ï¼è®¾ç½®å°å°å¾ |
| | | const position = [form.longitude, form.latitude]; |
| | | map.setCenter(position); |
| | | updateMarker(position); |
| | | updateCircle(position); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // æ´æ°æ è®°ä½ç½® |
| | | const updateMarker = position => { |
| | | if (marker) { |
| | | marker.setPosition(position); |
| | | } |
| | | }; |
| | | |
| | | // æ´æ°åå½¢èå´ |
| | | const updateCircle = position => { |
| | | if (circle) { |
| | | circle.setCenter(position); |
| | | circle.setRadius(form.radius); |
| | | } |
| | | }; |
| | | |
| | | // è·åå½åä½ç½® |
| | | const getCurrentLocation = () => { |
| | | if (navigator.geolocation) { |
| | | navigator.geolocation.getCurrentPosition( |
| | | position => { |
| | | const { longitude, latitude } = position.coords; |
| | | form.longitude = longitude; |
| | | form.latitude = latitude; |
| | | if (map) { |
| | | map.setCenter([longitude, latitude]); |
| | | updateMarker([longitude, latitude]); |
| | | updateCircle([longitude, latitude]); |
| | | } |
| | | |
| | | // éå°çç¼ç è·åå°å |
| | | if (window.AMap) { |
| | | // å è½½Geocoderæä»¶ |
| | | window.AMap.plugin("AMap.Geocoder", function () { |
| | | const geocoder = new window.AMap.Geocoder(); |
| | | geocoder.getAddress([longitude, latitude], (status, result) => { |
| | | if (status === "complete" && result.regeocode) { |
| | | form.locationName = result.regeocode.formattedAddress; |
| | | } |
| | | }); |
| | | }); |
| | | } |
| | | }, |
| | | error => { |
| | | ElMessage.error("è·åä½ç½®å¤±è´¥ï¼è¯·æå¨éæ©"); |
| | | } |
| | | ); |
| | | } else { |
| | | ElMessage.error("æµè§å¨ä¸æ¯æå°çå®ä½"); |
| | | } |
| | | }; |
| | | |
| | | // çå¬åå¾åå |
| | | watch( |
| | | () => form.radius, |
| | | newValue => { |
| | | if (circle) { |
| | | circle.setRadius(newValue); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | // çå¬ä¸çæ¶é´ååï¼è§¦åä¸çæ¶é´æ ¡éª |
| | | watch( |
| | | () => form.startAt, |
| | | () => { |
| | | if (formRef.value && form.endAt) { |
| | | formRef.value.validateField("endAt"); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | // çå¬å¼¹çªæ¾ç¤º |
| | | watch( |
| | | () => dialogVisible.value, |
| | | newValue => { |
| | | if (newValue) { |
| | | // é置表å |
| | | Object.assign(form, { |
| | | id: "", |
| | | sysDeptId: "", |
| | | locationName: "", |
| | | longitude: "", |
| | | latitude: "", |
| | | radius: 100, |
| | | startAt: "09:00", |
| | | endAt: "18:00", |
| | | }); |
| | | |
| | | // 妿æ¯ç¼è¾ææ¥çï¼å¡«å
æ°æ® |
| | | if (props.operationType !== "add" && props.row.id) { |
| | | // å¤çæ¶é´æ ¼å¼ï¼ç¡®ä¿æ¯HH:mmæ ¼å¼ |
| | | const rowData = { ...props.row }; |
| | | if (rowData.startAt && rowData.startAt.includes(":")) { |
| | | rowData.startAt = rowData.startAt.split(":").slice(0, 2).join(":"); |
| | | } |
| | | if (rowData.endAt && rowData.endAt.includes(":")) { |
| | | rowData.endAt = rowData.endAt.split(":").slice(0, 2).join(":"); |
| | | } |
| | | Object.assign(form, rowData); |
| | | } |
| | | |
| | | // åå§åå°å¾ |
| | | setTimeout(() => { |
| | | initMap(); |
| | | }, 100); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | // æäº¤è¡¨å |
| | | const submitForm = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | const submitData = { |
| | | ...form, |
| | | // è½¬æ¢æ¶é´æ ¼å¼ï¼ç¡®ä¿åªä¿çæ¶åé¨å |
| | | startAt: form.startAt |
| | | ? `${form.startAt.split(":").slice(0, 2).join(":")}` |
| | | : null, |
| | | endAt: form.endAt |
| | | ? `${form.endAt.split(":").slice(0, 2).join(":")}` |
| | | : null, |
| | | }; |
| | | |
| | | if (props.operationType === "add") { |
| | | addAttendanceRule(submitData).then(() => { |
| | | ElMessage.success("æ°å¢æå"); |
| | | emit("close"); |
| | | }); |
| | | } else if (props.operationType === "edit") { |
| | | addAttendanceRule(submitData).then(() => { |
| | | ElMessage.success("æ´æ°æå"); |
| | | emit("close"); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // åå§å |
| | | onMounted(() => { |
| | | fetchDeptOptions(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .map-container { |
| | | width: 100%; |
| | | } |
| | | |
| | | .map { |
| | | width: 100%; |
| | | height: 400px; |
| | | border: 1px solid #e4e7ed; |
| | | } |
| | | |
| | | .coordinates-info { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .coordinates-display { |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .mt10 { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .mt8 { |
| | | margin-top: 8px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢åæä½æé® --> |
| | | <div class="page-header"> |
| | | <div class="title">æå¡è§åé
ç½®</div> |
| | | <div class="actions"> |
| | | <el-button type="primary" |
| | | @click="openForm('add')"> |
| | | <el-icon> |
| | | <Plus /> |
| | | </el-icon> |
| | | æ°å¢è§å |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <!-- æ¥è¯¢æ¡ä»¶ --> |
| | | <!-- <el-form :model="searchForm" |
| | | :inline="true" |
| | | class="search-form mb16"> |
| | | <el-form-item label="é¨é¨ï¼" |
| | | prop="countId"> |
| | | <el-tree-select v-model="searchForm.countId" |
| | | :data="deptOptions" |
| | | :props="{ value: 'id', label: 'label', children: 'children' }" |
| | | value-key="id" |
| | | placeholder="è¯·éæ©é¨é¨" |
| | | check-strictly |
| | | style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item label="å°ç¹ï¼" |
| | | prop="locationName"> |
| | | <el-input v-model="searchForm.locationName" |
| | | placeholder="请è¾å
¥å°ç¹åç§°" |
| | | clearable |
| | | style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="fetchData"> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | æç´¢ |
| | | </el-button> |
| | | <el-button @click="resetSearch"> |
| | | <el-icon> |
| | | <Refresh /> |
| | | </el-icon> |
| | | éç½® |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> --> |
| | | <!-- è§åå表 --> |
| | | <el-card shadow="never" |
| | | class="mb16"> |
| | | <el-table :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | style="width: 100%" |
| | | row-key="id"> |
| | | <el-table-column type="index" |
| | | label="åºå·" |
| | | width="60" |
| | | align="center" /> |
| | | <el-table-column label="é¨é¨"> |
| | | <template #default="scope"> |
| | | {{ getDeptNameById(scope.row.sysDeptId) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="locationName" |
| | | label="å°ç¹åç§°" /> |
| | | <el-table-column prop="longitude" |
| | | label="ç»åº¦" /> |
| | | <el-table-column prop="latitude" |
| | | label="纬度" /> |
| | | <el-table-column prop="radius" |
| | | label="æå¡èå´(m)" /> |
| | | <el-table-column prop="startAt" |
| | | label="ä¸çæ¶é´"> |
| | | <template #default="scope"> |
| | | {{ scope.row.startAt }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="endAt" |
| | | label="ä¸çæ¶é´"> |
| | | <template #default="scope"> |
| | | {{ scope.row.endAt }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" |
| | | width="180" |
| | | fixed="right" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" |
| | | size="small" |
| | | link |
| | | @click="openForm('edit', scope.row)">ç¼è¾</el-button> |
| | | <el-button type="danger" |
| | | size="small" |
| | | link |
| | | @click="handleDelete(scope.row.id)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination :total="page.total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" |
| | | class="mt10" /> |
| | | </el-card> |
| | | <!-- æ°å¢/ç¼è¾è§åå¼¹çª --> |
| | | <rule-form ref="ruleFormRef" |
| | | v-model="dialogVisible" |
| | | :operation-type="operationType" |
| | | :row="currentRow" |
| | | @close="dialogVisible = false; fetchData()" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Edit, Delete, Search, Refresh } from "@element-plus/icons-vue"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import RuleForm from "./components/form.vue"; |
| | | import { deptTreeSelect } from "@/api/system/user.js"; |
| | | import { |
| | | getAttendanceRules, |
| | | deleteAttendanceRule, |
| | | } from "@/api/personnelManagement/attendanceRules.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // è¡¨æ ¼æ°æ® |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | |
| | | // å页忰 |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | // æ¥è¯¢è¡¨å |
| | | const searchForm = reactive({ |
| | | countId: "", |
| | | locationName: "", |
| | | }); |
| | | |
| | | // é¨é¨é项 |
| | | const deptOptions = ref([]); |
| | | |
| | | // å¼¹çªæ§å¶ |
| | | const dialogVisible = ref(false); |
| | | const operationType = ref("add"); |
| | | const currentRow = ref({}); |
| | | const ruleFormRef = ref(); |
| | | |
| | | // æ ¼å¼åæ¶é´ |
| | | const formatTime = timestamp => { |
| | | if (!timestamp) return ""; |
| | | const date = new Date(timestamp); |
| | | return `${String(date.getHours()).padStart(2, "0")}:${String( |
| | | date.getMinutes() |
| | | ).padStart(2, "0")}`; |
| | | }; |
| | | |
| | | // è·åé¨é¨å表 |
| | | const fetchDeptOptions = () => { |
| | | deptTreeSelect().then(response => { |
| | | deptOptions.value = filterDisabledDept( |
| | | JSON.parse(JSON.stringify(response.data)) |
| | | ); |
| | | }); |
| | | }; |
| | | |
| | | // è¿æ»¤ç¦ç¨çé¨é¨ |
| | | const filterDisabledDept = deptList => { |
| | | return deptList.filter(dept => { |
| | | if (dept.disabled) { |
| | | return false; |
| | | } |
| | | if (dept.children && dept.children.length) { |
| | | dept.children = filterDisabledDept(dept.children); |
| | | } |
| | | return true; |
| | | }); |
| | | }; |
| | | |
| | | // æ ¹æ®é¨é¨IDæ¥æ¾é¨é¨åç§° |
| | | const getDeptNameById = (deptId, deptList = deptOptions.value) => { |
| | | for (const dept of deptList) { |
| | | if (dept.id === deptId) { |
| | | return dept.label; |
| | | } |
| | | if (dept.children && dept.children.length) { |
| | | const name = getDeptNameById(deptId, dept.children); |
| | | if (name) { |
| | | return name; |
| | | } |
| | | } |
| | | } |
| | | return ""; |
| | | }; |
| | | |
| | | // æ¥è¯¢è§åå表 |
| | | const fetchData = () => { |
| | | tableLoading.value = true; |
| | | getAttendanceRules({ ...page, ...searchForm }) |
| | | .then(res => { |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // å页忴 |
| | | const paginationChange = pagination => { |
| | | page.current = pagination.page; |
| | | page.size = pagination.limit; |
| | | fetchData(); |
| | | }; |
| | | |
| | | // éç½®æç´¢ |
| | | const resetSearch = () => { |
| | | searchForm.countId = ""; |
| | | searchForm.locationName = ""; |
| | | fetchData(); |
| | | }; |
| | | |
| | | // æå¼è¡¨å |
| | | const openForm = (type, row = {}) => { |
| | | operationType.value = type; |
| | | currentRow.value = row; |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // å é¤è§å |
| | | const handleDelete = id => { |
| | | ElMessageBox.confirm("ç¡®å®è¦å é¤è¿æ¡è§ååï¼", "å é¤ç¡®è®¤", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | deleteAttendanceRule([id]).then(() => { |
| | | ElMessage.success("å 餿å"); |
| | | fetchData(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | // åæ¶å é¤ |
| | | }); |
| | | }; |
| | | |
| | | // åå§å |
| | | onMounted(() => { |
| | | fetchDeptOptions(); |
| | | fetchData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | |
| | | .title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | } |
| | | |
| | | .mb16 { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .mt10 { |
| | | margin-top: 10px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- åå·¥æå¡åº --> |
| | | <!-- <el-card shadow="never" |
| | | class="mb16"> |
| | | <div class="attendance-header"> |
| | | <div> |
| | | <div class="title">æå¡ç¾å° |
| | | </div> |
| | | <div class="sub-title">æ¯æä¸é®æå¡ï¼èªå¨è®°å½ä¸ä¸çæ¶é´</div> |
| | | </div> |
| | | <div class="attendance-actions"> |
| | | <div class="time-block"> |
| | | <div class="label">å½åæ¶é´</div> |
| | | <div class="value">{{ nowTime }}</div> |
| | | </div> |
| | | <el-button type="primary" |
| | | size="large" |
| | | @click="handleCheckInOut" |
| | | :disabled="todayRecord.workEndAt"> |
| | | {{ checkInOutText }} |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <el-descriptions border |
| | | :column="4" |
| | | class="mt10"> |
| | | <el-descriptions-item label="åå·¥å§å"> |
| | | {{ todayRecord.staffName }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å·¥å·"> |
| | | {{ todayRecord.staffNo }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æå±é¨é¨"> |
| | | {{ todayRecord.deptName }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="仿¥ç¶æ"> |
| | | <el-tag :type="todayStatusTag" |
| | | size="small"> |
| | | {{ todayStatusText }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¸çæ¶é´"> |
| | | {{ todayRecord?.workStartAt || '-' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¸çæ¶é´"> |
| | | {{ todayRecord?.workEndAt || '-' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å·¥æ¶(å°æ¶)"> |
| | | {{ todayRecord?.workHours ?? '-' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å¼å¸¸æ è®°"> |
| | | <span v-if="!todayRecord.id || todayRecord?.status === 0">-</span> |
| | | <el-tag v-else |
| | | type="danger" |
| | | size="small"> |
| | | {{ todayRecord?.status ? getStatusText(todayRecord.status) : '-' }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-card> --> |
| | | <div class="attendance-operation"> |
| | | <!-- æ¥è¯¢æ¡ä»¶ï¼ç®¡çåè夿¥æ¥ï¼ --> |
| | | <el-form :model="searchForm" |
| | | :inline="true" |
| | | class="search-form"> |
| | | <el-form-item label="é¨é¨ï¼" |
| | | prop="deptId"> |
| | | <el-tree-select v-model="searchForm.deptId" |
| | | :data="deptOptions" |
| | | :props="{ value: 'id', label: 'label', children: 'children' }" |
| | | value-key="id" |
| | | placeholder="è¯·éæ©é¨é¨" |
| | | check-strictly |
| | | style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item label="æ¥æï¼" |
| | | prop="date"> |
| | | <el-date-picker v-model="searchForm.date" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | clearable /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="fetchData"> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | | æç´¢ |
| | | </el-button> |
| | | <el-button @click="resetSearch"> |
| | | <el-icon> |
| | | <Refresh /> |
| | | </el-icon> |
| | | éç½® |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <el-button icon="Download" |
| | | @click="handleExport"> |
| | | 导åºè夿¥æ¥ |
| | | </el-button> |
| | | </div> |
| | | <!-- è夿¥æ¥è¡¨æ ¼ --> |
| | | <div class="table_list"> |
| | | <el-table :data="tableData" |
| | | border |
| | | v-loading="tableLoading" |
| | | style="width: 100%" |
| | | height="calc(100vh - 24em)" |
| | | :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" |
| | | :row-class-name="rowClassName"> |
| | | <el-table-column type="index" |
| | | label="åºå·" |
| | | width="60" |
| | | align="center" /> |
| | | <el-table-column prop="date" |
| | | label="æ¥æ" |
| | | width="120" /> |
| | | <el-table-column prop="deptName" |
| | | label="é¨é¨" |
| | | width="140" /> |
| | | <el-table-column prop="staffName" |
| | | label="å§å" |
| | | width="120" /> |
| | | <el-table-column prop="staffNo" |
| | | label="å·¥å·" |
| | | width="120" /> |
| | | <el-table-column prop="workStartAt" |
| | | label="ä¸çæ¶é´" |
| | | width="140" /> |
| | | <el-table-column prop="workEndAt" |
| | | label="ä¸çæ¶é´" |
| | | width="140" /> |
| | | <el-table-column prop="workHours" |
| | | label="å·¥æ¶(å°æ¶)" |
| | | align="center" /> |
| | | <el-table-column prop="status" |
| | | label="èå¤ç¶æ" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.status === 0" |
| | | type="success" |
| | | size="small"> |
| | | æ£å¸¸ |
| | | </el-tag> |
| | | <el-tag v-else |
| | | type="danger" |
| | | size="small"> |
| | | <!-- {{ scope.row.status === 1 ? 'è¿å°' : scope.row.status === 2 ? 'æ©é' : 'è¿å°ãæ©é' }} --> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="remark" |
| | | label="夿³¨" |
| | | show-overflow-tooltip /> |
| | | </el-table> |
| | | <pagination :total="page.total" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | @pagination="paginationChange" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted, onBeforeUnmount } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { |
| | | createPersonalAttendanceRecord, |
| | | findPersonalAttendanceRecords, |
| | | findTodayPersonalAttendanceRecord, |
| | | } from "@/api/personnelManagement/personalAttendanceRecords.js"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | import { deptTreeSelect } from "@/api/system/user.js"; |
| | | import { Refresh, Search } from "@element-plus/icons-vue"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tableLoading = ref(false); |
| | | // å页忰 |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | // 仿¥æ°æ® |
| | | const todayRecord = ref({}); |
| | | |
| | | // é¨é¨é项 |
| | | const deptOptions = ref([]); |
| | | |
| | | // æ¥è¯¢è¡¨å |
| | | const searchForm = reactive({ |
| | | deptId: "", |
| | | date: "", |
| | | }); |
| | | |
| | | // è¡¨æ ¼æ°æ® |
| | | const tableData = ref([]); |
| | | |
| | | // å½åæ¶é´å±ç¤º |
| | | const nowTime = ref(""); |
| | | let timer = null; |
| | | |
| | | const updateNowTime = () => { |
| | | const now = new Date(); |
| | | const Y = now.getFullYear(); |
| | | const M = String(now.getMonth() + 1).padStart(2, "0"); |
| | | const D = String(now.getDate()).padStart(2, "0"); |
| | | const h = String(now.getHours()).padStart(2, "0"); |
| | | const m = String(now.getMinutes()).padStart(2, "0"); |
| | | const s = String(now.getSeconds()).padStart(2, "0"); |
| | | nowTime.value = `${Y}-${M}-${D} ${h}:${m}:${s}`; |
| | | }; |
| | | |
| | | // æå¡æé®ææ¬ |
| | | const checkInOutText = computed(() => { |
| | | if (!todayRecord.value || !todayRecord.value.workStartAt) { |
| | | return "ä¸çæå¡"; |
| | | } |
| | | if (!todayRecord.value.workEndAt) { |
| | | return "ä¸çæå¡"; |
| | | } |
| | | return "仿¥å·²æå¡å®æ"; |
| | | }); |
| | | |
| | | // 仿¥ç¶æå±ç¤º |
| | | const todayStatusTag = computed(() => { |
| | | if (!todayRecord.value.id) return "info"; |
| | | if (todayRecord.value.status === 0) return "success"; |
| | | return "danger"; |
| | | }); |
| | | const getStatusText = status => { |
| | | switch (status) { |
| | | case 0: |
| | | return "æ£å¸¸"; |
| | | case 1: |
| | | return "è¿å°"; |
| | | case 2: |
| | | return "æ©é"; |
| | | case 3: |
| | | return "è¿å°ãæ©é"; |
| | | case 4: |
| | | return "缺å¤"; |
| | | } |
| | | }; |
| | | |
| | | const todayStatusText = computed(() => { |
| | | if (!todayRecord.value.id) return "æªæå¡"; |
| | | switch (todayRecord.value.status) { |
| | | case 0: |
| | | return "æ£å¸¸"; |
| | | case 1: |
| | | return "è¿å°"; |
| | | case 2: |
| | | return "æ©é"; |
| | | case 3: |
| | | return "è¿å°ãæ©é"; |
| | | case 4: |
| | | return "缺å¤"; |
| | | } |
| | | }); |
| | | |
| | | // è¡æ ·å¼ï¼å¼å¸¸é«äº® |
| | | const rowClassName = ({ row }) => { |
| | | if (row.status === 1 || row.status === 2) { |
| | | return "row-abnormal"; |
| | | } |
| | | return ""; |
| | | }; |
| | | |
| | | // æ¥è¯¢é¨é¨å表 |
| | | const fetchDeptOptions = () => { |
| | | deptTreeSelect().then(response => { |
| | | deptOptions.value = filterDisabledDept( |
| | | JSON.parse(JSON.stringify(response.data)) |
| | | ); |
| | | }); |
| | | }; |
| | | |
| | | /** è¿æ»¤ç¦ç¨çé¨é¨ */ |
| | | function filterDisabledDept(deptList) { |
| | | return deptList.filter(dept => { |
| | | if (dept.disabled) { |
| | | return false; |
| | | } |
| | | if (dept.children && dept.children.length) { |
| | | dept.children = filterDisabledDept(dept.children); |
| | | } |
| | | return true; |
| | | }); |
| | | } |
| | | |
| | | // æ¥è¯¢ |
| | | const fetchData = () => { |
| | | tableLoading.value = true; |
| | | findPersonalAttendanceRecords({ ...page, ...searchForm }) |
| | | .then(res => { |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢ä»æ¥æå¡ä¿¡æ¯ |
| | | const fetchTodayData = () => { |
| | | // findTodayPersonalAttendanceRecord({}).then(res => { |
| | | // todayRecord.value = res.data; |
| | | // }); |
| | | }; |
| | | |
| | | const paginationChange = pagination => { |
| | | page.current = pagination.page; |
| | | page.size = pagination.limit; |
| | | fetchData(); |
| | | }; |
| | | |
| | | const resetSearch = () => { |
| | | searchForm.deptId = ""; |
| | | searchForm.date = ""; |
| | | fetchData(); |
| | | }; |
| | | |
| | | const handleExport = () => { |
| | | ElMessageBox.confirm("æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/personalAttendanceRecords/export", {}, "èå¤è®°å½.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // è·åå½åä½ç½® |
| | | const getCurrentLocation = () => { |
| | | return new Promise((resolve, reject) => { |
| | | if (!navigator.geolocation) { |
| | | reject(new Error("æµè§å¨ä¸æ¯æå°çå®ä½")); |
| | | return; |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦ä½¿ç¨HTTPS |
| | | const isSecureContext = |
| | | window.isSecureContext || window.location.protocol === "https:"; |
| | | console.log( |
| | | "å½ååè®®:", |
| | | window.location.protocol, |
| | | "æ¯å¦å®å
¨ä¸ä¸æ:", |
| | | isSecureContext |
| | | ); |
| | | |
| | | if (!isSecureContext) { |
| | | console.warn("å½å䏿¯HTTPSåè®®ï¼å°çä½ç½®APIå¯è½åé"); |
| | | } |
| | | |
| | | navigator.geolocation.getCurrentPosition( |
| | | position => { |
| | | const { longitude, latitude } = position.coords; |
| | | console.log("è·åä½ç½®æå:", longitude, latitude); |
| | | resolve({ longitude, latitude }); |
| | | }, |
| | | error => { |
| | | console.log("è·åä½ç½®å¤±è´¥:", error); |
| | | let errorMessage = "è·åä½ç½®å¤±è´¥"; |
| | | |
| | | // æ ¹æ®é误类åæä¾æ´å
·ä½çæç¤º |
| | | switch (error.code) { |
| | | case error.PERMISSION_DENIED: |
| | | errorMessage = |
| | | "ç¨æ·æç»äºä½ç½®æé请æ±ï¼è¯·å¨æµè§å¨è®¾ç½®ä¸å
许ä½ç½®è®¿é®"; |
| | | break; |
| | | case error.POSITION_UNAVAILABLE: |
| | | errorMessage = "ä½ç½®ä¿¡æ¯ä¸å¯ç¨ï¼è¯·æ£æ¥è®¾å¤å®ä½åè½"; |
| | | break; |
| | | case error.TIMEOUT: |
| | | errorMessage = "è·åä½ç½®è¶
æ¶ï¼è¯·éè¯"; |
| | | break; |
| | | case error.UNKNOWN_ERROR: |
| | | errorMessage = "è·åä½ç½®æ¶åçæªç¥é误"; |
| | | break; |
| | | default: |
| | | errorMessage = `è·åä½ç½®å¤±è´¥: ${error.message}`; |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦æ¯HTTPSé®é¢ |
| | | if (error.code === error.PERMISSION_DENIED && !isSecureContext) { |
| | | errorMessage += "ï¼æ³¨æï¼ç产ç¯å¢éè¦ä½¿ç¨HTTPSåè®®æè½è·åä½ç½®ï¼"; |
| | | } |
| | | |
| | | reject(new Error(errorMessage)); |
| | | }, |
| | | { |
| | | enableHighAccuracy: true, |
| | | timeout: 10000, |
| | | maximumAge: 0, |
| | | } |
| | | ); |
| | | }); |
| | | }; |
| | | |
| | | // æå¡ |
| | | const handleCheckInOut = () => { |
| | | getCurrentLocation() |
| | | .then(location => { |
| | | console.log("ä½ç½®æå"); |
| | | createPersonalAttendanceRecord(location).then(res => { |
| | | fetchData(); |
| | | fetchTodayData(); |
| | | ElMessage.success("æå¡æåï¼"); |
| | | }); |
| | | }) |
| | | .catch(error => { |
| | | // è·åä½ç½®å¤±è´¥æ¶ï¼ä»å
许æå¡ |
| | | ElMessage.warning("è·åä½ç½®å¤±è´¥ï¼å°ä½¿ç¨é»è®¤ä½ç½®æå¡"); |
| | | createPersonalAttendanceRecord({}).then(res => { |
| | | fetchData(); |
| | | fetchTodayData(); |
| | | ElMessage.success("æå¡æåï¼"); |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | updateNowTime(); |
| | | timer = setInterval(updateNowTime, 1000); |
| | | // é»è®¤å±ç¤ºå½å¤©æ°æ® |
| | | const today = new Date(); |
| | | const Y = today.getFullYear(); |
| | | const M = String(today.getMonth() + 1).padStart(2, "0"); |
| | | const D = String(today.getDate()).padStart(2, "0"); |
| | | searchForm.date = `${Y}-${M}-${D}`; |
| | | fetchData(); |
| | | fetchTodayData(); |
| | | fetchDeptOptions(); |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | if (timer) { |
| | | clearInterval(timer); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .mb16 { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .attendance-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .attendance-header .title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .attendance-header .sub-title { |
| | | font-size: 13px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .attendance-actions { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .time-block { |
| | | text-align: right; |
| | | } |
| | | |
| | | .time-block .label { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .time-block .value { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | ::v-deep(.row-abnormal) { |
| | | background-color: #fff5f5; |
| | | } |
| | | |
| | | .attendance-operation { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | } |
| | | </style> |
| | | |
| | |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | :showPagination="false" |
| | | height="600" |
| | | ></PIMTable> |
| | | <template #footer> |
| | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <Files ref="filesDia"></Files> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {staffOnJobInfo} from "@/api/personnelManagement/employeeRecord.js"; |
| | | import {findStaffContractListPage} from "@/api/personnelManagement/staffContract.js"; |
| | | const Files = defineAsyncComponent(() => import( "@/views/personnelManagement/contractManagement/filesDia.vue")); |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const filesDia = ref() |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const tableColumn = ref([ |
| | | // { |
| | | // label: "ååå¹´é", |
| | | // prop: "contractTerm", |
| | | // }, |
| | | { |
| | | label: "ååå¹´é", |
| | | prop: "contractTerm", |
| | | }, |
| | | { |
| | | label: "ååå¼å§æ¥æ", |
| | | prop: "contractStartTime", |
| | |
| | | { |
| | | label: "ååç»ææ¥æ", |
| | | prop: "contractEndTime", |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: 'right', |
| | | width: 120, |
| | | operation: [ |
| | | { |
| | | name: "ä¸ä¼ éä»¶", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | filesDia.value.openDialog( row,'åå') |
| | | }, |
| | | } |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | if (operationType.value === 'edit') { |
| | | staffOnJobInfo({staffNo: row.staffNo}).then(res => { |
| | | tableData.value = res.data |
| | | findStaffContractListPage({staffOnJobId: row.id}).then(res => { |
| | | tableData.value = res.data.records |
| | | }) |
| | | } |
| | | } |
| | | |
| | | const openUploadFile = (row) => { |
| | | filesDia.value.open = true |
| | | filesDia.value.row = row |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | dialogFormVisible.value = false; |
| | |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | :isSelection="true" |
| | | :page="page" |
| | | @selection-change="handleSelectionChange" |
| | | height="500" |
| | | @pagination="paginationSearch" |
| | | :total="page.total" |
| | | > |
| | | </PIMTable> |
| | | <pagination |
| | | style="margin: 10px 0" |
| | | v-show="total > 0" |
| | | @pagination="paginationSearch" |
| | | :total="total" |
| | | :page="page.current" |
| | | :limit="page.size" |
| | | /> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | |
| | | const getList = () => { |
| | | fileListPage({accountId: currentId.value,accountType:accountType.value, ...page}).then(res => { |
| | | tableData.value = res.data.records; |
| | | total.value = res.data.total; |
| | | page.total = res.data.total; |
| | | }) |
| | | } |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search, UploadFilled } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue"; |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref } from "vue"; |
| | | import FormDia from "@/views/personnelManagement/contractManagement/components/formDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { staffOnJobListPage } from "@/api/personnelManagement/employeeRecord.js"; |
| | | import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js"; |
| | | import dayjs from "dayjs"; |
| | | import { getToken } from "@/utils/auth.js"; |
| | | import FilesDia from "./filesDia.vue"; |
| | |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: 'right', |
| | | width: 130, |
| | | width: 120, |
| | | operation: [ |
| | | { |
| | | name: "详æ
", |
| | |
| | | clickFun: (row) => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "éä»¶", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openFilesFormDia(row); |
| | | }, |
| | | }, |
| | | } |
| | | ], |
| | | }, |
| | | ]); |
| | |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined |
| | | params.staffState = 1 |
| | | staffOnJobListPage(params).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/staff/staffOnJob/export", {}, "åå管ç.xlsx"); |
| | | proxy.download("/staff/staffOnJob/export", {staffState: 1}, "åå管ç.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | |
| | | <!-- å工信æ¯å±ç¤ºåºå --> |
| | | <div class="info-section"> |
| | | <div class="info-title">å工信æ¯</div> |
| | | <el-form :model="form" label-width="200px" label-position="left" :rules="rules" ref="formRef" style="margin-top: 20px"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">å§åï¼</span> |
| | | <el-select v-model="form.staffName" placeholder="è¯·éæ©äººå" style="width: 100%" @change="handleSelect" filterable> |
| | | <el-form-item label="å§åï¼" prop="staffOnJobId"> |
| | | <el-select v-model="form.staffOnJobId" |
| | | placeholder="è¯·éæ©äººå" |
| | | style="width: 100%" |
| | | :disabled="operationType === 'edit'" |
| | | @change="handleSelect"> |
| | | <el-option |
| | | v-for="item in personList" |
| | | :key="item.id" |
| | | :label="item.staffName" |
| | | :value="item.staffName" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">åå·¥ç¼å·ï¼</span> |
| | | <span class="info-value">{{ form.staffNo || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">æ§å«ï¼</span> |
| | | <span class="info-value">{{ form.sex || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">æ·ç±ä½åï¼</span> |
| | | <span class="info-value">{{ form.nativePlace || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">å²ä½ï¼</span> |
| | | <span class="info-value">{{ form.postJob || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">ç°ä½åï¼</span> |
| | | <span class="info-value">{{ form.adress || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">第ä¸å¦åï¼</span> |
| | | <span class="info-value">{{ form.firstStudy || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">ä¸ä¸ï¼</span> |
| | | <span class="info-value">{{ form.profession || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">å¹´é¾ï¼</span> |
| | | <span class="info-value">{{ form.age || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">èç³»çµè¯ï¼</span> |
| | | <span class="info-value">{{ form.phone || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">ç´§æ¥è系人ï¼</span> |
| | | <span class="info-value">{{ form.emergencyContact || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">ç´§æ¥è系人èç³»çµè¯ï¼</span> |
| | | <span class="info-value">{{ form.emergencyContactPhone || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">ååå¼å§æ¥æï¼</span> |
| | | <span class="info-value">{{ form.contractStartTime || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="info-item"> |
| | | <span class="info-label">ååç»ææ¥æï¼</span> |
| | | <span class="info-value">{{ form.contractExpireTime || '-' }}</span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- 离èä¿¡æ¯å¡«ååºå --> |
| | | <!-- <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef" style="margin-top: 20px"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç¦»èæ¥æï¼" prop="dimissionDate"> |
| | | <el-date-picker |
| | | v-model="form.dimissionDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©ç¦»èæ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="离èåå ï¼" prop="dimissionReason"> |
| | | <el-form-item label="åå·¥ç¼å·ï¼"> |
| | | {{ currentStaffRecord.staffNo || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ§å«ï¼"> |
| | | {{ currentStaffRecord.sex || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ·ç±ä½åï¼"> |
| | | {{ currentStaffRecord.nativePlace || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å²ä½ï¼"> |
| | | {{ currentStaffRecord.postName || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç°ä½åï¼"> |
| | | {{ currentStaffRecord.adress || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="第ä¸å¦åï¼"> |
| | | {{ currentStaffRecord.firstStudy || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¸ä¸ï¼"> |
| | | {{ currentStaffRecord.profession || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¹´é¾ï¼"> |
| | | {{ currentStaffRecord.age || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èç³»çµè¯ï¼"> |
| | | {{ currentStaffRecord.phone || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç´§æ¥è系人ï¼"> |
| | | {{ currentStaffRecord.emergencyContact || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç´§æ¥è系人èç³»çµè¯ï¼"> |
| | | {{ currentStaffRecord.emergencyContactPhone || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="离èåå ï¼" prop="reason"> |
| | | <el-select v-model="form.reason" placeholder="è¯·éæ©ç¦»èåå " style="width: 100%" @change="handleSelectDimissionReason"> |
| | | <el-option |
| | | v-for="(item, index) in dimissionReasonOptions" |
| | | :key="index" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="夿³¨ï¼" prop="remark" v-if="form.reason === 'other'"> |
| | | <el-input |
| | | v-model="form.dimissionReason" |
| | | v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥ç¦»èåå " |
| | | placeholder="夿³¨" |
| | | maxlength="500" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> --> |
| | | </el-form> |
| | | |
| | | <!-- <el-row :gutter="30">--> |
| | | <!-- <el-col :span="12">--> |
| | | <!-- <div class="info-item">--> |
| | | <!-- <span class="info-label">离èåå ï¼</span>--> |
| | | <!-- <el-select v-model="form.reason" placeholder="è¯·éæ©äººå" style="width: 100%" @change="handleSelect">--> |
| | | <!-- <el-option--> |
| | | <!-- v-for="(item, index) in dimissionReasonOptions"--> |
| | | <!-- :key="index"--> |
| | | <!-- :label="item.label"--> |
| | | <!-- :value="item.value"--> |
| | | <!-- />--> |
| | | <!-- </el-select>--> |
| | | <!-- </div>--> |
| | | <!-- </el-col>--> |
| | | <!-- <el-col :span="12">--> |
| | | <!-- <div class="info-item">--> |
| | | <!-- <span class="info-label">åå·¥ç¼å·ï¼</span>--> |
| | | <!-- <span class="info-value">{{ form.staffNo || '-' }}</span>--> |
| | | <!-- </div>--> |
| | | <!-- </el-col>--> |
| | | <!-- </el-row>--> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | |
| | | |
| | | <script setup> |
| | | import {ref, reactive, toRefs, getCurrentInstance} from "vue"; |
| | | import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js"; |
| | | import { staffOnJobListPage } from "@/api/personnelManagement/employeeRecord.js"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import {createStaffLeave, updateStaffLeave} from "@/api/personnelManagement/staffLeave.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | |
| | | const operationType = ref('') |
| | | const data = reactive({ |
| | | form: { |
| | | staffNo: "", |
| | | staffName: "", |
| | | sex: "", |
| | | nativePlace: "", |
| | | postJob: "", |
| | | adress: "", |
| | | firstStudy: "", |
| | | profession: "", |
| | | age: 0, |
| | | phone: "", |
| | | emergencyContact: "", |
| | | emergencyContactPhone: "", |
| | | contractTerm: 0, |
| | | contractStartTime: "", |
| | | contractExpireTime: "", |
| | | dimissionDate: "", |
| | | dimissionReason: "", |
| | | staffState: "", |
| | | staffOnJobId: undefined, |
| | | reason: "", |
| | | remark: "", |
| | | }, |
| | | rules: { |
| | | staffName: [{ required: true, message: "è¯·éæ©äººå", trigger: "change" }], |
| | | dimissionDate: [{ required: true, message: "è¯·éæ©ç¦»èæ¥æ", trigger: "change" }], |
| | | dimissionReason: [{ required: true, message: "请è¾å
¥ç¦»èåå ", trigger: "blur" }], |
| | | staffName: [{ required: true, message: "è¯·éæ©äººå" }], |
| | | reason: [{ required: true, message: "è¯·éæ©ç¦»èåå "}], |
| | | }, |
| | | dimissionReasonOptions: [ |
| | | {label: 'èªèµå¾
é', value: 'salary'}, |
| | | {label: 'èä¸åå±', value: 'career_development'}, |
| | | {label: 'å·¥ä½ç¯å¢', value: 'work_environment'}, |
| | | {label: '个人åå ', value: 'personal_reason'}, |
| | | {label: 'å
¶ä»', value: 'other'}, |
| | | ], |
| | | currentStaffRecord: {}, |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | const { form, rules, dimissionReasonOptions, currentStaffRecord } = toRefs(data); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | getList() |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | if (operationType.value === 'edit') { |
| | | getStaffJoinInfo(row.id).then(res => { |
| | | form.value = {...res.data} |
| | | }) |
| | | currentStaffRecord.value = row |
| | | form.value.staffOnJobId = row.staffOnJobId |
| | | form.value.reason = row.reason |
| | | form.value.remark = row.remark |
| | | personList.value = [ |
| | | { |
| | | staffName: row.staffName, |
| | | id: row.staffOnJobId, |
| | | } |
| | | ] |
| | | } else { |
| | | getList() |
| | | } |
| | | } |
| | | |
| | | const handleSelectDimissionReason = (val) => { |
| | | if (val === 'other') { |
| | | form.value.remark = '' |
| | | } |
| | | } |
| | | // æäº¤äº§å表å |
| | | const submitForm = () => { |
| | | // 表å已注éï¼ç´æ¥æäº¤ï¼ä¸è¿è¡éªè¯ |
| | | if (!form.value.staffName) { |
| | | proxy.$modal.msgError("è¯·éæ©äººå"); |
| | | return; |
| | | } |
| | | form.value.staffState = 0 |
| | | if (form.value.reason !== 'other') { |
| | | form.value.remark = '' |
| | | } |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "add") { |
| | | staffJoinAdd(form.value).then(res => { |
| | | createStaffLeave(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } else { |
| | | staffJoinUpdate(form.value).then(res => { |
| | | updateStaffLeave(currentStaffRecord.value.id, form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }) |
| | | } |
| | | } |
| | | }) |
| | | |
| | | } |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | // 表å已注éï¼æå¨éç½®è¡¨åæ°æ® |
| | | form.value = { |
| | | staffNo: "", |
| | | staffName: "", |
| | | sex: "", |
| | | nativePlace: "", |
| | | postJob: "", |
| | | adress: "", |
| | | firstStudy: "", |
| | | profession: "", |
| | | age: 0, |
| | | phone: "", |
| | | emergencyContact: "", |
| | | emergencyContactPhone: "", |
| | | contractTerm: 0, |
| | | contractStartTime: "", |
| | | contractExpireTime: "", |
| | | dimissionDate: "", |
| | | dimissionReason: "", |
| | | staffState: "", |
| | | staffOnJobId: undefined, |
| | | reason: "", |
| | | remark: "", |
| | | }; |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | |
| | | }; |
| | | |
| | | const handleSelect = (val) => { |
| | | let obj = personList.value.find(item => item.staffName === val) |
| | | let obj = personList.value.find(item => item.id === val) |
| | | currentStaffRecord.value = {} |
| | | if (obj) { |
| | | let { |
| | | sex, |
| | | phone, |
| | | staffNo, |
| | | nativePlace, |
| | | postJob, |
| | | adress, |
| | | firstStudy, |
| | | profession, |
| | | age, |
| | | emergencyContact, |
| | | emergencyContactPhone, |
| | | contractTerm, |
| | | contractStartTime, |
| | | contractExpireTime, |
| | | staffName |
| | | } = obj |
| | | // ä¿çç¦»èæ¥æå离èåå ï¼åªæ´æ°åå·¥ä¿¡æ¯ |
| | | form.value = { |
| | | ...form.value, |
| | | sex, |
| | | phone, |
| | | staffNo, |
| | | nativePlace, |
| | | postJob, |
| | | adress, |
| | | firstStudy, |
| | | profession, |
| | | age, |
| | | emergencyContact, |
| | | emergencyContactPhone, |
| | | contractTerm, |
| | | contractStartTime, |
| | | contractExpireTime, |
| | | staffName |
| | | } |
| | | currentStaffRecord.value = obj |
| | | } |
| | | } |
| | | defineExpose({ |
| | |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <span style="margin-left: 10px;" class="search_title">ååå¼å§æ¥æï¼</span> |
| | | <el-date-picker |
| | | v-model="searchForm.entryDateStart" |
| | | type="date" |
| | | placeholder="è¯·éæ©ååå¼å§æ¥æ" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,1)" |
| | | /> |
| | | <span style="margin-left: 10px;" class="search_title">ååç»ææ¥æï¼</span> |
| | | <el-date-picker |
| | | v-model="searchForm.entryDateEnd" |
| | | type="date" |
| | | placeholder="è¯·éæ©ååç»ææ¥æ" |
| | | size="default" |
| | | @change="(date) => handleDateChange(date,2)" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >æç´¢</el-button |
| | | > |
| | |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import {onMounted, ref} from "vue"; |
| | | import FormDia from "@/views/personnelManagement/dimission/components/formDia.vue"; |
| | | import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js"; |
| | | import {findStaffLeaveListPage, batchDeleteStaffLeaves} from "@/api/personnelManagement/staffLeave.js"; |
| | | import {ElMessageBox} from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | |
| | | prop: "nativePlace", |
| | | }, |
| | | { |
| | | label: "é¨é¨", |
| | | prop: "deptName", |
| | | }, |
| | | { |
| | | label: "å²ä½", |
| | | prop: "postJob", |
| | | prop: "postName", |
| | | }, |
| | | { |
| | | label: "ç°ä½å", |
| | |
| | | prop: "emergencyContactPhone", |
| | | width:150 |
| | | }, |
| | | // { |
| | | // label: "ååå¹´é", |
| | | // prop: "contractTerm", |
| | | // }, |
| | | { |
| | | label: "ååå¼å§æ¥æ", |
| | | prop: "contractStartTime", |
| | | width: 120 |
| | | }, |
| | | { |
| | | label: "ååç»ææ¥æ", |
| | | prop: "contractEndTime", |
| | | width: 120 |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: 'right', |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | |
| | | 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 = () => { |
| | |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | staffJoinListPage({...page, ...searchForm.value, staffState: 0}).then(res => { |
| | | findStaffLeaveListPage({...page, ...searchForm.value}).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | page.total = res.data.total; |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | staffJoinDel(ids).then((res) => { |
| | | batchDeleteStaffLeaves(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/staff/staffJoinLeaveRecord/export", {staffState: 0}, "人å离è.xlsx"); |
| | | proxy.download("/staff/staffLeave/export", {}, "人å离è.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? 'æ°å¢å
¥è' : 'ç¼è¾äººå'" |
| | | width="70%" |
| | | @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="staffNo"> |
| | | <el-input v-model="form.staffNo" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | :disabled="operationType !== 'add'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å§åï¼" |
| | | prop="staffName"> |
| | | <el-input v-model="form.staffName" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ§å«ï¼" |
| | | prop="sex"> |
| | | <el-select v-model="form.sex"> |
| | | <el-option label="ç·" |
| | | value="ç·" /> |
| | | <el-option label="女" |
| | | value="女" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æ·ç±ä½åï¼" |
| | | prop="nativePlace"> |
| | | <el-input v-model="form.nativePlace" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å²ä½ï¼" |
| | | prop="sysPostId"> |
| | | <el-select v-model="form.sysPostId" |
| | | placeholder="è¯·éæ©å²ä½" |
| | | clearable> |
| | | <el-option v-for="item in postOptions" |
| | | :key="item.postId" |
| | | :label="item.postName" |
| | | :value="item.postId" |
| | | :disabled="item.status === '1'" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç°ä½åï¼" |
| | | prop="adress"> |
| | | <el-input v-model="form.adress" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="é¨é¨ï¼" |
| | | prop="sysDeptId"> |
| | | <el-tree-select v-model="form.sysDeptId" |
| | | :data="deptOptions" |
| | | :props="{ value: 'id', label: 'label', children: 'children' }" |
| | | value-key="id" |
| | | placeholder="è¯·éæ©é¨é¨" |
| | | check-strictly /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å¹´é¾ï¼" |
| | | prop="age"> |
| | | <el-input-number v-model="form.age" |
| | | :precision="0" |
| | | :step="1" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="第ä¸å¦åï¼" |
| | | prop="firstStudy"> |
| | | <el-input v-model="form.firstStudy" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¸ä¸ï¼" |
| | | prop="profession"> |
| | | <el-input v-model="form.profession" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="èç³»çµè¯ï¼" |
| | | prop="phone"> |
| | | <el-input v-model="form.phone" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç´§æ¥è系人ï¼" |
| | | prop="emergencyContact"> |
| | | <el-input v-model="form.emergencyContact" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç´§æ¥è系人èç³»çµè¯ï¼" |
| | | prop="emergencyContactPhone"> |
| | | <el-input v-model="form.emergencyContactPhone" |
| | | placeholder="请è¾å
¥" |
| | | clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ååå¹´éï¼" |
| | | prop="contractTerm"> |
| | | <el-input-number v-model="form.contractTerm" |
| | | :precision="0" |
| | | :step="1" |
| | | style="width: 100%" |
| | | :disabled="true" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ååå¼å§æ¥æï¼" |
| | | prop="contractStartTime"> |
| | | <el-date-picker v-model="form.contractStartTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="calculateContractTerm" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ååç»ææ¥æï¼" |
| | | prop="contractEndTime"> |
| | | <el-date-picker v-model="form.contractEndTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="calculateContractTerm" /> |
| | | </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, onMounted } from "vue"; |
| | | import { findPostOptions } from "@/api/system/post.js"; |
| | | import { listDept } from "@/api/system/dept.js"; |
| | | import { |
| | | staffOnJobInfo, |
| | | createStaffOnJob, |
| | | updateStaffOnJob, |
| | | } from "@/api/personnelManagement/staffOnJob.js"; |
| | | import { deptTreeSelect } from "@/api/system/user.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref(""); |
| | | const id = ref(0); |
| | | const data = reactive({ |
| | | form: { |
| | | staffNo: "", |
| | | staffName: "", |
| | | sex: "", |
| | | nativePlace: "", |
| | | postJob: "", |
| | | adress: "", |
| | | firstStudy: "", |
| | | profession: "", |
| | | age: 0, |
| | | phone: "", |
| | | emergencyContact: "", |
| | | emergencyContactPhone: "", |
| | | contractTerm: 0, |
| | | contractStartTime: "", |
| | | contractEndTime: "", |
| | | sysPostId: undefined, |
| | | sysDeptId: undefined, |
| | | }, |
| | | rules: { |
| | | staffNo: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | staffName: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | sex: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | nativePlace: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | postJob: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | adress: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | firstStudy: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | profession: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | age: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | phone: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | emergencyContact: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | emergencyContactPhone: [ |
| | | { required: true, message: "请è¾å
¥", trigger: "blur" }, |
| | | ], |
| | | contractTerm: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | contractStartTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | contractEndTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | sysDeptId: [{ required: true, message: "è¯·éæ©", trigger: "change" }], |
| | | }, |
| | | postOptions: [], // å²ä½é项 |
| | | deptOptions: [], // é¨é¨é项 |
| | | }); |
| | | const { form, rules, postOptions, deptOptions } = toRefs(data); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | if (operationType.value === "edit") { |
| | | id.value = row.id; |
| | | staffOnJobInfo(id.value, {}).then(res => { |
| | | form.value = { ...res.data }; |
| | | if (form.value.sysPostId === 0) { |
| | | form.value.sysPostId = undefined; |
| | | } |
| | | if (form.value.sysDeptId === 0) { |
| | | form.value.sysDeptId = undefined; |
| | | } |
| | | // ç¼è¾æ¶ä¹è®¡ç®ä¸æ¬¡ååå¹´é |
| | | calculateContractTerm(); |
| | | }); |
| | | } else { |
| | | form.value.id = ""; |
| | | } |
| | | }; |
| | | onMounted(() => { |
| | | fetchPostOptions(); |
| | | fetchDeptOptions(); |
| | | }); |
| | | |
| | | const fetchPostOptions = () => { |
| | | findPostOptions().then(res => { |
| | | postOptions.value = res.data; |
| | | }); |
| | | }; |
| | | |
| | | // æ¥è¯¢é¨é¨å表 |
| | | const fetchDeptOptions = () => { |
| | | deptTreeSelect().then(response => { |
| | | deptOptions.value = filterDisabledDept( |
| | | JSON.parse(JSON.stringify(response.data)) |
| | | ); |
| | | }); |
| | | }; |
| | | |
| | | /** è¿æ»¤ç¦ç¨çé¨é¨ */ |
| | | function filterDisabledDept(deptList) { |
| | | return deptList.filter(dept => { |
| | | if (dept.disabled) { |
| | | return false; |
| | | } |
| | | if (dept.children && dept.children.length) { |
| | | dept.children = filterDisabledDept(dept.children); |
| | | } |
| | | return true; |
| | | }); |
| | | } |
| | | |
| | | // æäº¤äº§å表å |
| | | const submitForm = () => { |
| | | if (!form.value.sysPostId) { |
| | | form.value.sysPostId = undefined; |
| | | } |
| | | if (!form.value.sysDeptId) { |
| | | form.value.sysDeptId = undefined; |
| | | } |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "add") { |
| | | createStaffOnJob(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | closeDia(); |
| | | }); |
| | | } else { |
| | | updateStaffOnJob(id.value, 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> |
| | | <el-dialog |
| | | v-model="isShow" |
| | | title="ç»ç¾åå" |
| | | width="800px" |
| | | @close="closeModal" |
| | | > |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-form-item label="ååå¼å§æ¥æï¼" prop="contractStartTime"> |
| | | <el-date-picker |
| | | v-model="form.contractStartTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="calculateContractTerm" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ååç»ææ¥æï¼" prop="contractEndTime"> |
| | | <el-date-picker |
| | | v-model="form.contractEndTime" |
| | | type="date" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" |
| | | @change="calculateContractTerm" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ååå¹´éï¼" prop="contractTerm"> |
| | | <el-input-number v-model="form.contractTerm" :precision="0" :step="1" style="width: 100%" :disabled="true"/> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button @click="closeModal">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | // ç»ç¾åå |
| | | import { renewContract } from "@/api/personnelManagement/staffOnJob.js"; |
| | | import {computed, getCurrentInstance,} from "vue"; |
| | | |
| | | const emit = defineEmits(['update:visible', 'completed']); |
| | | |
| | | const data = reactive({ |
| | | form: { |
| | | contractTerm: 0, |
| | | contractStartTime: "", |
| | | contractEndTime: "", |
| | | }, |
| | | rules: { |
| | | contractTerm: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | contractStartTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | contractEndTime: [{ required: true, message: "请è¾å
¥", trigger: "blur" }], |
| | | } |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | | let { proxy } = getCurrentInstance() |
| | | |
| | | const props = defineProps({ |
| | | id: { |
| | | type: Number, |
| | | default: 0, |
| | | }, |
| | | |
| | | visible: { |
| | | type: Boolean, |
| | | required: true, |
| | | }, |
| | | }) |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | | }, |
| | | set(val) { |
| | | emit('update:visible', val); |
| | | }, |
| | | }); |
| | | |
| | | // 计ç®ååå¹´é |
| | | 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 submitForm = () => { |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | | renewContract(props.id, form.value).then(res => { |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("ç»ç¾ååæå"); |
| | | emit('completed'); |
| | | closeModal(); |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeModal = () => { |
| | | // éç½®è¡¨åæ°æ® |
| | | form.value = { |
| | | contractTerm: 0, |
| | | contractStartTime: "", |
| | | contractEndTime: "", |
| | | }; |
| | | isShow.value = false; |
| | | }; |
| | | </script> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | title="详æ
" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :tableLoading="tableLoading" |
| | | height="600" |
| | | ></PIMTable> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeDia">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {staffOnJobInfo} from "@/api/personnelManagement/staffOnJob.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const tableColumn = ref([ |
| | | // { |
| | | // label: "ååå¹´é", |
| | | // prop: "contractTerm", |
| | | // }, |
| | | { |
| | | label: "ååå¼å§æ¥æ", |
| | | prop: "contractStartTime", |
| | | }, |
| | | { |
| | | label: "ååç»ææ¥æ", |
| | | prop: "contractEndTime", |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | |
| | | // æå¼å¼¹æ¡ |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | if (operationType.value === 'edit') { |
| | | staffOnJobInfo({staffNo: row.staffNo}).then(res => { |
| | | tableData.value = res.data |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // å
³éå¼¹æ¡ |
| | | const closeDia = () => { |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| | |
| | | > |
| | | </div> |
| | | <div> |
| | | <!-- <el-button type="primary" @click="openForm('add')">æ°å¢å
¥è</el-button>--> |
| | | <el-button type="primary" @click="openFormNewOrEditFormDia('add')">æ°å¢å
¥è</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | <!-- <el-button type="danger" plain @click="handleDelete">å é¤</el-button>--> |
| | | <el-button type="danger" plain @click="handleDelete">å é¤</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | <show-form-dia ref="formDia" @close="handleQuery"></show-form-dia> |
| | | <new-or-edit-form-dia ref="formDiaNewOrEditFormDia" @close="handleQuery"></new-or-edit-form-dia> |
| | | <renew-contract |
| | | v-if="isShowRenewContractModal" |
| | | v-model:visible="isShowRenewContractModal" |
| | | :id="id" |
| | | @completed="handleQuery" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import {onMounted, ref} from "vue"; |
| | | import FormDia from "@/views/personnelManagement/employeeRecord/components/formDia.vue"; |
| | | import {ElMessageBox} from "element-plus"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/employeeRecord.js"; |
| | | import {batchDeleteStaffOnJobs, staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import dayjs from "dayjs"; |
| | | const NewOrEditFormDia = defineAsyncComponent(() => import("@/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue")); |
| | | const ShowFormDia = defineAsyncComponent(() => import( "@/views/personnelManagement/employeeRecord/components/Show.vue")); |
| | | const RenewContract = defineAsyncComponent(() => import( "@/views/personnelManagement/employeeRecord/components/RenewContract.vue")); |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | const isShowRenewContractModal = ref(false); |
| | | const id = ref(0); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "ç¶æ", |
| | |
| | | { |
| | | label: "æ·ç±ä½å", |
| | | prop: "nativePlace", |
| | | }, |
| | | { |
| | | label: "é¨é¨", |
| | | prop: "deptName", |
| | | }, |
| | | { |
| | | label: "å²ä½", |
| | |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: 'right', |
| | | width: 180, |
| | | operation: [ |
| | | { |
| | | name: "详æ
", |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | openForm("edit", row); |
| | | openFormNewOrEditFormDia("edit", row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç»ç¾åå", |
| | | type: "text", |
| | | showHide: row => row.staffState === 1, |
| | | clickFun: (row) => { |
| | | isShowRenewContractModal.value = true; |
| | | id.value = row.id; |
| | | }, |
| | | }, |
| | | // { |
| | | // name: "详æ
", |
| | | // type: "text", |
| | | // clickFun: (row) => { |
| | | // openForm("edit", row); |
| | | // }, |
| | | // }, |
| | | ], |
| | | }, |
| | | ]); |
| | |
| | | total: 0 |
| | | }); |
| | | const formDia = ref() |
| | | const formDiaNewOrEditFormDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | const changeDaterange = (value) => { |
| | |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined |
| | | staffOnJobListPage({...params, staffState: 1}).then(res => { |
| | | staffOnJobListPage({...params}).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | page.total = res.data.total; |
| | |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | }; |
| | | const openFormNewOrEditFormDia = (type, row) => { |
| | | nextTick(() => { |
| | | formDiaNewOrEditFormDia.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(() => { |
| | | batchDeleteStaffOnJobs(ids).then((res) => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/staff/staffOnJob/export", {staffState: 1}, "å¨èåå·¥å°è´¦.xlsx"); |
| | | proxy.download("/staff/staffOnJob/export", {staffState: 1}, "åå·¥å°è´¦.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="dialogVisible" |
| | | :title="title" |
| | | width="700px" |
| | | :close-on-click-modal="false"> |
| | | <el-form ref="formRef" |
| | | :model="form" |
| | | :rules="rules" |
| | | label-width="140px" |
| | | label-position="top"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ç»è®¡æä»½" |
| | | prop="payDate"> |
| | | <el-date-picker v-model="form.payDate" |
| | | type="month" |
| | | value-format="YYYY-MM" |
| | | format="YYYY-MM" |
| | | placeholder="è¯·éæ©æä»½" |
| | | style="width: 100%" |
| | | :disabled="operationType === 'view'" /> |
| | | </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%" |
| | | :disabled="operationType === 'view'"> |
| | | <el-option v-for="item in userList" |
| | | :key="item.id" |
| | | :label="item.staffName" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åºæ¬å·¥èµ" |
| | | prop="basicSalary"> |
| | | <el-input v-model="form.basicSalary" |
| | | type="number" |
| | | placeholder="请è¾å
¥åºæ¬å·¥èµ" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="计件工èµ" |
| | | prop="pieceworkSalary"> |
| | | <el-input v-model="form.pieceworkSalary" |
| | | type="number" |
| | | placeholder="请è¾å
¥è®¡ä»¶å·¥èµ" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="计æ¶å·¥èµ" |
| | | prop="hourlySalary"> |
| | | <el-input v-model="form.hourlySalary" |
| | | type="number" |
| | | placeholder="请è¾å
¥è®¡æ¶å·¥èµ" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¶ä»æ¶å
¥" |
| | | prop="otherIncome"> |
| | | <el-input v-model="form.otherIncome" |
| | | type="number" |
| | | placeholder="请è¾å
¥å
¶ä»æ¶å
¥" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="社ä¿ä¸ªäºº" |
| | | prop="socialSecurityIndividuals"> |
| | | <el-input v-model="form.socialSecurityIndividuals" |
| | | type="number" |
| | | placeholder="请è¾å
¥ç¤¾ä¿ä¸ªäºº" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¬ç§¯é个人" |
| | | prop="providentFundIndividuals"> |
| | | <el-input v-model="form.providentFundIndividuals" |
| | | type="number" |
| | | placeholder="请è¾å
¥å
¬ç§¯é个人" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="个人æå¾ç¨" |
| | | prop="personalIncomeTax"> |
| | | <el-input v-model="form.personalIncomeTax" |
| | | type="number" |
| | | placeholder="请è¾å
¥ä¸ªäººæå¾ç¨" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="å
¶ä»æ£æ¬¾" |
| | | prop="otherDeductions"> |
| | | <el-input v-model="form.otherDeductions" |
| | | type="number" |
| | | placeholder="请è¾å
¥å
¶ä»æ£æ¬¾" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="夿³¨" |
| | | prop="remark"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | placeholder="请è¾å
¥å¤æ³¨" |
| | | :rows="3" |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm" |
| | | v-if="operationType !== 'view'"> |
| | | ç¡®å® |
| | | </el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { |
| | | monthlyStatisticsAdd, |
| | | monthlyStatisticsUpdate, |
| | | staffOnJobList, |
| | | } from "@/api/personnelManagement/monthlyStatistics.js"; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | operationType: { |
| | | type: String, |
| | | default: "add", |
| | | }, |
| | | row: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:modelValue", "close"]); |
| | | |
| | | const dialogVisible = computed({ |
| | | get: () => props.modelValue, |
| | | set: val => emit("update:modelValue", val), |
| | | }); |
| | | |
| | | const title = computed(() => { |
| | | if (props.operationType === "add") return "æ°å¢èªèµå°è´¦"; |
| | | if (props.operationType === "edit") return "ç¼è¾èªèµå°è´¦"; |
| | | return "æ¥çèªèµå°è´¦"; |
| | | }); |
| | | |
| | | const formRef = ref(); |
| | | const form = reactive({ |
| | | id: "", |
| | | payDate: "", |
| | | staffId: "", |
| | | basicSalary: 0, |
| | | pieceworkSalary: 0, |
| | | hourlySalary: 0, |
| | | otherIncome: 0, |
| | | socialSecurityIndividuals: 0, |
| | | providentFundIndividuals: 0, |
| | | personalIncomeTax: 0, |
| | | otherDeductions: 0, |
| | | payableWages: 0, |
| | | deductibleWages: 0, |
| | | actualWages: 0, |
| | | remark: "", |
| | | }); |
| | | |
| | | const rules = { |
| | | payDate: [{ required: true, message: "è¯·éæ©ç»è®¡æä»½", trigger: "change" }], |
| | | staffId: [{ required: true, message: "è¯·éæ©åå·¥", trigger: "change" }], |
| | | basicSalary: [{ required: true, message: "请è¾å
¥åºæ¬å·¥èµ", trigger: "blur" }], |
| | | }; |
| | | |
| | | const userList = ref([]); |
| | | |
| | | const loadUserList = () => { |
| | | // userListNoPage().then(res => { |
| | | // userList.value = res.data || []; |
| | | // }); |
| | | staffOnJobList().then(res => { |
| | | userList.value = res.data || []; |
| | | }); |
| | | }; |
| | | |
| | | const openDialog = (type, row) => { |
| | | // é置表å |
| | | Object.assign(form, { |
| | | id: "", |
| | | payDate: "", |
| | | staffId: "", |
| | | basicSalary: 0, |
| | | pieceworkSalary: 0, |
| | | hourlySalary: 0, |
| | | otherIncome: 0, |
| | | socialSecurityIndividuals: 0, |
| | | providentFundIndividuals: 0, |
| | | personalIncomeTax: 0, |
| | | otherDeductions: 0, |
| | | payableWages: 0, |
| | | deductibleWages: 0, |
| | | actualWages: 0, |
| | | remark: "", |
| | | }); |
| | | |
| | | if (type === "add") { |
| | | dialogVisible.value = true; |
| | | } else if (type === "edit" || type === "view") { |
| | | if (row && row.id) { |
| | | Object.assign(form, row); |
| | | dialogVisible.value = true; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const submitForm = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | form.basicSalary = Number(form.basicSalary); |
| | | form.pieceworkSalary = Number(form.pieceworkSalary); |
| | | form.hourlySalary = Number(form.hourlySalary); |
| | | form.otherIncome = Number(form.otherIncome); |
| | | form.socialSecurityIndividuals = Number(form.socialSecurityIndividuals); |
| | | form.providentFundIndividuals = Number(form.providentFundIndividuals); |
| | | form.personalIncomeTax = Number(form.personalIncomeTax); |
| | | form.otherDeductions = Number(form.otherDeductions); |
| | | |
| | | // 计ç®åºåå·¥èµãåºæ£å·¥èµåå®åå·¥èµ |
| | | const payableWages = |
| | | form.basicSalary + |
| | | form.pieceworkSalary + |
| | | form.hourlySalary + |
| | | form.otherIncome; |
| | | const deductibleWages = |
| | | form.socialSecurityIndividuals + |
| | | form.providentFundIndividuals + |
| | | form.personalIncomeTax + |
| | | form.otherDeductions; |
| | | const actualWages = payableWages - deductibleWages; |
| | | |
| | | const submitData = { |
| | | ...form, |
| | | payableWages, |
| | | deductibleWages, |
| | | actualWages, |
| | | }; |
| | | |
| | | if (props.operationType === "add") { |
| | | monthlyStatisticsAdd(submitData).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("æ°å¢æå"); |
| | | dialogVisible.value = false; |
| | | emit("close"); |
| | | } else { |
| | | ElMessage.error(res.msg || "æ°å¢å¤±è´¥"); |
| | | } |
| | | }); |
| | | } else if (props.operationType === "edit") { |
| | | monthlyStatisticsUpdate(submitData).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("æ´æ°æå"); |
| | | dialogVisible.value = false; |
| | | emit("close"); |
| | | } else { |
| | | ElMessage.error(res.msg || "æ´æ°å¤±è´¥"); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | loadUserList(); |
| | | }); |
| | | |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title">å§åï¼</span> |
| | | <el-input v-model="searchForm.staffName" |
| | | 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 @click="handleExport" |
| | | style="margin-right: 10px">导åº</el-button> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">æ°å¢å°è´¦</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 v-model="dialogVisible" |
| | | :operation-type="operationType" |
| | | :row="currentRow" |
| | | ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { |
| | | onMounted, |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | getCurrentInstance, |
| | | nextTick, |
| | | } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import FormDia from "./components/formDia.vue"; |
| | | import { |
| | | monthlyStatisticsListPage, |
| | | monthlyStatisticsDelete, |
| | | } from "@/api/personnelManagement/monthlyStatistics.js"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | staffName: "", |
| | | payDateStr: "", |
| | | }, |
| | | }); |
| | | |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "åå·¥å§å", |
| | | prop: "staffName", |
| | | }, |
| | | { |
| | | label: "é¨é¨", |
| | | prop: "deptName", |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "æä»½", |
| | | prop: "payDate", |
| | | }, |
| | | { |
| | | label: "åºæ¬å·¥èµ", |
| | | prop: "basicSalary", |
| | | }, |
| | | { |
| | | label: "计件工èµ", |
| | | prop: "pieceworkSalary", |
| | | }, |
| | | { |
| | | label: "计æ¶å·¥èµ", |
| | | prop: "hourlySalary", |
| | | }, |
| | | { |
| | | label: "å
¶ä»æ¶å
¥", |
| | | prop: "otherIncome", |
| | | }, |
| | | { |
| | | label: "社ä¿ä¸ªäºº", |
| | | prop: "socialSecurityIndividuals", |
| | | }, |
| | | { |
| | | label: "å
¬ç§¯é个人", |
| | | prop: "providentFundIndividuals", |
| | | width: 140, |
| | | }, |
| | | { |
| | | label: "å·¥èµä¸ªç¨", |
| | | prop: "personalIncomeTax", |
| | | }, |
| | | { |
| | | label: "å
¶ä»æ¯åº", |
| | | prop: "otherDeductions", |
| | | }, |
| | | { |
| | | label: "åºåå·¥èµ", |
| | | prop: "payableWages", |
| | | }, |
| | | { |
| | | label: "åºæ£å·¥èµ", |
| | | prop: "deductibleWages", |
| | | }, |
| | | { |
| | | label: "å®åå·¥èµ", |
| | | prop: "actualWages", |
| | | }, |
| | | { |
| | | label: "夿³¨", |
| | | prop: "remark", |
| | | width: 150, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | width: 220, |
| | | operation: [ |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | | // { |
| | | // name: "æ¥ç", |
| | | // type: "text", |
| | | // clickFun: row => { |
| | | // openForm("view", row); |
| | | // }, |
| | | // }, |
| | | ], |
| | | }, |
| | | ]); |
| | | |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | |
| | | const formDia = ref(); |
| | | const dialogVisible = ref(false); |
| | | const operationType = ref("add"); |
| | | const currentRow = ref({}); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | monthlyStatisticsListPage({ ...page, ...searchForm.value }) |
| | | .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) => { |
| | | operationType.value = type; |
| | | currentRow.value = row || {}; |
| | | dialogVisible.value = true; |
| | | 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(() => { |
| | | monthlyStatisticsDelete(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleExport = () => { |
| | | ElMessageBox.confirm("æ¯å¦ç¡®è®¤å¯¼åºäººåèªèµå°è´¦ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download( |
| | | "/compensationPerformance/export", |
| | | { ...searchForm.value, ...page }, |
| | | "人åèªèµå°è´¦.xlsx" |
| | | ); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .search_form { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .search_title { |
| | | font-weight: 500; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .ml10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .table_list { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | </style> |
| | |
| | | |
| | | <script setup> |
| | | import {ref} from "vue"; |
| | | import {getStaffJoinInfo, getStaffOnJob, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js"; |
| | | import {compensationAdd, compensationUpdate} from "@/api/personnelManagement/payrollManagement.js"; |
| | | import {staffOnJobInfo, staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | |
| | |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | getStaffOnJob().then(res => { |
| | | personList.value = res.data |
| | | staffOnJobListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | staffState: 1 |
| | | }).then(res => { |
| | | personList.value = res.data.records || [] |
| | | }) |
| | | form.value = {} |
| | | if (operationType.value === 'edit') { |
| | | getStaffJoinInfo(row.id).then(res => { |
| | | staffOnJobInfo(row.staffId).then(res => { |
| | | form.value = {...row} |
| | | form.value.payDate = form.value.payDate + '-01' |
| | | }) |
| | |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} 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"; |
| | |
| | | import {useDict} from "@/utils/dict.js" |
| | | import {Plus, Download, Search, Refresh} from '@element-plus/icons-vue' |
| | | import {save, del, delByIds, listPage} from "@/api/personnelManagement/scheduling.js" |
| | | import {getStaffOnJob} from "@/api/personnelManagement/onboarding.js"; |
| | | import dayjs from "dayjs"; |
| | | import pagination from "@/components/PIMTable/Pagination.vue"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | |
| | | * è·åå½åå¨è人åå表 |
| | | */ |
| | | const getPersonList = () => { |
| | | getStaffOnJob().then(res => { |
| | | personList.value = res.data |
| | | staffOnJobListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | staffState: 1 |
| | | }).then(res => { |
| | | personList.value = res.data.records || [] |
| | | }) |
| | | }; |
| | | const paginationChange = (obj) => { |
| | |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | import { getUserProfile } from '@/api/system/user.js' |
| | | import {staffJoinUpdate, staffJoinListPage} from "@/api/personnelManagement/onboarding.js"; |
| | | import { fa, id } from 'element-plus/es/locales.mjs' |
| | | import {staffOnJobListPage, updateStaffOnJob} from "@/api/personnelManagement/staffOnJob.js"; |
| | | |
| | | const tableLoading = ref(false) |
| | | // å页忰 |
| | |
| | | currentUser.value = res.data |
| | | // console.log("----",currentUser.value) |
| | | //å¾å°äººåå表 |
| | | staffJoinListPage({staffState: 1}).then(res => { |
| | | staffOnJobListPage({staffState: 1}).then(res => { |
| | | //çéåºåcurrentUserååç人å |
| | | // let tableData = res.data.records |
| | | user.value = res.data.records.find(item => item.staffName === currentUser.value.userName) |
| | |
| | | const userRes = await getUserProfile(); |
| | | if (userRes.code === 200) { |
| | | currentUser.value = userRes.data; |
| | | const staffListRes = await staffJoinListPage({ staffState: 1 }); |
| | | const staffListRes = await staffOnJobListPage({ staffState: 1 }); |
| | | user.value = staffListRes.data.records.find(item => item.staffName === currentUser.value.userName); |
| | | // console.log("++++", user.value); |
| | | |
| | | Object.assign(joinForm, user.value); |
| | | joinForm.staffName = profileForm.name; |
| | | joinForm.phone = profileForm.phone; |
| | | joinForm.email = profileForm.email; |
| | | joinForm.adress = profileForm.adress; |
| | | console.log(joinForm) |
| | | // è°ç¨æ´æ°ä¸ªäººä¿¡æ¯çæ¥å£ |
| | | staffJoinUpdate(joinForm).then(res => { |
| | | updateStaffOnJob(user.value.id, joinForm).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success('个人信æ¯ä¿åæå'); |
| | | getProfile(); |