<template>
<basic-container-card>
<!-- 基础信息 -->
<el-card>
<avue-form :option="baseformOption" v-model="baseForm" ref="baseForm" class="eer-avue_form">
<template slot="naturalWeek">
<el-date-picker
v-model="baseForm.naturalWeek"
type="week"
format="yyyy 第 WW 周"
:picker-options="{firstDayOfWeek: 1}"
:placeholder="$t('weeklyWorkingHourRegistration.weekPlaceholder')" @change="changeWeek">
</el-date-picker>
</template>
</avue-form>
</el-card>
<!-- 工时明细 -->
<el-card>
<el-collapse v-model="workHoursDetailNames" class="eer-collapse">
<el-collapse-item name="1">
<template #title>
<i class="el-icon-menu avue-group__icon"/>
<h1 class="avue-group__title">{{ $t('tagsView.workHoursDetailInfo') }}</h1>
</template>
<avue-crud :option="workHoursDetailOption"
:data="workHoursDetailData"
v-model="workHoursDetailForm"
:table-loading="workHoursDetailLoading"
@row-save="rowSave"
@row-update="rowSave"
@selection-change="selectionChange"
@refresh-change="refreshChange"
ref="workHoursDetailCrud">
<template slot="menuLeft">
<el-button type="primary"
size="small"
icon="el-icon-plus"
v-if="permission.weeklyWorkingHourRegistration_workHourDetailAdd"
@click="handleAdd"> {{ $t("menuLeft.addBtn") }}
</el-button>
<el-button type="primary"
size="small"
icon="el-icon-delete"
v-if="permission.weeklyWorkingHourRegistration_workHourDetailDelete"
@click="handleDelete"> {{ $t('menuLeft.deleteBtn') }}
</el-button>
</template>
<template slot="projectMembersForm" slot-scope="{row}">
<el-input v-model="row.projectMembers" :placeholder="$t('weeklyWorkingHourRegistration.projectMembersPlaceholder')" maxlength="200" show-word-limit v-if="row.roles === '11'"/>
<el-select v-model="row.projectMembersId" :placeholder="$t('weeklyWorkingHourRegistration.projectMembersIdPlaceholder')" filterable v-else>
<el-option v-for="(item, index) in projectMembersData" :key="index" :label="item.realName" :value="item.id"/>
</el-select>
</template>
<template slot-scope="{row}" slot="menu">
<el-button type="text" size="small" icon="el-icon-edit"
v-if="permission.weeklyWorkingHourRegistration_workHourDetailEdit && !row.$cellEdit"
@click="handleEdit(row)"> {{ $t("menuLeft.editBtn") }}
</el-button>
</template>
</avue-crud>
</el-collapse-item>
</el-collapse>
</el-card>
<!-- 工时汇总 -->
<el-card>
<el-collapse v-model="workHoursTotalNames" class="eer-collapse">
<el-collapse-item name="1">
<template #title>
<i class="el-icon-menu avue-group__icon"/>
<h1 class="avue-group__title">{{ $t('tagsView.workHoursTotalInfo') }}</h1>
</template>
<avue-crud :option="workHoursTotalOption"
:data="workHoursTotalData"
v-model="workHoursTotalForm"
ref="workHoursTotalCrud">
</avue-crud>
</el-collapse-item>
</el-collapse>
<div class="eer-page__footer">
<el-button size="small" type="primary" icon="el-icon-circle-plus-outline"
:loading="saveLoading"
@click="saveTask('draft')"> {{ $t('menuLeft.saveDraftBtn') }}
</el-button>
<el-button size="small" type="primary" icon="el-icon-circle-plus-outline"
:loading="saveLoading"
@click="saveTask('submit')"> {{ $t('menuLeft.submit') }}
</el-button>
<el-button size="small" icon="el-icon-circle-close"
@click="cancelTask"> {{ $t('menuLeft.cancel') }}
</el-button>
</div>
</el-card>
</basic-container-card>
</template>
<script>
import i18n from "@/lang";
import {
dateColumnOption, dictBizUrl,
formOption, labelWidth130, limitColumnOption,
numberColumnOption,
requiredRule,
selectColumnOption, span24,
span6, tableColumnOption, tableOption, textareaColumnOption
} from "@/util/configuration";
import {mapGetters} from "vuex";
import {getWeekByDate, getWeekRange, translateDate} from "@/util/date";
import {
getProjectPhaseDataList,
addDataList,
deleteWorkHourDetailDataList, addWorkHourDetailDataList, getWorkHourDetailDataList, getDetailInfo
} from "@/api/projectMetricsManagement/weeklyWorkingHourRegistration";
import {getUserRoleList} from "@/api/financeManagement/expenseManagement/employeeDailyReimbursementApplication";
export default {
data() {
return {
// 基础信息
baseForm: {},
baseformOption: {
...formOption,
...labelWidth130,
group: [{
label: i18n.t('tagsView.baseInfo'),
prop: 'baseInfo',
icon: 'el-icon-menu',
column: [{
label: i18n.t('weeklyWorkingHourRegistration.projectId'),
prop: 'projectId',
...span6,
...selectColumnOption,
dicUrl: '/api/tmprojectmanagement/page',
dicQuery: {
current: 1,
size: 999999,
keywords: '',
activeStatus: 2,
},
dicFormatter: (res) => {
return res.data.records.map(item => {
return {
label: item.projectName,
value: item.id,
projectManager: item.projectManager
}
})
},
props: {
label: "label",
value: "value"
},
filterable: true,
disabled: true,
rules: [
requiredRule
],
change: (value) => {
if (value.value) {
this.baseForm.projectManagerId = value.item.projectManager;
getProjectPhaseDataList({projectId: value.value}).then(res => {
this.findObject(this.baseformOption.group[0].column, 'projectPhase').dicData = res.data.data;
}).catch(error => {
console.error('获取项目阶段数据失败:', error);
});
}
}
}, {
label: i18n.t('weeklyWorkingHourRegistration.projectManagerId'),
prop: 'projectManagerId',
...span6,
...selectColumnOption,
dicData: [],
props: {
label: "realName",
value: "id"
},
filterable: true,
disabled: true,
rules: [
requiredRule
]
}, {
label: i18n.t('weeklyWorkingHourRegistration.projectPhase'),
prop: 'projectPhase',
...span6,
...selectColumnOption,
dicData: [],
props: {
label: "taskName",
value: "id"
},
filterable: true,
rules: [
requiredRule
]
}, {
label: i18n.t('weeklyWorkingHourRegistration.naturalWeek'),
prop: 'naturalWeek',
...span6,
rules: [
requiredRule
]
}, {
label: i18n.t('weeklyWorkingHourRegistration.startAndEndDate'),
prop: 'startAndEndDate',
...span6,
...dateColumnOption,
type: "daterange",
disabled: true,
rules: [
requiredRule
]
}, {
label: i18n.t('weeklyWorkingHourRegistration.actualHour'),
prop: 'actualHour',
...span6,
disabled: true,
rules: [
requiredRule
]
}, {
label: i18n.t('weeklyWorkingHourRegistration.remark'),
prop: 'remark',
...span24,
...textareaColumnOption,
}]
}]
},
// 工时明细
workHoursDetailNames: ['1'],
workHoursDetailOption: {
...tableOption,
cellBtn: true,
column: [{
label: i18n.t("weeklyWorkingHourRegistration.roles"),
prop: "roles",
...tableColumnOption,
...selectColumnOption,
type: "select",
dicUrl: "/api/blade-system/dict-biz/dictionary?code=oa_tt_member_management_role",
props: {
label: "dictValue",
value: "dictKey"
},
cell: true,
rules: [
requiredRule
]
}, {
label: i18n.t("weeklyWorkingHourRegistration.projectMembersId"),
prop: "projectMembers",
...tableColumnOption,
cell: true,
}, {
label: i18n.t("weeklyWorkingHourRegistration.actualHour"),
prop: "actualHour",
...tableColumnOption,
...numberColumnOption,
precision: 1,
step: '0.1',
cell: true,
rules: [
requiredRule,
{
validator: (rule, value, callback) => {
if (Number(value) <= 0) {
callback(new Error(i18n.t('wcommonMessage.10023')));
} else {
callback();
}
}
}
]
}, {
label: i18n.t("weeklyWorkingHourRegistration.remark"),
prop: "remark",
...tableColumnOption,
...limitColumnOption,
cell: true,
}]
},
workHoursDetailData: [],
workHoursDetailForm: {},
workHoursDetailLoading: false,
workHoursDetailSelectionList: [],
// 工时汇总
workHoursTotalNames: ['1'],
workHoursTotalOption: {
...tableOption,
menu: false,
selection: false,
column: [{
label: i18n.t("weeklyWorkingHourRegistration.roles"),
prop: "roles",
...tableColumnOption,
...selectColumnOption,
dicUrl: dictBizUrl + "oa_tt_member_management_role",
}, {
label: i18n.t("weeklyWorkingHourRegistration.actualHour"),
prop: "actualHour",
...tableColumnOption,
...numberColumnOption,
step: '0.1',
}]
},
workHoursTotalData: [],
workHoursTotalForm: {},
projectMembersData: [],
batchNo: '',
saveLoading: false, // 保存草稿
}
},
computed: {
...mapGetters(["permission"]),
ids() {
return this.workHoursDetailSelectionList.map(ele => ele.id).join(",");
}
},
mounted() {
this.$nextTick(async () => {
try {
const res = await getUserRoleList(1, 999999, {removeAccount: 'admin'});
this.findObject(this.baseformOption.group[0].column, 'projectManagerId').dicData = res.data.data.records;
await this.getDetail();
await this.getProjectMembersData();
} catch (error) {
console.error('初始化数据失败:', error);
}
});
},
methods: {
// 切换自然周
changeWeek(value) {
if (value) {
const weekRange = getWeekRange(value);
this.baseForm.startAndEndDate = [weekRange.start, weekRange.end];
} else {
this.baseForm.startAndEndDate = [];
}
},
// 项目成员下拉列表
async getProjectMembersData() {
try {
const res = await getUserRoleList(1, 999999, {removeAccount: 'admin'});
this.projectMembersData = res.data.data.records;
} catch (error) {
console.error('获取项目成员数据失败:', error);
}
},
// 查看数据
async getDetail() {
try {
this.$nextTick(async () => {
const row = JSON.parse(sessionStorage.getItem('weekly-working-hour-registration') || '{}');
if (!row.id) {
this.$message.warning(i18n.t('warningMessage.noRecordSelected'));
return;
}
const res = await getDetailInfo({id: row.id});
const data = res.data.data;
// 安全地合并数据
this.baseForm = {
...this.baseForm,
...data,
startAndEndDate: []
};
// this.batchNo = data.batchNo;
// 设置日期范围
if (data.naturalWeek) {
const weekRange = getWeekRange(new Date(data.naturalWeek));
this.baseForm.startAndEndDate = [weekRange.start, weekRange.end];
}
await this.onLoad();
});
} catch (error) {
console.error('获取详情失败:', error);
}
},
// 多选
selectionChange(list) {
this.workHoursDetailSelectionList = [...list];
},
// 清空多选
selectionClear() {
this.workHoursDetailSelectionList = [];
},
// 刷新
refreshChange() {
this.onLoad();
this.selectionClear();
},
async onLoad() {
try {
const params = {
parentId: this.baseForm.id || '',
batchNo: this.batchNo || '', // 添加默认值
type: 'edit',
};
const res = await getWorkHourDetailDataList(params);
// 检查响应状态
if (res.data && res.data.code !== 200) {
const errorMsg = res.data.msg || '加载工时明细失败';
throw new Error(errorMsg);
}
// 确保数据存在
if (!res.data || !res.data.data) {
throw new Error('返回数据格式不正确');
}
const data = res.data.data.detailList || [];
data.forEach(item => {
// 确保actualHour是有效数字
const hour = Number(item.actualHour);
if (!isNaN(hour)) {
item.actualHour = hour.toFixed(1);
} else {
item.actualHour = '0.0';
}
// 确保项目成员字段正确处理
// 如果角色是11并且有projectMembers字段,则保留该字段
// 否则确保projectMembersId字段正确设置
if (item.roles === '11' && item.projectMembers) {
// 自定义角色使用projectMembers字段
} else if (item.projectMembersId) {
// 从projectMembersData中查找对应的成员名称用于显示
const member = this.projectMembersData.find(m => m.id === item.projectMembersId);
if (member) {
item.projectMembers = member.realName;
}
}
});
this.workHoursDetailData = data;
this.batchNo = res.data.data.batchNo || this.batchNo || '';
this.handleSummary();
} catch (error) {
console.error('加载工时明细失败:', error);
// 显示更友好的错误提示
let message = '加载工时明细失败,请稍后重试';
if (error.message && error.message !== '加载工时明细失败') {
message = error.message;
} else if (error.response && error.response.data && error.response.data.msg) {
message = error.response.data.msg;
}
this.$message.error(message);
}
},
// 工时汇总
handleSummary() {
try {
// 过滤掉编辑状态的数据
const filteredData = this.workHoursDetailData.filter(item => !item.$cellEdit);
// 使用 reduce 计算每个角色的总工时
const totals = filteredData.reduce((acc, item) => {
const {roles, actualHour} = item;
let amount = Number(actualHour);
// 校验数值有效性
if (isNaN(amount) || amount <= 0) {
return acc;
}
// 限制最大值
amount = Math.min(999999.99, amount);
// 查找是否已有该角色的项
const existing = acc.find(i => i.roles === roles);
if (existing) {
// 累加金额并限制最大值
existing.actualHour = Math.min(999999.99, existing.actualHour + amount);
} else {
// 添加新项
acc.push({
roles,
actualHour: amount
});
}
return acc;
}, []);
// 保留一位小数
this.workHoursTotalData = totals.map(item => ({
...item,
actualHour: String(item.actualHour.toFixed(1))
}));
this.baseFormSummary();
} catch (error) {
console.error('计算工时汇总失败:', error);
}
},
// 基础信息-本周实际工时汇总
baseFormSummary() {
try {
const total = this.workHoursTotalData.reduce((sum, item) => {
const hour = parseFloat(item.actualHour);
return sum + (isNaN(hour) ? 0 : hour);
}, 0);
this.baseForm.actualHour = total.toFixed(1);
} catch (error) {
console.error('更新基础信息汇总失败:', error);
}
},
// 工时明细新增
handleAdd() {
try {
const obj = {
$cellEdit: true,
roles: null,
projectMembers: null, // 确保初始化projectMembers字段
projectMembersId: null, // 确保初始化projectMembersId字段
actualHour: null,
remark: null,
};
this.workHoursDetailData.unshift({...obj});
} catch (error) {
console.error('新增工时明细失败:', error);
}
},
// 保存
rowSave(row, index, done) {
try {
// 校验数据
if (!row || typeof row !== 'object') {
throw new Error('无效的行数据');
}
if (typeof row.actualHour !== 'number' || isNaN(row.actualHour)) {
throw new Error('实际工时必须是有效数字');
}
// 构造参数,确保正确处理项目成员信息
const params = {
...row,
actualHour: parseFloat(row.actualHour.toFixed(1)),
batchNo: this.batchNo,
};
// 根据角色类型处理项目成员字段
if (row.roles === '11') {
// 自定义角色使用projectMembers字段
params.projectMembers = row.projectMembers;
// 确保清除projectMembersId字段
delete params.projectMembersId;
} else {
// 预定义角色使用projectMembersId字段
params.projectMembersId = row.projectMembersId;
// 确保清除projectMembers字段
delete params.projectMembers;
}
addWorkHourDetailDataList(params)
.then(() => {
this.$message.success(i18n.t("commonMessage.10011"));
return this.onLoad();
})
.catch((error) => {
console.error('保存工时明细失败:', error);
// 显示更具体的错误信息
if (error.response && error.response.data && error.response.data.msg) {
this.$message.error(error.response.data.msg);
} else {
this.$message.error('保存工时明细失败');
}
})
.finally(() => {
done();
});
} catch (error) {
console.error('保存行数据时发生错误:', error);
this.$message.error(error.message || '保存工时明细时发生错误');
done();
}
},
// 工时明细编辑
handleEdit(row) {
try {
if (!row || typeof row !== 'object') {
throw new Error('无效的行数据');
}
this.$set(row, '$cellEdit', true);
} catch (error) {
console.error('编辑行数据时发生错误:', error);
}
},
// 工时明细删除
handleDelete() {
try {
if (this.workHoursDetailSelectionList.length === 0) {
this.$message.warning(i18n.t("commonMessage.10001"));
return;
}
this.$confirm(i18n.t("commonMessage.10018"), {
confirmButtonText: i18n.t("tips.confirm"),
cancelButtonText: i18n.t("tips.cancel"),
type: "warning"
}).then(async () => {
await deleteWorkHourDetailDataList(this.ids);
this.$message.success(i18n.t("commonMessage.10011"));
await this.onLoad();
}).catch((res) => {
if (res.data && res.data.code === 1000) {
this.$eermessage.notify({content: res.data.msg});
}
this.onLoad();
this.$refs.workHoursDetailCrud.toggleSelection();
this.workHoursDetailSelectionList = [];
});
} catch (error) {
console.error('删除工时明细失败:', error);
}
},
// 保存草稿/提交
saveTask(flag) {
if (flag === 'submit') {
this.$refs.baseForm.validate((valid, done) => {
if (valid) {
if (this.workHoursDetailData.length === 0) {
this.$message.warning(i18n.t('commonMessage.50115'));
return false;
}
this.submitTask(flag);
}
done();
});
} else {
this.submitTask(flag);
}
},
submitTask(flag) {
try {
this.saveLoading = true;
const weekValue = this.baseForm.naturalWeek
? getWeekByDate(this.baseForm.naturalWeek)
: '';
const startDate = this.baseForm.startAndEndDate?.[0] || '';
const endDate = this.baseForm.startAndEndDate?.[1] || '';
const params = {
...this.baseForm,
naturalWeek: this.baseForm.naturalWeek ? translateDate(this.baseForm.naturalWeek) : '',
naturalWeekValue: weekValue,
startTime: startDate,
endTime: endDate,
summaryList: this.workHoursTotalData,
status: flag === 'submit' ? '2' : '1',
batchNo: this.batchNo,
};
addDataList(params)
.then(() => {
this.$message.success(i18n.t('commonMessage.10011'));
this.cancelTask();
})
.catch((error) => {
console.error('提交任务失败:', error);
})
.finally(() => {
this.saveLoading = false;
});
} catch (error) {
console.error('提交任务时发生错误:', error);
this.saveLoading = false;
}
},
// 取消
cancelTask() {
try {
this.$router.$avueRouter.closeTag();
this.$router.push({
path: '/projectMetricsManagement/weeklyWorkingHourRegistration',
});
if (this.$refs.baseForm) {
this.$refs.baseForm.resetFields();
this.$nextTick(() => {
this.$refs.baseForm.clearValidate();
});
}
} catch (error) {
console.error('取消操作时发生错误:', error);
}
}
}
}
</script>
<style scoped>
/* 保持原有样式不变 */
</style>
进入编辑页面,出现如下报错,改正
加载工时明细失败: Error: 请联系管理员查看!(10002)
at __webpack_exports__.default (axios.js:176:1)