接口调试
This commit is contained in:
parent
aec7da7272
commit
9f6c55c54b
|
|
@ -10,6 +10,7 @@
|
|||
ref="pickerRef"
|
||||
:show="showPicker"
|
||||
:columns="columns"
|
||||
:defaultIndex="defaultIndex"
|
||||
closeOnClickOverlay
|
||||
@cancel="handleCancel"
|
||||
@confirm="handleConfirm"
|
||||
|
|
@ -94,7 +95,7 @@ const displayValue = computed(() => {
|
|||
}
|
||||
|
||||
// 如果是字符串数组
|
||||
if (typeof props.options[0] === 'string') {
|
||||
if (props.options.length > 0 && typeof props.options[0] === 'string') {
|
||||
return props.options.find((item) => item === props.modelValue) || ''
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +104,29 @@ const displayValue = computed(() => {
|
|||
return option ? option[props.labelKey] : ''
|
||||
})
|
||||
|
||||
/**
|
||||
* 计算默认选中索引
|
||||
* 业务背景:当选择器打开时,需要根据当前 modelValue 找到对应的选项索引,以便选择器默认选中该项
|
||||
* 设计决策:通过遍历 options 数组,找到与 modelValue 匹配的选项索引
|
||||
*/
|
||||
const defaultIndex = computed(() => {
|
||||
if (!props.modelValue || props.options.length === 0) {
|
||||
return [0]
|
||||
}
|
||||
|
||||
// 如果是字符串数组
|
||||
if (typeof props.options[0] === 'string') {
|
||||
const index = props.options.findIndex((item) => item === props.modelValue)
|
||||
return [index >= 0 ? index : 0]
|
||||
}
|
||||
|
||||
// 如果是对象数组
|
||||
const index = props.options.findIndex(
|
||||
(item) => String(item[props.valueKey]) === String(props.modelValue),
|
||||
)
|
||||
return [index >= 0 ? index : 0]
|
||||
})
|
||||
|
||||
/**
|
||||
* 打开选择器
|
||||
*/
|
||||
|
|
@ -119,12 +143,32 @@ const handleCancel = () => {
|
|||
|
||||
/**
|
||||
* 确认选择
|
||||
* @param {Object} e - 事件对象
|
||||
* 业务背景:处理用户确认选择的操作,获取选中的值并触发更新
|
||||
* 设计决策:
|
||||
* 1. up-picker 的 confirm 事件返回的 e.value[0] 是一个对象,包含 text 和 value 属性
|
||||
* 2. update:modelValue 传递值,符合 v-model 标准
|
||||
* 3. change 事件传递整个项对象(包含 label 和 value),方便父组件使用
|
||||
* @param {Object} e - 事件对象,e.value[0] 包含 { text, value }
|
||||
*/
|
||||
const handleConfirm = (e) => {
|
||||
const selectedValue = e.value[0]
|
||||
// up-picker 返回的 e.value[0] 是一个对象,包含 text 和 value
|
||||
const selectedItem = e.value[0]
|
||||
const selectedValue = selectedItem?.value ?? selectedItem
|
||||
|
||||
// 从原始 options 中找到对应的项对象,以便传递完整的 label 和 value
|
||||
let changeItem = null
|
||||
if (props.options.length > 0) {
|
||||
if (typeof props.options[0] === 'string') {
|
||||
changeItem = { label: selectedItem?.text, value: selectedValue }
|
||||
} else {
|
||||
changeItem = props.options.find(
|
||||
(item) => String(item[props.valueKey]) === String(selectedValue),
|
||||
) || { label: selectedItem?.text, value: selectedValue }
|
||||
}
|
||||
}
|
||||
|
||||
emit('update:modelValue', selectedValue)
|
||||
emit('change', selectedValue)
|
||||
emit('change', changeItem || { label: selectedItem?.text, value: selectedValue })
|
||||
showPicker.value = false
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
ref="datePickerRef"
|
||||
:mode="pickerMode"
|
||||
:show="showPicker"
|
||||
:minDate="minDate"
|
||||
:maxDate="maxDate"
|
||||
:minDate="computedMinDate"
|
||||
:maxDate="computedMaxDate"
|
||||
v-model="pickerValue"
|
||||
@cancel="handleCancel"
|
||||
@confirm="handleConfirm"
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/**
|
||||
|
|
@ -102,6 +102,28 @@ const pickerMode = computed(() => {
|
|||
return 'date'
|
||||
})
|
||||
|
||||
/**
|
||||
* 计算最小日期
|
||||
* 业务背景:当 minDate 为 0 时,会被解析为 1970-01-01,导致选择器只能选择 1970 年
|
||||
* 设计决策:如果 minDate 为 0,则不传递该属性,让选择器使用默认的最小日期范围
|
||||
*/
|
||||
const computedMinDate = computed(() => {
|
||||
// 如果 minDate 为 0,返回 undefined,不限制最小日期
|
||||
// 这样选择器会使用合理的默认范围(通常是当前年份的前几年)
|
||||
return props.minDate === 0 ? undefined : props.minDate
|
||||
})
|
||||
|
||||
/**
|
||||
* 计算最大日期
|
||||
* 业务背景:当 maxDate 为 0 时,会被解析为 1970-01-01,导致选择器只能选择 1970 年
|
||||
* 设计决策:如果 maxDate 为 0,则不传递该属性,让选择器使用默认的最大日期范围
|
||||
*/
|
||||
const computedMaxDate = computed(() => {
|
||||
// 如果 maxDate 为 0,返回 undefined,不限制最大日期
|
||||
// 这样选择器会使用合理的默认范围(通常是当前年份的后几年)
|
||||
return props.maxDate === 0 ? undefined : props.maxDate
|
||||
})
|
||||
|
||||
// 计算显示值
|
||||
const displayValue = computed(() => {
|
||||
if (!props.modelValue) {
|
||||
|
|
@ -123,6 +145,7 @@ const displayValue = computed(() => {
|
|||
const pickerValue = computed({
|
||||
get: () => {
|
||||
if (props.modelValue) {
|
||||
console.log(props.modelValue, 'props.modelValue')
|
||||
return typeof props.modelValue === 'number'
|
||||
? props.modelValue
|
||||
: new Date(props.modelValue).getTime()
|
||||
|
|
|
|||
|
|
@ -38,10 +38,13 @@
|
|||
<scroll-view v-if="activeTab === 'form'" class="scroll-container" scroll-y>
|
||||
<up-form :model="formData" :rules="rules" ref="formRef" labelWidth="100">
|
||||
<!-- 请假类型 -->
|
||||
<up-form-item label="请假类型" prop="leaveType" required :borderBottom="true">
|
||||
<up-form-item label="请假类型" prop="leaveTypeId" required :borderBottom="true">
|
||||
<view class="form-picker-wrapper" @tap="handleOpenLeaveTypePicker">
|
||||
<text class="picker-text" :class="{ placeholder: !formData.leaveType }">
|
||||
{{ leaveTypeText || '请选择请假类型' }}
|
||||
<text
|
||||
class="picker-text"
|
||||
:class="{ placeholder: !formData.leaveTypeId }"
|
||||
>
|
||||
{{ formData.leaveTypeName || '请选择请假类型' }}
|
||||
</text>
|
||||
<up-icon name="arrow-right" size="16" color="#999" />
|
||||
</view>
|
||||
|
|
@ -50,8 +53,8 @@
|
|||
<!-- 请假日期 -->
|
||||
<up-form-item label="请假日期" prop="leaveDate" required :borderBottom="true">
|
||||
<view class="form-picker-wrapper" @tap="handleOpenDatePicker">
|
||||
<text class="picker-text" :class="{ placeholder: !formData.leaveDate }">
|
||||
{{ leaveDateText || '请选择请假日期' }}
|
||||
<text class="picker-text" :class="{ placeholder: !leaveDateText }">
|
||||
{{ leaveDateText || '请选择请假日期范围' }}
|
||||
</text>
|
||||
<up-icon name="arrow-right" size="16" color="#999" />
|
||||
</view>
|
||||
|
|
@ -94,9 +97,12 @@
|
|||
</up-form-item>
|
||||
|
||||
<!-- 审批人 -->
|
||||
<up-form-item label="审批人" prop="approver" :borderBottom="true">
|
||||
<up-form-item label="审批人" prop="approverId" :borderBottom="true">
|
||||
<view class="form-picker-wrapper" @tap="handleOpenApproverPicker">
|
||||
<text class="picker-text" :class="{ placeholder: !formData.approver }">
|
||||
<text
|
||||
class="picker-text"
|
||||
:class="{ placeholder: !formData.approverId }"
|
||||
>
|
||||
{{ approverText || '请选择审批人' }}
|
||||
</text>
|
||||
<up-icon name="arrow-right" size="16" color="#999" />
|
||||
|
|
@ -106,6 +112,7 @@
|
|||
<!-- 上传附件 -->
|
||||
<up-form-item label="上传附件" prop="attachments" :borderBottom="true">
|
||||
<up-upload
|
||||
accept="file"
|
||||
:fileList="fileList"
|
||||
@afterRead="handleAfterRead"
|
||||
@delete="handleDelete"
|
||||
|
|
@ -119,7 +126,7 @@
|
|||
<!-- 提交按钮 -->
|
||||
<view class="submit-section">
|
||||
<up-button
|
||||
text="提交请假"
|
||||
:text="submitButtonText"
|
||||
type="primary"
|
||||
:loading="submitting"
|
||||
@tap="handleSubmit"
|
||||
|
|
@ -164,10 +171,72 @@
|
|||
class="record-item"
|
||||
@tap="handleRecordClick(item)"
|
||||
>
|
||||
<view class="record-content">
|
||||
<text class="record-title">{{ item.title || '请假记录' }}</text>
|
||||
<text class="record-subtitle">{{ item.subtitle || '暂无描述' }}</text>
|
||||
<text class="record-time">{{ item.createTime || '2025-01-01' }}</text>
|
||||
<view class="record-header">
|
||||
<view class="record-user">
|
||||
<up-icon name="account" size="18" color="#07c160" />
|
||||
<text class="record-title">{{ item.title || '请假记录' }}</text>
|
||||
</view>
|
||||
<text class="record-date">{{ item.createTime || '--' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="record-type">
|
||||
<text>请假类型:{{ item.typeText || '暂无类型' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="record-body">
|
||||
<view class="record-range">
|
||||
<up-icon name="calendar" size="16" color="#999" />
|
||||
<text class="record-range-text">
|
||||
{{ item.dateRange || '暂无日期' }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="record-days" v-if="item.leaveCnt">
|
||||
{{ item.leaveCnt }}天
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="record-status">
|
||||
<view class="status-left">
|
||||
<text
|
||||
class="status-tag"
|
||||
:class="`status-${item.statusType || 'default'}`"
|
||||
>
|
||||
{{ item.statusText || '待审批' }}
|
||||
</text>
|
||||
<text
|
||||
v-if="item.statusType === 'success'"
|
||||
class="status-tag status-success status-outline"
|
||||
>
|
||||
结束
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="record-actions">
|
||||
<template v-if="item.canEdit">
|
||||
<up-button
|
||||
text="修改"
|
||||
size="small"
|
||||
type="primary"
|
||||
plain
|
||||
@tap.stop="handleModify(item)"
|
||||
/>
|
||||
<up-button
|
||||
text="撤销"
|
||||
size="small"
|
||||
type="warning"
|
||||
plain
|
||||
@tap.stop="handleRevoke(item)"
|
||||
/>
|
||||
</template>
|
||||
<up-button
|
||||
v-else
|
||||
text="结束"
|
||||
size="small"
|
||||
type="success"
|
||||
plain
|
||||
@tap.stop="handleFinish(item)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -193,13 +262,23 @@
|
|||
@close="showLeaveTypePicker = false"
|
||||
/>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<!-- 开始日期选择器 -->
|
||||
<up-datetime-picker
|
||||
:show="showDatePicker"
|
||||
:show="showStartDatePicker"
|
||||
mode="date"
|
||||
v-model="datePickerValue"
|
||||
@cancel="showDatePicker = false"
|
||||
@confirm="handleDateConfirm"
|
||||
v-model="startDatePickerValue"
|
||||
@cancel="showStartDatePicker = false"
|
||||
@confirm="handleStartDateConfirm"
|
||||
/>
|
||||
|
||||
<!-- 结束日期选择器 -->
|
||||
<up-datetime-picker
|
||||
:show="showEndDatePicker"
|
||||
mode="date"
|
||||
v-model="endDatePickerValue"
|
||||
:minDate="computedEndDateMinDate"
|
||||
@cancel="showEndDatePicker = false"
|
||||
@confirm="handleEndDateConfirm"
|
||||
/>
|
||||
|
||||
<!-- 审批人选择器 -->
|
||||
|
|
@ -229,6 +308,10 @@ import {
|
|||
submitLeaveRequestApi,
|
||||
getMyLeaveRecordListApi,
|
||||
getLeaveApprovalListApi,
|
||||
getLeaveDetailApi,
|
||||
getPersonPerformanceDaysApi,
|
||||
checkHasUnfinishedLeaveApi,
|
||||
endLeaveRequestApi,
|
||||
} from '@/services/leader/leave-request'
|
||||
|
||||
/**
|
||||
|
|
@ -250,24 +333,30 @@ const contentStyle = computed(() => {
|
|||
|
||||
// 当前激活的Tab
|
||||
const activeTab = ref('form') // 'form' | 'record' | 'approval'
|
||||
const isEditing = ref(false)
|
||||
const editingRecord = ref(null)
|
||||
|
||||
// 表单相关
|
||||
const formRef = ref(null)
|
||||
const formData = ref({
|
||||
leaveType: '',
|
||||
leaveDate: null,
|
||||
leaveTypeId: '',
|
||||
leaveTypeName: '',
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
reason: '',
|
||||
duration: '0',
|
||||
beforeDays: '0',
|
||||
afterDays: '0',
|
||||
approver: '',
|
||||
approverId: '',
|
||||
approverName: '',
|
||||
attachments: [],
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const rules = ref({
|
||||
leaveType: [
|
||||
leaveTypeId: [
|
||||
{
|
||||
type: 'number',
|
||||
required: true,
|
||||
message: '请选择请假类型',
|
||||
trigger: 'change',
|
||||
|
|
@ -275,8 +364,13 @@ const rules = ref({
|
|||
],
|
||||
leaveDate: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择请假日期',
|
||||
validator: (rule, value, callback) => {
|
||||
if (!formData.value.startDate || !formData.value.endDate) {
|
||||
callback(new Error('请选择请假日期范围'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
|
|
@ -294,25 +388,37 @@ const showLeaveTypePicker = ref(false)
|
|||
const leaveTypeColumns = computed(() => {
|
||||
return [
|
||||
leaveTypeList.value.map((item) => ({
|
||||
text: item.dictLabel || item.label,
|
||||
value: item.dictValue || item.value,
|
||||
text: item.dictLabel,
|
||||
value: item.dictCode || item.dictValue,
|
||||
})),
|
||||
]
|
||||
})
|
||||
const leaveTypeText = computed(() => {
|
||||
if (!formData.value.leaveType) return ''
|
||||
const item = leaveTypeList.value.find(
|
||||
(item) => (item.dictValue || item.value) === formData.value.leaveType,
|
||||
)
|
||||
return item ? item.dictLabel || item.label : ''
|
||||
})
|
||||
|
||||
// 日期相关
|
||||
const showDatePicker = ref(false)
|
||||
const datePickerValue = ref(Date.now())
|
||||
const showStartDatePicker = ref(false)
|
||||
const showEndDatePicker = ref(false)
|
||||
const startDatePickerValue = ref(Date.now())
|
||||
const endDatePickerValue = ref(Date.now())
|
||||
|
||||
/**
|
||||
* 计算结束日期选择器的最小日期
|
||||
* 业务背景:确保结束日期不早于开始日期
|
||||
* 设计决策:如果已选择开始日期,则结束日期的最小值为开始日期;否则为当前日期
|
||||
*/
|
||||
const computedEndDateMinDate = computed(() => {
|
||||
if (formData.value.startDate) {
|
||||
return typeof formData.value.startDate === 'number'
|
||||
? formData.value.startDate
|
||||
: new Date(formData.value.startDate).getTime()
|
||||
}
|
||||
return Date.now()
|
||||
})
|
||||
|
||||
const leaveDateText = computed(() => {
|
||||
if (!formData.value.leaveDate) return ''
|
||||
return dayjs(formData.value.leaveDate).format('YYYY-MM-DD')
|
||||
if (!formData.value.startDate || !formData.value.endDate) return ''
|
||||
const start = dayjs(formData.value.startDate).format('YYYY-MM-DD')
|
||||
const end = dayjs(formData.value.endDate).format('YYYY-MM-DD')
|
||||
return `${start} 至 ${end}`
|
||||
})
|
||||
|
||||
// 审批人相关
|
||||
|
|
@ -327,11 +433,8 @@ const approverColumns = computed(() => {
|
|||
]
|
||||
})
|
||||
const approverText = computed(() => {
|
||||
if (!formData.value.approver) return ''
|
||||
const item = approverList.value.find(
|
||||
(item) => (item.userId || item.id) === formData.value.approver,
|
||||
)
|
||||
return item ? item.nickName || item.name : ''
|
||||
if (!formData.value.approverId) return ''
|
||||
return formData.value.approverName || ''
|
||||
})
|
||||
|
||||
// 记录列表相关
|
||||
|
|
@ -342,11 +445,33 @@ const loading = ref(false)
|
|||
// 查询参数
|
||||
const queryParams = ref({
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
year: new Date().getFullYear().toString(),
|
||||
month: '',
|
||||
})
|
||||
|
||||
/**
|
||||
* 计算请假记录查询的时间范围
|
||||
* 业务背景:后端列表接口要求按起止日期筛选,不接受 pageSize
|
||||
* 设计决策:保留年/月选择体验,将其转换为 startTime、endTime
|
||||
*/
|
||||
const computedRecordRange = computed(() => {
|
||||
const year = queryParams.value.year
|
||||
const month = queryParams.value.month
|
||||
|
||||
if (month) {
|
||||
const start = dayjs(`${year}-${month}-01`)
|
||||
return {
|
||||
startTime: start.format('YYYY-MM-DD'),
|
||||
endTime: start.endOf('month').format('YYYY-MM-DD'),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
startTime: dayjs(`${year}-01-01`).format('YYYY-MM-DD'),
|
||||
endTime: dayjs(`${year}-12-31`).format('YYYY-MM-DD'),
|
||||
}
|
||||
})
|
||||
|
||||
// 年份选项(当前年份前后5年)
|
||||
const yearOptions = computed(() => {
|
||||
const currentYear = new Date().getFullYear()
|
||||
|
|
@ -377,6 +502,14 @@ const monthOptions = ref([
|
|||
{ label: '12月', value: '12' },
|
||||
])
|
||||
|
||||
/**
|
||||
* 计算提交按钮文案
|
||||
* 业务背景:编辑模式需要提示“保存修改”,新增则为“提交请假”
|
||||
*/
|
||||
const submitButtonText = computed(() => {
|
||||
return isEditing.value ? '保存修改' : '提交请假'
|
||||
})
|
||||
|
||||
/**
|
||||
* 计算是否还有更多数据
|
||||
*/
|
||||
|
|
@ -411,28 +544,67 @@ const handleOpenLeaveTypePicker = () => {
|
|||
|
||||
/**
|
||||
* 处理请假类型确认
|
||||
* 业务背景:保存请假类型ID和名称
|
||||
*/
|
||||
const handleLeaveTypeConfirm = (e) => {
|
||||
formData.value.leaveType = e.value[0]
|
||||
const selectedValue = e.value[0]?.value ?? e.value[0]
|
||||
const selectedItem = leaveTypeList.value.find(
|
||||
(item) => (item.dictCode || item.dictValue) === selectedValue,
|
||||
)
|
||||
formData.value.leaveTypeId = selectedValue
|
||||
formData.value.leaveTypeName = selectedItem?.dictLabel || e.value[0]?.text || ''
|
||||
showLeaveTypePicker.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开日期选择器
|
||||
* 业务背景:打开日期范围选择器,先选择开始日期,再选择结束日期
|
||||
* 设计决策:分两步选择,确保结束日期不早于开始日期
|
||||
*/
|
||||
const handleOpenDatePicker = () => {
|
||||
if (formData.value.leaveDate) {
|
||||
datePickerValue.value = formData.value.leaveDate
|
||||
if (formData.value.startDate) {
|
||||
startDatePickerValue.value = formData.value.startDate
|
||||
} else {
|
||||
startDatePickerValue.value = Date.now()
|
||||
}
|
||||
showDatePicker.value = true
|
||||
showStartDatePicker.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理日期确认
|
||||
* 处理开始日期确认
|
||||
* 业务背景:选择开始日期后,自动打开结束日期选择器
|
||||
*/
|
||||
const handleDateConfirm = (e) => {
|
||||
formData.value.leaveDate = e.value
|
||||
showDatePicker.value = false
|
||||
const handleStartDateConfirm = (e) => {
|
||||
formData.value.startDate = e.value
|
||||
startDatePickerValue.value = e.value
|
||||
showStartDatePicker.value = false
|
||||
|
||||
// 如果结束日期早于开始日期,重置结束日期
|
||||
if (formData.value.endDate && formData.value.endDate < e.value) {
|
||||
formData.value.endDate = null
|
||||
}
|
||||
|
||||
// 打开结束日期选择器
|
||||
if (formData.value.endDate) {
|
||||
endDatePickerValue.value = formData.value.endDate
|
||||
} else {
|
||||
// 默认结束日期为开始日期
|
||||
endDatePickerValue.value = e.value
|
||||
}
|
||||
showEndDatePicker.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理结束日期确认
|
||||
* 业务背景:选择结束日期后,调用接口获取请假详情数据并回显
|
||||
*/
|
||||
const handleEndDateConfirm = async (e) => {
|
||||
formData.value.endDate = e.value
|
||||
endDatePickerValue.value = e.value
|
||||
showEndDatePicker.value = false
|
||||
|
||||
// 调用接口获取请假详情
|
||||
await loadLeaveDetail()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -447,9 +619,15 @@ const handleOpenApproverPicker = () => {
|
|||
|
||||
/**
|
||||
* 处理审批人确认
|
||||
* 业务背景:保存审批人ID和名称
|
||||
*/
|
||||
const handleApproverConfirm = (e) => {
|
||||
formData.value.approver = e.value[0]
|
||||
const selectedValue = e.value[0]?.value ?? e.value[0]
|
||||
const selectedItem = approverList.value.find(
|
||||
(item) => (item.userId || item.id) === selectedValue,
|
||||
)
|
||||
formData.value.approverId = selectedValue
|
||||
formData.value.approverName = selectedItem?.nickName || selectedItem?.name || ''
|
||||
showApproverPicker.value = false
|
||||
}
|
||||
|
||||
|
|
@ -460,18 +638,12 @@ const handleApproverConfirm = (e) => {
|
|||
* @returns {Promise<String>} 返回文件URL
|
||||
*/
|
||||
const uploadFilePromise = (filePath) => {
|
||||
console.log(filePath, 'filePath')
|
||||
return new Promise((resolve, reject) => {
|
||||
const memberStore = useMemberStore()
|
||||
const baseURL = import.meta.env.VITE_API_BASE_URL
|
||||
const uploadUrl = baseURL + '/common/upload'
|
||||
|
||||
uni.uploadFile({
|
||||
url: uploadUrl,
|
||||
url: '/common/upload',
|
||||
filePath: filePath,
|
||||
name: 'file',
|
||||
header: {
|
||||
Authorization: memberStore.token || '',
|
||||
},
|
||||
formData: {
|
||||
user: 'test',
|
||||
},
|
||||
|
|
@ -501,17 +673,31 @@ const uploadFilePromise = (filePath) => {
|
|||
|
||||
/**
|
||||
* 处理文件上传
|
||||
* 业务背景:上传文件到服务器,保存完整的文件信息对象
|
||||
* 设计决策:保存文件信息对象,包含 url, size, name, type, status, message 等字段
|
||||
*/
|
||||
const handleAfterRead = async (event) => {
|
||||
const { file } = event
|
||||
console.log(file, 'file')
|
||||
try {
|
||||
const fileName = await uploadFilePromise(file.url)
|
||||
const fileName = await uploadFilePromise(file[0].url)
|
||||
|
||||
// 构建文件信息对象
|
||||
const fileInfo = {
|
||||
url: fileName,
|
||||
size: file.size || 0,
|
||||
name: file.name || '',
|
||||
type: file.type || '',
|
||||
status: 'success',
|
||||
message: '',
|
||||
}
|
||||
|
||||
fileList.value.push({
|
||||
...file,
|
||||
url: fileName,
|
||||
status: 'success',
|
||||
})
|
||||
formData.value.attachments.push(fileName)
|
||||
formData.value.attachments.push(fileInfo)
|
||||
} catch (error) {
|
||||
console.error('文件上传失败:', error)
|
||||
uni.showToast({
|
||||
|
|
@ -532,16 +718,36 @@ const handleDelete = (event) => {
|
|||
|
||||
/**
|
||||
* 处理提交
|
||||
* 业务背景:按照后台接口要求的参数格式组装提交数据
|
||||
* 设计决策:
|
||||
* 1. 将日期格式化为 YYYY-MM-DD
|
||||
* 2. 将文件信息数组转换为 JSON 字符串
|
||||
* 3. 将数字字段转换为数字类型
|
||||
* 4. 组装 startEndTime 格式为 "YYYY-MM-DD / YYYY-MM-DD"
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
console.log(formData.value, 'formData.value.attachments')
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
submitting.value = true
|
||||
|
||||
const startTime = dayjs(formData.value.startDate).format('YYYY-MM-DD')
|
||||
const endTime = dayjs(formData.value.endDate).format('YYYY-MM-DD')
|
||||
const startEndTime = `${startTime} / ${endTime}`
|
||||
|
||||
const submitData = {
|
||||
...formData.value,
|
||||
leaveDate: dayjs(formData.value.leaveDate).format('YYYY-MM-DD'),
|
||||
attachments: JSON.stringify(formData.value.attachments),
|
||||
approverId: formData.value.approverId,
|
||||
approverName: formData.value.approverName,
|
||||
endTime: endTime,
|
||||
fileInfo: JSON.stringify(formData.value.attachments),
|
||||
leaveDays: Number(formData.value.duration) || 0,
|
||||
leaveTypeId: formData.value.leaveTypeId,
|
||||
leaveTypeName: formData.value.leaveTypeName,
|
||||
lzDaysAfterLeave: Number(formData.value.afterDays) || 0,
|
||||
lzDaysBeforeLeave: Number(formData.value.beforeDays) || 0,
|
||||
reason: formData.value.reason,
|
||||
startEndTime: startEndTime,
|
||||
startTime: startTime,
|
||||
}
|
||||
|
||||
await submitLeaveRequestApi(submitData)
|
||||
|
|
@ -551,13 +757,16 @@ const handleSubmit = async () => {
|
|||
})
|
||||
// 重置表单
|
||||
formData.value = {
|
||||
leaveType: '',
|
||||
leaveDate: null,
|
||||
leaveTypeId: '',
|
||||
leaveTypeName: '',
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
reason: '',
|
||||
duration: '0',
|
||||
beforeDays: '0',
|
||||
afterDays: '0',
|
||||
approver: '',
|
||||
approverId: '',
|
||||
approverName: '',
|
||||
attachments: [],
|
||||
}
|
||||
fileList.value = []
|
||||
|
|
@ -591,16 +800,58 @@ const loadLeaveTypeList = async () => {
|
|||
*/
|
||||
const loadApproverList = async () => {
|
||||
try {
|
||||
const res = await getApproverListApi({
|
||||
pageNum: 1,
|
||||
pageSize: 99999,
|
||||
})
|
||||
const res = await getApproverListApi()
|
||||
approverList.value = res?.rows || res?.data || []
|
||||
} catch (error) {
|
||||
console.error('加载审批人列表失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载请假详情数据
|
||||
* 业务背景:根据选择的开始日期和结束日期,调用接口获取请假时长、履职天数等信息
|
||||
* 设计决策:在用户选择完日期范围后自动调用,回显相关字段
|
||||
*/
|
||||
const loadLeaveDetail = async () => {
|
||||
if (!formData.value.startDate || !formData.value.endDate) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const startDate = dayjs(formData.value.startDate).format('YYYY-MM-DD')
|
||||
const endDate = dayjs(formData.value.endDate).format('YYYY-MM-DD')
|
||||
|
||||
const res = await getLeaveDetailApi({
|
||||
startDate,
|
||||
endDate,
|
||||
})
|
||||
|
||||
// 回显数据
|
||||
if (res && res.data) {
|
||||
// 请假时长
|
||||
if (res.data.leaveDays !== undefined) {
|
||||
formData.value.duration = String(res.data.leaveDays)
|
||||
}
|
||||
|
||||
// 请假前需履职天数
|
||||
if (res.data.lzDaysBeforeLeave !== undefined) {
|
||||
formData.value.beforeDays = String(res.data.lzDaysBeforeLeave)
|
||||
}
|
||||
|
||||
// 请假后需履职天数
|
||||
if (res.data.lzDaysAfterLeave !== undefined) {
|
||||
formData.value.afterDays = String(res.data.lzDaysAfterLeave)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载请假详情失败:', error)
|
||||
uni.showToast({
|
||||
title: '获取请假详情失败',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理年份变更
|
||||
*/
|
||||
|
|
@ -621,24 +872,99 @@ const handleMonthChange = (item) => {
|
|||
loadRecordList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据状态值映射展示样式
|
||||
* 业务背景:后端可能返回多种状态文案,需要统一前端展示颜色
|
||||
* 设计决策:简单映射几类常见状态,其余走默认灰色
|
||||
*/
|
||||
const mapStatusType = (statusText = '') => {
|
||||
const text = statusText.toString()
|
||||
if (['待审批', '审批中', '审核中'].some((v) => text.includes(v))) return 'pending'
|
||||
if (['同意', '通过', '已审批', '完成', '结束'].some((v) => text.includes(v))) return 'success'
|
||||
if (['拒绝', '驳回', '不通过'].some((v) => text.includes(v))) return 'error'
|
||||
return 'default'
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否可修改/撤销
|
||||
* 业务背景:仅未来的请假可修改和撤销
|
||||
* 设计决策:使用开始日期与今日对比,精确到天
|
||||
*/
|
||||
const canEditLeave = (startDate) => {
|
||||
if (!startDate) return false
|
||||
return dayjs(startDate).isAfter(dayjs(), 'day')
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算规范化的开始、结束日期文本
|
||||
* 业务背景:后端字段命名不统一,需前端兜底
|
||||
*/
|
||||
const normalizeStartEnd = (item) => {
|
||||
const startRaw =
|
||||
item.startTime || item.startDate || item.beginDate || item.startDateStr || item.start_at
|
||||
const endRaw = item.endTime || item.endDate || item.finishDate || item.endDateStr || item.end_at
|
||||
const start = startRaw ? dayjs(startRaw) : null
|
||||
const end = endRaw ? dayjs(endRaw) : null
|
||||
return {
|
||||
startRaw,
|
||||
endRaw,
|
||||
startText: start?.format('YYYY-MM-DD') || '',
|
||||
endText: end?.format('YYYY-MM-DD') || '',
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载记录列表
|
||||
* 业务背景:根据时间范围与页码获取请假记录/审批列表
|
||||
* 设计决策:请求参数使用 startTime/endTime,移除 pageSize,前端保留分页 pageNum
|
||||
*/
|
||||
const loadRecordList = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const api = activeTab.value === 'record' ? getMyLeaveRecordListApi : getLeaveApprovalListApi
|
||||
|
||||
const res = await api(queryParams.value)
|
||||
const res = await api({
|
||||
pageNum: queryParams.value.pageNum,
|
||||
startTime: computedRecordRange.value.startTime,
|
||||
endTime: computedRecordRange.value.endTime,
|
||||
})
|
||||
total.value = res?.total || 0
|
||||
if (res?.rows && res.rows.length > 0) {
|
||||
// 处理数据,添加占位字段
|
||||
const processedData = res.rows.map((item) => ({
|
||||
...item,
|
||||
title: item.title || item.leaveTypeName || item.name || '请假记录',
|
||||
subtitle: item.subtitle || item.reason || item.description || '暂无描述',
|
||||
createTime: item.createTime || item.createTimeStr || item.date || '',
|
||||
}))
|
||||
const processedData = res.rows.map((item) => {
|
||||
const normalized = normalizeStartEnd(item)
|
||||
return {
|
||||
...item,
|
||||
title:
|
||||
item.userName || item.nickName
|
||||
? `${item.userName || item.nickName}的请假`
|
||||
: item.title || '请假记录',
|
||||
typeText: item.leaveTypeName || item.typeName || item.leaveType || '请假',
|
||||
createTime: item.createTime || item.createTimeStr || item.date || '',
|
||||
dateRange:
|
||||
(item.startTime && item.endTime && `${item.startTime}~${item.endTime}`) ||
|
||||
(normalized.startText && normalized.endText
|
||||
? `${normalized.startText}~${normalized.endText}`
|
||||
: '') ||
|
||||
item.startEndTime ||
|
||||
'',
|
||||
daysText:
|
||||
(item.leaveDays && `${item.leaveDays}天`) ||
|
||||
(item.duration && `${item.duration}天`) ||
|
||||
'',
|
||||
statusText:
|
||||
item.statusText ||
|
||||
item.statusName ||
|
||||
item.statusLabel ||
|
||||
item.status ||
|
||||
'待审批',
|
||||
statusType: mapStatusType(
|
||||
item.statusText || item.statusName || item.statusLabel || item.status || '',
|
||||
),
|
||||
...normalized,
|
||||
canEdit: canEditLeave(normalized.startRaw || normalized.startText),
|
||||
}
|
||||
})
|
||||
recordList.value = [...recordList.value, ...processedData]
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -684,6 +1010,139 @@ const handleRecordClick = (item) => {
|
|||
// TODO: 跳转到记录详情页面
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理修改操作
|
||||
* 业务背景:点击记录的“修改”按钮后进入表单回显并重新计算履职天数
|
||||
*/
|
||||
const handleModify = async (item) => {
|
||||
// 回显表单数据
|
||||
prefillFormData(item)
|
||||
// 切换到表单Tab
|
||||
activeTab.value = 'form'
|
||||
isEditing.value = true
|
||||
editingRecord.value = item
|
||||
|
||||
// 重新计算履职天数
|
||||
if (formData.value.startDate && formData.value.endDate) {
|
||||
await fetchPerformanceDays()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预填表单数据
|
||||
* 业务背景:修改时需要用列表行的数据回显到表单
|
||||
* 设计决策:日期统一转为时间戳,避免与日期组件冲突
|
||||
*/
|
||||
const prefillFormData = (item) => {
|
||||
const { startRaw, endRaw } = normalizeStartEnd(item)
|
||||
|
||||
formData.value.leaveTypeId = item.leaveTypeId || item.leaveType || ''
|
||||
formData.value.leaveTypeName = item.leaveTypeName || item.typeText || ''
|
||||
formData.value.reason = item.reason || item.description || ''
|
||||
formData.value.startDate = startRaw ? dayjs(startRaw).valueOf() : null
|
||||
formData.value.endDate = endRaw ? dayjs(endRaw).valueOf() : null
|
||||
formData.value.duration = item.leaveDays ? String(item.leaveDays) : item.duration || '0'
|
||||
formData.value.beforeDays = item.lzDaysBeforeLeave
|
||||
? String(item.lzDaysBeforeLeave)
|
||||
: item.beforeDays || '0'
|
||||
formData.value.afterDays = item.lzDaysAfterLeave
|
||||
? String(item.lzDaysAfterLeave)
|
||||
: item.afterDays || '0'
|
||||
formData.value.approverId = item.approverId || ''
|
||||
formData.value.approverName = item.approverName || ''
|
||||
formData.value.attachments = item.fileInfo ? JSON.parse(item.fileInfo || '[]') : []
|
||||
fileList.value = formData.value.attachments.map((file) => ({
|
||||
...file,
|
||||
status: 'success',
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新获取履职天数
|
||||
* 业务背景:修改日期后需重新计算请假时长及履职天数
|
||||
* 设计决策:复用后端接口 getPersonPerformanceDaysApi,保持字段与老代码一致
|
||||
*/
|
||||
const fetchPerformanceDays = async () => {
|
||||
if (!formData.value.startDate || !formData.value.endDate) return
|
||||
const startDate = dayjs(formData.value.startDate).format('YYYY-MM-DD')
|
||||
const endDate = dayjs(formData.value.endDate).format('YYYY-MM-DD')
|
||||
try {
|
||||
const res = await getPersonPerformanceDaysApi({
|
||||
startDate,
|
||||
endDate,
|
||||
})
|
||||
if (res && res.data) {
|
||||
formData.value.duration = String(res.data.leaveDays || 0)
|
||||
formData.value.beforeDays = String(res.data.lzDaysBeforeLeave || 0)
|
||||
formData.value.afterDays = String(res.data.lzDaysAfterLeave || 0)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取履职天数失败:', error)
|
||||
uni.showToast({
|
||||
title: '获取请假天数失败',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理撤销
|
||||
* 业务背景:未来的请假可撤销
|
||||
*/
|
||||
const handleRevoke = (item) => {
|
||||
uni.showToast({
|
||||
title: '撤销功能待接入接口',
|
||||
icon: 'none',
|
||||
})
|
||||
console.log('撤销', item)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理结束
|
||||
* 业务背景:结束前需校验是否存在未结束的请假,避免重复操作
|
||||
* 设计决策:先调用检查接口,按返回信息二次确认,再调用结束接口,完成后刷新列表
|
||||
*/
|
||||
const handleFinish = async (item) => {
|
||||
try {
|
||||
const checkRes = await checkHasUnfinishedLeaveApi()
|
||||
const content = checkRes?.msg || '确认结束当前请假吗?'
|
||||
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content,
|
||||
cancelText: '再想想',
|
||||
confirmText: '确认结束',
|
||||
confirmColor: '#00aa7c',
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return
|
||||
try {
|
||||
const endRes = await endLeaveRequestApi()
|
||||
uni.showToast({
|
||||
title: endRes?.msg || '结束成功',
|
||||
icon: 'none',
|
||||
})
|
||||
// 刷新列表
|
||||
queryParams.value.pageNum = 1
|
||||
recordList.value = []
|
||||
await loadRecordList()
|
||||
} catch (err) {
|
||||
console.error('结束失败:', err)
|
||||
uni.showToast({
|
||||
title: err?.message || '结束失败,请重试',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('检查未结束请假失败:', error)
|
||||
uni.showToast({
|
||||
title: error?.message || '检查失败,请稍后重试',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
|
@ -835,10 +1294,17 @@ onMounted(() => {
|
|||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.record-content {
|
||||
.record-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.record-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.record-title {
|
||||
|
|
@ -847,16 +1313,89 @@ onMounted(() => {
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.record-subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
.record-date {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
.record-type {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.record-body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.record-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
color: #555;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.record-range-text {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.record-days {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.record-status {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.status-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 4rpx;
|
||||
border: 1rpx solid #dcdfe6;
|
||||
color: #666;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
color: #07c160;
|
||||
border-color: #07c160;
|
||||
background: rgba(7, 193, 96, 0.08);
|
||||
}
|
||||
|
||||
.status-success.status-outline {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
color: #ffb100;
|
||||
border-color: #ffb100;
|
||||
background: rgba(255, 177, 0, 0.12);
|
||||
}
|
||||
|
||||
.status-error {
|
||||
color: #ff4d4f;
|
||||
border-color: #ff4d4f;
|
||||
background: rgba(255, 77, 79, 0.12);
|
||||
}
|
||||
|
||||
.record-actions {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
|
|
|
|||
|
|
@ -58,10 +58,10 @@
|
|||
<view class="filter-row-second">
|
||||
<view class="filter-item">
|
||||
<CommonPicker
|
||||
:options="rectifyOptions"
|
||||
:modelValue="queryParams.isRectified"
|
||||
placeholder="是否整改"
|
||||
@change="handleRectifyChange"
|
||||
:options="zgTypeOptions"
|
||||
:modelValue="queryParams.zgType"
|
||||
placeholder="整改状态"
|
||||
@change="handleZgTypeChange"
|
||||
/>
|
||||
</view>
|
||||
<view class="search-item">
|
||||
|
|
@ -93,10 +93,82 @@
|
|||
class="record-item"
|
||||
@tap="handleRecordClick(item)"
|
||||
>
|
||||
<!-- 序号标识 -->
|
||||
<view class="record-badge">{{ index + 1 }}</view>
|
||||
|
||||
<view class="record-content">
|
||||
<text class="record-title">{{ item.title || '问题记录' }}</text>
|
||||
<text class="record-subtitle">{{ item.subtitle || '暂无描述' }}</text>
|
||||
<text class="record-time">{{ item.createTime || '2025-01-01' }}</text>
|
||||
<!-- 主标题 -->
|
||||
<view class="record-header">
|
||||
<text class="record-title">{{ getRecordTitle(item) }}</text>
|
||||
<text class="record-type-tag">
|
||||
{{ item.type == 0 ? '现场履职' : '班组履职' }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 日期时间 -->
|
||||
<text class="record-time">
|
||||
{{ formatDateTime(item.createTime || item.createTimeStr) }}
|
||||
</text>
|
||||
|
||||
<!-- 手册信息 -->
|
||||
<view v-if="item.handbookName" class="record-info-row">
|
||||
<up-icon name="file-text" size="16" color="#07c160" />
|
||||
<text class="record-info-text">{{ item.handbookName }}</text>
|
||||
<view>
|
||||
<up-button
|
||||
text="复制"
|
||||
type="success"
|
||||
size="mini"
|
||||
shape="circle"
|
||||
@tap.stop="handleCopyManual(item)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 问题信息列表 -->
|
||||
<template v-if="item.problemList && item.problemList.length > 0">
|
||||
<view
|
||||
v-for="(problem, pIndex) in item.problemList"
|
||||
:key="problem.id || pIndex"
|
||||
class="record-info-row"
|
||||
>
|
||||
<up-icon name="edit-pen" size="16" color="#2979ff" />
|
||||
<text class="record-problem-text">{{ problem.remark }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 负责人信息列表 -->
|
||||
<template v-if="item.problemList && item.problemList.length > 0">
|
||||
<view
|
||||
v-for="(problem, pIndex) in item.problemList"
|
||||
:key="`responsible-${problem.id || pIndex}`"
|
||||
class="record-info-row"
|
||||
>
|
||||
<up-icon name="account" size="16" color="#ff9800" />
|
||||
<text class="record-info-text">{{
|
||||
problem.responsiblePerson
|
||||
}}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 同行人信息 -->
|
||||
<view class="record-info-row">
|
||||
<up-icon name="account-fill" size="16" color="#07c160" />
|
||||
<text class="record-info-text">{{ item.otherUserName }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 整改状态标签 -->
|
||||
<view class="record-info-row" v-if="item.zgType">
|
||||
<text
|
||||
class="rectify-status-tag"
|
||||
:class="{
|
||||
'rectify-status-tag--done': item.zgType == 1,
|
||||
'rectify-status-tag--pending': item.zgType == 2,
|
||||
}"
|
||||
>
|
||||
{{ item.zgType == 1 ? '已整改' : '未整改' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -156,11 +228,11 @@ const loading = ref(false)
|
|||
// 查询参数
|
||||
const queryParams = ref({
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
pageSize: 10,
|
||||
year: new Date().getFullYear().toString(),
|
||||
month: '',
|
||||
type: '',
|
||||
isRectified: '',
|
||||
zgType: '',
|
||||
keyword: '',
|
||||
})
|
||||
|
||||
|
|
@ -201,11 +273,11 @@ const typeOptions = ref([
|
|||
{ label: '班组履职', value: '1' },
|
||||
])
|
||||
|
||||
// 是否整改选项
|
||||
const rectifyOptions = ref([
|
||||
// 整改状态选项
|
||||
const zgTypeOptions = ref([
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '已整改', value: '1' },
|
||||
{ label: '未整改', value: '0' },
|
||||
{ label: '未整改', value: '2' },
|
||||
])
|
||||
|
||||
/**
|
||||
|
|
@ -263,11 +335,11 @@ const handleTypeChange = (item) => {
|
|||
}
|
||||
|
||||
/**
|
||||
* 处理是否整改变更
|
||||
* 处理整改状态变更
|
||||
* @param {Object} item - 选中的整改状态项
|
||||
*/
|
||||
const handleRectifyChange = (item) => {
|
||||
queryParams.value.isRectified = item.value
|
||||
const handleZgTypeChange = (item) => {
|
||||
queryParams.value.zgType = item.value
|
||||
queryParams.value.pageNum = 1
|
||||
recordList.value = []
|
||||
loadRecordList()
|
||||
|
|
@ -283,6 +355,43 @@ const handleSearch = debounce(() => {
|
|||
loadRecordList()
|
||||
}, 500)
|
||||
|
||||
/**
|
||||
* 构建查询参数
|
||||
* 业务背景:根据年份和月份生成 startTime 和 endTime,按照新的参数格式组装
|
||||
* 设计决策:如果选择了月份,则精确到该月的第一天和最后一天;如果只选择年份,则使用整年范围
|
||||
*/
|
||||
const buildQueryParams = () => {
|
||||
const { year, month, type, zgType, keyword, pageNum, pageSize } = queryParams.value
|
||||
|
||||
let startTime = ''
|
||||
let endTime = ''
|
||||
|
||||
if (year) {
|
||||
if (month) {
|
||||
// 选择了月份,精确到该月
|
||||
const yearNum = parseInt(year)
|
||||
const monthNum = parseInt(month)
|
||||
const lastDay = new Date(yearNum, monthNum, 0).getDate()
|
||||
startTime = `${year}-${String(monthNum).padStart(2, '0')}-01`
|
||||
endTime = `${year}-${String(monthNum).padStart(2, '0')}-${String(lastDay).padStart(2, '0')}`
|
||||
} else {
|
||||
// 只选择了年份,使用整年范围
|
||||
startTime = `${year}-01-01`
|
||||
endTime = `${year}-12-31`
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
pageNum,
|
||||
pageSize,
|
||||
startTime,
|
||||
endTime,
|
||||
handbookType: type || '',
|
||||
zgType: zgType || '',
|
||||
keyword: keyword || '',
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载记录列表
|
||||
* 业务背景:根据当前tab调用不同的接口获取记录列表
|
||||
|
|
@ -293,38 +402,18 @@ const loadRecordList = async () => {
|
|||
const api =
|
||||
activeTab.value === 'my' ? getMyProblemRecordListApi : getPeerProblemRecordListApi
|
||||
|
||||
const res = await api(queryParams.value)
|
||||
const params = buildQueryParams()
|
||||
const res = await api(params)
|
||||
total.value = res?.total || 0
|
||||
if (res?.rows && res.rows.length > 0) {
|
||||
// 处理数据,添加占位字段
|
||||
const processedData = res.rows.map((item) => ({
|
||||
...item,
|
||||
title: item.title || item.problemTitle || item.name || '问题记录',
|
||||
subtitle: item.subtitle || item.problemDesc || item.description || '暂无描述',
|
||||
createTime: item.createTime || item.createTimeStr || item.date || '',
|
||||
}))
|
||||
recordList.value = [...recordList.value, ...processedData]
|
||||
recordList.value = [...recordList.value, ...res.rows]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载记录列表失败:', error)
|
||||
// 模拟数据
|
||||
if (queryParams.value.pageNum === 1) {
|
||||
recordList.value = [
|
||||
{
|
||||
id: 1,
|
||||
title: '问题记录示例1',
|
||||
subtitle: '这是问题记录的描述信息',
|
||||
createTime: '2025-01-15 10:30:00',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '问题记录示例2',
|
||||
subtitle: '这是问题记录的描述信息',
|
||||
createTime: '2025-01-14 14:20:00',
|
||||
},
|
||||
]
|
||||
total.value = 2
|
||||
}
|
||||
uni.showToast({
|
||||
title: '加载失败,请重试',
|
||||
icon: 'none',
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
|
@ -340,14 +429,88 @@ const onHandleScrollToLower = debounce(() => {
|
|||
}
|
||||
}, 1000)
|
||||
|
||||
/**
|
||||
* 获取记录标题
|
||||
* 业务背景:根据项目名称和标题组合显示记录标题
|
||||
* @param {Object} item - 记录项数据
|
||||
* @returns {String} 记录标题
|
||||
*/
|
||||
const getRecordTitle = (item) => {
|
||||
if (item.projectName && item.dutyTypeName) {
|
||||
return `${item.projectName} ${item.dutyTypeName}`
|
||||
}
|
||||
return item.projectName || item.title || item.name || '问题记录'
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日期时间
|
||||
* 业务背景:将日期时间格式化为 YYYY-MM-DD HH:mm 格式
|
||||
* @param {String} dateTime - 日期时间字符串
|
||||
* @returns {String} 格式化后的日期时间
|
||||
*/
|
||||
const formatDateTime = (dateTime) => {
|
||||
if (!dateTime) return ''
|
||||
// 如果是完整的时间戳格式,提取日期和时间部分
|
||||
const date = new Date(dateTime)
|
||||
if (isNaN(date.getTime())) {
|
||||
// 如果不是有效日期,尝试直接格式化
|
||||
return dateTime
|
||||
}
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理复制手册
|
||||
* 业务背景:复制手册名称到剪贴板
|
||||
* @param {Object} item - 记录项数据
|
||||
*/
|
||||
const handleCopyManual = (item) => {
|
||||
if (!item.handbookName) return
|
||||
uni.setClipboardData({
|
||||
data: item.handbookName,
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '复制成功',
|
||||
icon: 'success',
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '复制失败',
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理记录点击
|
||||
* @param {Object} item - 记录项数据
|
||||
* 业务背景:点击记录项后查看详情(后续实现)
|
||||
* 业务背景:点击记录项后查看详情
|
||||
*/
|
||||
const handleRecordClick = (item) => {
|
||||
console.log('点击记录:', item)
|
||||
// TODO: 跳转到记录详情页面
|
||||
if (!item.id) {
|
||||
uni.showToast({
|
||||
title: '记录ID不存在',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/leader/problem-record-details/index?recordId=${item.id}`,
|
||||
fail: (err) => {
|
||||
console.error('导航失败:', err)
|
||||
uni.showToast({
|
||||
title: '页面跳转失败',
|
||||
icon: 'none',
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
|
|
@ -431,12 +594,8 @@ onMounted(() => {
|
|||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
// display: flex;
|
||||
}
|
||||
|
||||
.search-item {
|
||||
display: flex;
|
||||
.search-row {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// 选择框样式
|
||||
|
|
@ -464,10 +623,12 @@ onMounted(() => {
|
|||
.record-item {
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
padding: 32rpx 24rpx 24rpx;
|
||||
padding-right: 60rpx;
|
||||
margin-bottom: 16rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.record-item:active {
|
||||
|
|
@ -475,28 +636,99 @@ onMounted(() => {
|
|||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.record-badge {
|
||||
position: absolute;
|
||||
top: 16rpx;
|
||||
right: 16rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background: #ff4444;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.record-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.record-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
flex-wrap: wrap;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.record-title {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.record-subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
.record-type-tag {
|
||||
font-size: 24rpx;
|
||||
color: #ff9800;
|
||||
background: rgba(255, 152, 0, 0.1);
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 4rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.record-info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.record-info-text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.record-problem-text {
|
||||
font-size: 26rpx;
|
||||
color: #ff4444;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.rectify-status-tag {
|
||||
font-size: 24rpx;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 4rpx;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rectify-status-tag--done {
|
||||
color: #07c160;
|
||||
background: rgba(7, 193, 96, 0.1);
|
||||
}
|
||||
|
||||
.rectify-status-tag--pending {
|
||||
color: #ff4444;
|
||||
background: rgba(255, 68, 68, 0.1);
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
|
|
|
|||
|
|
@ -13,13 +13,8 @@ import { http } from '@/utils/http'
|
|||
*/
|
||||
export const getLeaveTypeListApi = () => {
|
||||
return http({
|
||||
url: '/system/dict/data/list',
|
||||
url: '/perform_set/leave_type/applist',
|
||||
method: 'GET',
|
||||
data: {
|
||||
pageNum: 1,
|
||||
pageSize: 99999,
|
||||
dictType: 'leave_type',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -29,11 +24,10 @@ export const getLeaveTypeListApi = () => {
|
|||
* @param {Object} data - 查询参数 { pageNum, pageSize, keyword }
|
||||
* @returns {Promise} 返回审批人列表数据
|
||||
*/
|
||||
export const getApproverListApi = (data) => {
|
||||
export const getApproverListApi = () => {
|
||||
return http({
|
||||
url: '/system/user_leader/peerlist',
|
||||
url: '/system/user/approver_list',
|
||||
method: 'GET',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -45,12 +39,54 @@ export const getApproverListApi = (data) => {
|
|||
*/
|
||||
export const submitLeaveRequestApi = (data) => {
|
||||
return http({
|
||||
url: '/leave-request/submit',
|
||||
url: '/report/leave_log/list',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 修改请假申请
|
||||
export const updateLeaveRequestApi = (data) => {
|
||||
return http({
|
||||
url: '/leave_log/edit_leave',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 撤销请假
|
||||
export const cancelLeaveRequestApi = (id) => {
|
||||
return http({
|
||||
url: '/leave_log/cancel_leave/' + id,
|
||||
method: 'PUT',
|
||||
})
|
||||
}
|
||||
|
||||
// 结束请假
|
||||
export const endLeaveRequestApi = () => {
|
||||
return http({
|
||||
url: '/leave_log/end_leave',
|
||||
method: 'PUT',
|
||||
})
|
||||
}
|
||||
|
||||
// 获取人员履职天数
|
||||
export const getPersonPerformanceDaysApi = (data) => {
|
||||
return http({
|
||||
url: '/perform_set/user/detail',
|
||||
method: 'GET',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 查看是否有未结束的请假
|
||||
export const checkHasUnfinishedLeaveApi = () => {
|
||||
return http({
|
||||
url: '/leave_log/end_leave_info',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的请假记录列表
|
||||
* 业务背景:获取当前用户的请假记录列表
|
||||
|
|
@ -59,7 +95,7 @@ export const submitLeaveRequestApi = (data) => {
|
|||
*/
|
||||
export const getMyLeaveRecordListApi = (data) => {
|
||||
return http({
|
||||
url: '/leave-request/my-list',
|
||||
url: '/report/leave_log/list',
|
||||
method: 'GET',
|
||||
data,
|
||||
})
|
||||
|
|
@ -78,3 +114,18 @@ export const getLeaveApprovalListApi = (data) => {
|
|||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请假详情数据
|
||||
* 业务背景:根据开始日期和结束日期获取请假相关的详情数据,包括请假时长、履职天数等
|
||||
* 设计决策:使用 GET 请求获取详情数据,日期参数通过查询参数传递
|
||||
* @param {Object} data - 查询参数 { startDate, endDate },日期格式为 YYYY-MM-DD
|
||||
* @returns {Promise} 返回请假详情数据
|
||||
*/
|
||||
export const getLeaveDetailApi = (data) => {
|
||||
return http({
|
||||
url: '/perform_set/user/detail',
|
||||
method: 'GET',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ export const getMyPerformanceRecordListApi = (data) => {
|
|||
*/
|
||||
export const getPeerPerformanceRecordListApi = (data) => {
|
||||
return http({
|
||||
url: '/performance-record/peer-list',
|
||||
method: 'GET',
|
||||
url: '/app/recordInfo/otherlist',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -127,8 +127,8 @@ export const getPeerPerformanceRecordListApi = (data) => {
|
|||
*/
|
||||
export const getMyProblemRecordListApi = (data) => {
|
||||
return http({
|
||||
url: '/problem-record/my-list',
|
||||
method: 'GET',
|
||||
url: '/app/recordInfo/userProblemList',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -142,8 +142,8 @@ export const getMyProblemRecordListApi = (data) => {
|
|||
*/
|
||||
export const getPeerProblemRecordListApi = (data) => {
|
||||
return http({
|
||||
url: '/problem-record/peer-list',
|
||||
method: 'GET',
|
||||
url: '/app/recordInfo/otherProblemList',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue