Files
fad_oa/ruoyi-ui/src/views/oa/project/report/my.vue

577 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="96px">
<el-form-item label="项目名称" prop="projectId">
<project-select v-model="queryParams.projectId" style="width: 200px; margin-right: 10px;"></project-select>
</el-form-item>
<el-form-item label="项目编号" prop="projectNum">
<el-input v-model="queryParams.projectNum" placeholder="请输入项目编号" clearable />
</el-form-item>
<el-form-item label="项目代号" prop="projectCode">
<el-input v-model="queryParams.projectCode" placeholder="请输入项目代号" clearable />
</el-form-item>
<el-form-item label="报工人" prop="nickName">
<el-input v-model="queryParams.nickName" placeholder="请输入报工人" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="经办部门" prop="deptId">
<el-select v-model="queryParams.deptId" filterable placeholder="请选择经办部门">
<el-option v-for="item in deptList" :key="item.deptId" :label="item.deptName" :value="item.deptId">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="报工时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" type="date" placeholder="请输入报工时间" value-format="yyyy-MM-dd">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['oa:projectReport:add']">新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate"
v-hasPermi="['oa:projectReport:edit']">修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete"
v-hasPermi="['oa:projectReport:remove']">删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
v-hasPermi="['oa:projectReport:export']">导出
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleSupp"
v-hasPermi="['oa:projectReport:edit']">补录</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 替换表格为日历视图 -->
<el-calendar v-loading="loading" ref="calendar">
<!-- 自定义日历单元格内容 -->
<template slot="dateCell" slot-scope="{date, data}">
<!-- 格式化日期为 yyyy-MM-dd 用于匹配报工记录 -->
<div class="calendar-day">
<span class="date">{{ data.day.split('-')[2] }}</span>
<!-- 显示当天的报工记录 -->
<div class="report-item" v-for="item in getDayReports(formatDate(date))" :key="item.reportId"
@click="openDetail(item)">
<el-tag size="mini" type="info">{{ item.projectName || '无项目' }}</el-tag>
<span class="report-desc">{{ item.workPlace }} ({{ item.nickName }})</span>
</div>
</div>
</template>
</el-calendar>
<!-- 保留分页日历视图仍需分页逻辑仅隐藏表格分页展示 -->
<!-- <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" style="margin-top: 20px;" /> -->
<!-- 添加或修改项目报工对话框 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
<!-- 今日报工提示 -->
<el-alert v-if="hasTodayReport" title="今日已报工" type="warning" description="您今天已有报工记录,提交后将修改今日报工内容" show-icon
:closable="false" style="margin-bottom: 20px;">
</el-alert>
<!-- 定位权限说明 -->
<el-alert
v-if="workPlaceLocateError && !form.reportId"
title="定位获取失败"
type="info"
:closable="false"
style="margin-bottom: 20px;">
<div slot="default">
<p style="margin: 5px 0; font-weight: bold;">如果浏览器无法获取定位,请按以下步骤操作:</p>
<ol style="margin: 5px 0; padding-left: 20px; line-height: 1.8;">
<li>在浏览器地址栏输入:<code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px;">edge://flags/#unsafely-treat-insecure-origin-as-secure</code>(Edge) 或 <code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px;">chrome://flags/#unsafely-treat-insecure-origin-as-secure</code>(Chrome)</li>
<li>找到选项 <strong>"Insecure origins treated as secure"</strong></li>
<li>输入您的站点地址:<code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px;">http://49.232.154.205</code>,然后设置为 <strong>Enabled</strong></li>
<li>重启浏览器,再尝试点击重新获取定位按钮</li>
</ol>
</div>
</el-alert>
<el-form ref="form" :model="form" :rules="rules" label-width="96px">
<el-form-item label="工作地点" prop="workPlace">
<el-input
v-model="form.workPlace"
:placeholder="workPlaceLoading ? '正在获取定位…' : '请输入工作地点或点击「重新获取定位」'"
>
<el-button
slot="append"
icon="el-icon-location-outline"
:loading="workPlaceLoading"
@click="refreshWorkPlace"
>重新获取定位</el-button>
</el-input>
<div v-if="workPlaceLocateError" class="work-place-error">{{ workPlaceLocateError }}</div>
</el-form-item>
<el-form-item label="是否出差" prop="isTrip">
<el-radio-group v-model="form.isTrip">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<template v-if="form.isTrip === 1">
<el-form-item label="国内/国外" prop="workType">
<el-radio-group v-model="form.workType">
<el-radio :label="0">国内</el-radio>
<el-radio :label="1">国外</el-radio>
</el-radio-group>
</el-form-item>
</template>
<el-form-item label="请选择项目" prop="projectId">
<project-select v-model="form.projectId" style="width: 100%;" />
</el-form-item>
<el-form-item label="报工内容">
<editor v-model="form.content" :min-height="192" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<!-- 只在编辑模式或者补录模式下下显示报工时间 -->
<el-form-item v-if="form.reportId || suppVisible" label="报工时间" prop="createTime">
<el-date-picker v-model="form.createTime" type="datetime" placeholder="请选择报工时间"
value-format="yyyy-MM-dd HH:mm:ss" format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
</el-form-item>
<el-form-item v-if="suppVisible" label="报工人" prop="userId">
<el-select v-model="form.userId" filterable clearable placeholder="请选择报工人">
<el-option v-for="item in userList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<project-report-detail v-model="detailVisible" :record="currentRow" />
</div>
</template>
<script>
import {
addProjectReport,
delProjectReport,
getProjectReport,
getTodayProjectReport,
listProjectReport,
suppProjectReport,
updateProjectReport
} from "@/api/oa/projectReport";
import { listDept } from "@/api/system/dept";
import { listUser } from "@/api/system/user";
import ProjectSelect from "@/components/fad-service/ProjectSelect";
import ProjectReportDetail from "@/views/oa/project/report/components/ProjectReportDetail.vue";
import {
EMPTY_GEOCODE,
geolocationUserMessage,
resolveWorkPlaceFromBrowser
} from "@/utils/geolocationWorkPlace";
export default {
name: "ProjectReport",
components: { ProjectReportDetail, ProjectSelect },
data () {
return {
workPlaceLoading: false,
workPlaceLocateError: "",
// 按钮loading
buttonLoading: false,
detailVisible: false,
// 今日报工状态
hasTodayReport: false,
todayReportId: null,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 项目报工表格数据(日历视图仍需此数组存储数据)
projectReportList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数 - 核心修改pageSize改为1000nickName默认赋值当前用户
queryParams: {
pageNum: 1,
pageSize: 1000, // 改为1000
handler: undefined,
workPlace: undefined,
projectId: null,
content: undefined,
nickName: this.$store.getters.nickName // 默认筛选当前用户的报工记录
},
deptList: [],
currentRow: {},
suppVisible: false,
userList: [],
// 表单参数
form: {},
// 表单校验
rules: {
workPlace: [
{ required: true, message: "工作地点不能为空", trigger: "blur" }
],
projectId: [
{ required: true, message: "项目 ID不能为空", trigger: "blur" }
],
content: [
{ required: true, message: "报工内容不能为空", trigger: "blur" }
],
isTrip: [
{ required: true, message: '请选择是否出差', trigger: 'change' }
],
workType: [
{
required: true,
message: '请选择出差地点',
trigger: 'change',
validator: (rule, value, callback) => {
if (this.form.isTrip === 1 && !value && value !== 0) {
callback(new Error('请选择出差地点'));
} else {
callback();
}
}
}
]
}
};
},
created () {
this.getList();
this.getUserList();
},
methods: {
/**
* @param {{ silent?: boolean, force?: boolean }} options
*/
async syncWorkPlaceFromGeolocation (options = {}) {
const silent = !!options.silent;
const force = !!options.force;
if (!force && this.form && this.form.reportId) {
return;
}
this.workPlaceLoading = true;
this.workPlaceLocateError = "";
try {
const text = await resolveWorkPlaceFromBrowser();
this.$set(this.form, "workPlace", text);
if (!silent) {
this.$modal.msgSuccess("已根据定位更新工作地点");
}
} catch (e) {
console.error("[projectReport] 工作地点定位失败", e);
const msg = geolocationUserMessage(e);
this.workPlaceLocateError = msg;
this.$set(this.form, "workPlace", undefined);
const isBrowserGeoError =
e &&
(e.code === 1 ||
e.code === 2 ||
e.code === 3 ||
e.message === "BROWSER_UNSUPPORTED" ||
e.message === EMPTY_GEOCODE);
if (isBrowserGeoError) {
this.$modal.msgWarning(msg);
}
} finally {
this.workPlaceLoading = false;
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.validateField("workPlace");
}
});
}
},
refreshWorkPlace () {
this.syncWorkPlaceFromGeolocation({ silent: false, force: true });
},
/** 格式化日期为 yyyy-MM-dd 格式 */
formatDate (date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
},
/** 获取指定日期的报工记录 */
getDayReports (dateStr) {
return this.projectReportList.filter(item => {
// 提取报工记录的日期部分(忽略时分秒)
const reportDate = item.createTime ? item.createTime.split(' ')[0] : '';
return reportDate === dateStr;
});
},
/** 查询详情 */
openDetail (row) {
getProjectReport(row.reportId).then(response => {
this.currentRow = response.data;
this.detailVisible = true;
})
},
handleSupp () {
this.suppVisible = true;
this.reset();
this.open = true;
this.title = "补录项目报工";
this.$nextTick(() => {
this.syncWorkPlaceFromGeolocation({ silent: true, force: false });
});
},
/** 检查今日报工(须返回 Promise供新增时 .finally 打开弹窗) */
checkTodayReport () {
return getTodayProjectReport()
.then(response => {
if (response.data && response.data.reportId) {
this.hasTodayReport = true;
this.todayReportId = response.data.reportId;
this.form = {
...this.form,
...response.data
};
} else {
this.hasTodayReport = false;
this.todayReportId = null;
}
})
.catch(() => {
this.hasTodayReport = false;
this.todayReportId = null;
});
},
getDeptList () {
listDept().then(res => {
this.deptList = res.data;
})
},
getUserList () {
listUser({
pageNum: 1,
pageSize: 1000
}).then(res => {
this.userList = res.rows.map(item => ({
value: item.userId,
label: item.nickName
}));
})
},
/** 查询项目报工列表 */
getList () {
this.loading = true;
listProjectReport(this.queryParams).then(response => {
this.projectReportList = response.rows;
this.total = response.total;
this.loading = false;
this.getDeptList();
});
},
// 取消按钮
cancel () {
this.open = false;
this.reset();
},
// 表单重置
reset () {
this.workPlaceLoading = false;
this.workPlaceLocateError = "";
this.form = {
reportId: undefined,
handler: undefined,
workPlace: undefined,
projectId: undefined,
content: undefined,
createTime: undefined,
createBy: undefined,
userId: undefined,
updateTime: undefined,
updateBy: undefined,
delFlag: undefined,
remark: undefined,
isTrip: undefined,
workType: undefined,
};
this.hasTodayReport = false;
this.todayReportId = null;
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.resetFields();
}
});
},
/** 搜索按钮操作 */
handleQuery () {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 - 核心修改:重置后仍保留报工人默认值 */
resetQuery () {
this.resetForm("queryForm");
// 重置后恢复报工人默认值为当前用户
this.queryParams.nickName = this.$store.getters.nickName;
this.handleQuery();
},
// 多选框选中数据(日历视图下可保留,用于批量操作)
handleSelectionChange (selection) {
this.ids = selection.map(item => item.reportId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd () {
this.reset();
this.suppVisible = false;
this.title = "添加项目报工";
this.checkTodayReport().finally(() => {
this.open = true;
this.$nextTick(() => {
if (!this.form.reportId) {
this.syncWorkPlaceFromGeolocation({ silent: true, force: false });
}
});
});
},
/** 修改按钮操作 */
handleUpdate (row) {
this.suppVisible = false;
this.loading = true;
this.reset();
const reportId = row.reportId || this.ids
getProjectReport(reportId).then(response => {
this.loading = false;
this.form = response.data;
this.open = true;
this.title = "修改项目报工";
});
},
/** 提交按钮 */
submitForm () {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
// 补录模式
if (this.suppVisible) {
suppProjectReport(this.form).then(response => {
this.$modal.msgSuccess("补录成功");
this.open = false;
this.getList();
this.suppVisible = false;
}).finally(() => {
this.buttonLoading = false;
});
return;
}
// 今日报工更新
if (this.hasTodayReport && !this.form.reportId) {
const updateData = { ...this.form, reportId: this.todayReportId };
updateProjectReport({ ...updateData, time: this.form.createTime }).then(response => {
this.$modal.msgSuccess("修改今日报工成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
} else if (this.form.reportId != null) {
updateProjectReport({ ...this.form, time: this.form.createTime }).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
} else {
addProjectReport(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
},
/** 删除按钮操作 */
handleDelete (row) {
const reportIds = row.reportId || this.ids;
this.$modal.confirm('是否确认删除项目报工编号为"' + reportIds + '"的数据项?').then(() => {
this.loading = true;
return delProjectReport(reportIds);
}).then(() => {
this.loading = false;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
}).finally(() => {
this.loading = false;
});
},
/** 导出按钮操作 */
handleExport () {
this.download('oa/projectReport/export', {
...this.queryParams
}, `projectReport_${new Date().getTime()}.xlsx`)
}
}
};
</script>
<style scoped>
/* 日历单元格样式优化 */
.calendar-day {
height: 100%;
padding: 5px;
box-sizing: border-box;
}
.date {
font-size: 14px;
margin-bottom: 4px;
display: block;
}
.report-item {
margin-bottom: 3px;
cursor: pointer;
}
.report-desc {
font-size: 12px;
color: #666;
display: block;
margin-top: 2px;
}
.work-place-error {
color: #f56c6c;
font-size: 12px;
line-height: 1.5;
margin-top: 4px;
}
</style>