Files
im-uniapp/pages/workbench/construction/detail.vue
2025-07-08 13:45:04 +08:00

670 lines
16 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>
<view class="container">
<!-- 基本信息展示 -->
<view class="info-card" v-if="receivedParams.reportTitle">
<view class="info-header">
<u-icon name="info-circle" size="40" color="#409eff"></u-icon>
<text class="info-title">汇报概述信息</text>
</view>
<view class="info-content">
<view class="info-item">
<text class="info-label">汇报标题</text>
<text class="info-value">{{ receivedParams.reportTitle }}</text>
</view>
<view class="info-item">
<text class="info-label">汇报人</text>
<text class="info-value">{{ receivedParams.reporter }}</text>
</view>
<view class="info-item">
<text class="info-label">涉及项目</text>
<text class="info-value">{{ receivedParams.projectName }}</text>
</view>
</view>
</view>
<!-- 数据列表 -->
<view class="list-wrapper">
<u-swipe-action>
<u-swipe-action-item
v-for="(item, index) in reportDetailList"
:key="item.detailId"
:options="[{text: '删除', style: {background: '#ff4d4f', color: '#fff'}}]"
@click="handleDelete(item)"
>
<view class="detail-list-item">
<view class="item-row">
<text class="item-label">设备编号</text>
<text class="item-value">{{ item.deviceCode || '-' }}</text>
</view>
<view class="item-row">
<text class="item-label">设备类别</text>
<text class="item-value">{{ item.category || '-' }}</text>
</view>
<view class="item-row">
<text class="item-label">汇报详情内容</text>
<text class="item-value">{{ item.reportDetail || '-' }}</text>
</view>
<view class="item-row">
<text class="item-label">图片概况</text>
<view v-if="item.ossIds">
<u-image
:src="item.ossIds.split(',')[0]"
width="60rpx"
height="60rpx"
@click.stop="handleImageDetail(item)"
></u-image>
</view>
<text v-else>-</text>
</view>
<view class="item-row">
<text class="item-label">备注</text>
<text class="item-value">{{ item.remark || '-' }}</text>
</view>
</view>
</u-swipe-action-item>
</u-swipe-action>
</view>
<!-- 分页 -->
<u-loadmore
v-if="total > 0"
:status="loadMoreStatus"
@loadmore="loadMore"
></u-loadmore>
<!-- 新增圆形按钮 -->
<view class="add-button-wrapper">
<view class="add-button" @click="handleAdd">
<u-icon name="plus" color="#fff" size="40"></u-icon>
</view>
</view>
<!-- 新增弹窗 -->
<uni-popup ref="formPopup" type="bottom" :mask-click="false">
<view class="form-popup">
<view class="popup-header">
<text class="popup-title">添加施工进度汇报详情</text>
<u-icon name="close" @click="cancel" size="40"></u-icon>
</view>
<scroll-view scroll-y class="form-content">
<u-form :model="form" ref="form" label-position="top" :rules="rules">
<u-form-item label="设备编号" prop="deviceCode">
<u-input v-model="form.deviceCode" placeholder="请输入设备唯一编号"></u-input>
</u-form-item>
<u-form-item label="设备类别" prop="category">
<u-input v-model="form.category" placeholder="请输入设备类别"></u-input>
</u-form-item>
<u-form-item label="详情内容" prop="reportDetail">
<u-textarea
v-model="form.reportDetail"
placeholder="请输入内容"
:height="200"
></u-textarea>
</u-form-item>
<u-form-item label="图像" prop="ossIds">
<view>
<u-button @click="handleChooseAndUpload">选择/拍照并上传图片</u-button>
<view class="image-list" v-if="fileList.length">
<image v-for="(item, idx) in fileList" :key="idx" :src="item.url" style="width: 80px; height: 80px; margin: 8px; border-radius: 8px;" />
</view>
</view>
</u-form-item>
<u-form-item label="备注" prop="remark">
<u-input v-model="form.remark" placeholder="请输入备注"></u-input>
</u-form-item>
</u-form>
</scroll-view>
<view class="popup-footer">
<u-button type="primary" @click="submitForm" :loading="buttonLoading">确定</u-button>
<u-button @click="cancel">取消</u-button>
</view>
</view>
</uni-popup>
<!-- 图片详情抽屉 -->
<u-popup v-model="imageDrawer" mode="right" width="600rpx" height="100%">
<view class="image-drawer">
<view class="drawer-header">
<text class="drawer-title">图片列表</text>
<u-icon name="close" @click="imageDrawer = false" size="40"></u-icon>
</view>
<scroll-view scroll-y class="image-content">
<view class="image-grid">
<u-image
v-for="(img, index) in srcList"
:key="index"
:src="img"
width="150rpx"
height="150rpx"
@click="previewImage(img, index)"
></u-image>
</view>
</scroll-view>
</view>
</u-popup>
</view>
</template>
<script>
import { listReportDetail, addReportDetail, delReportDetail } from '@/api/oa/reportDetail'
import { uploadImage } from '@/api/common/upload'
export default {
data() {
return {
// 按钮loading
buttonLoading: false,
// 遮罩层
loading: true,
// 选中数组
selectedIds: [],
// 全选状态
selectAll: false,
// 显示搜索条件
showSearch: false,
// 总条数
total: 0,
// 汇报详情表格数据
reportDetailList: [],
// 设备类别选项
categoryOptions: [
{ label: '挖掘机', value: '挖掘机' },
{ label: '推土机', value: '推土机' },
{ label: '装载机', value: '装载机' },
{ label: '起重机', value: '起重机' },
{ label: '混凝土泵车', value: '混凝土泵车' },
{ label: '其他', value: '其他' }
],
// 图片抽屉
imageDrawer: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
summaryId: undefined,
deviceCode: undefined,
category: undefined,
reportDetail: undefined,
},
// 表单参数
form: {},
// 文件列表
fileList: [],
// 图片列表
srcList: [],
// 加载更多状态
loadMoreStatus: 'loadmore',
// 接收到的参数
receivedParams: {},
// 表单校验规则
rules: {
deviceCode: [
{ required: true, message: "设备唯一编号不能为空", trigger: "blur" }
],
category: [
{ required: true, message: "设备类别不能为空", trigger: "blur" }
],
reportDetail: [
{ required: true, message: "汇报详情内容不能为空", trigger: "blur" }
],
ossIds: [
{ required: true, message: "请上传图片", trigger: "change" }
]
}
}
},
onLoad(options) {
// 获取路由参数
if (options.summaryId) {
this.queryParams.summaryId = options.summaryId;
this.form.summaryId = options.summaryId;
}
// 接收其他参数并设置页面标题
if (options.reportTitle) {
const title = decodeURIComponent(options.reportTitle);
uni.setNavigationBarTitle({
title: title || '施工进度详情'
});
}
// 保存接收到的参数到data中方便后续使用
this.receivedParams = {
summaryId: options.summaryId,
reportTitle: options.reportTitle ? decodeURIComponent(options.reportTitle) : '',
reporter: options.reporter ? decodeURIComponent(options.reporter) : '',
projectName: options.projectName ? decodeURIComponent(options.projectName) : ''
};
this.getList();
if (this.form.ossIds) {
this.fileList = this.form.ossIds.split(',').map(url => ({
url,
status: 'success',
message: '上传成功'
}));
}
},
methods: {
/** 查询汇报详情列表 */
getList() {
this.loading = true;
listReportDetail(this.queryParams).then(response => {
this.reportDetailList = response.rows || [];
this.total = response.total || 0;
this.loading = false;
if (this.reportDetailList.length >= this.total) {
this.loadMoreStatus = 'nomore';
}
// 重置选中状态
this.selectedIds = [];
this.selectAll = false;
}).catch(() => {
this.loading = false;
});
},
// 切换搜索显示
toggleSearch() {
this.showSearch = !this.showSearch;
},
// 全选/取消全选
handleSelectAll(value) {
if (value) {
// 全选
this.selectedIds = this.reportDetailList.map(item => item.detailId);
} else {
// 取消全选
this.selectedIds = [];
}
},
// 选择变化
handleSelectionChange(value) {
// value 是选中的 ID 数组
this.selectedIds = value;
// 检查是否全选
this.selectAll = this.reportDetailList.length > 0 &&
this.selectedIds.length === this.reportDetailList.length;
},
// 取消按钮
cancel() {
this.$refs.formPopup.close();
this.reset();
},
// 表单重置
reset() {
this.form = {
summaryId: this.queryParams.summaryId,
deviceCode: undefined,
category: undefined,
reportDetail: undefined,
ossIds: undefined,
remark: undefined,
};
this.fileList = [];
this.$refs.form && this.$refs.form.resetFields();
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.queryParams = {
pageNum: 1,
pageSize: 10,
summaryId: this.queryParams.summaryId,
deviceCode: undefined,
category: undefined,
reportDetail: undefined,
};
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.$refs.formPopup.open();
},
/** 提交按钮 */
submitForm() {
console.log('[submitForm] called');
// 设备编号校验
if (!this.form.deviceCode) {
uni.showToast({ title: '设备唯一编号不能为空', icon: 'none' });
return;
}
// 设备类别校验
if (!this.form.category) {
uni.showToast({ title: '设备类别不能为空', icon: 'none' });
return;
}
// 详情内容校验
if (!this.form.reportDetail) {
uni.showToast({ title: '汇报详情内容不能为空', icon: 'none' });
return;
}
// 图片校验
if (!this.form.ossIds) {
uni.showToast({ title: '请上传图片', icon: 'none' });
return;
}
this.buttonLoading = true;
console.log('[submitForm] 手动校验通过,提交数据:', this.form);
addReportDetail(this.form).then(response => {
console.log('[submitForm] 新增成功:', response);
uni.showToast({ title: '新增成功', icon: 'success' });
this.$refs.formPopup.close();
this.getList();
}).catch((err) => {
console.error('[submitForm] 新增失败:', err);
uni.showToast({ title: '新增失败', icon: 'error' });
}).finally(() => {
this.buttonLoading = false;
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row ? row.detailId : this.selectedIds;
if (!ids || (Array.isArray(ids) && ids.length === 0)) {
uni.showToast({
title: '请选择要删除的数据',
icon: 'none'
});
return;
}
uni.showModal({
title: '确认删除',
content: '是否确认删除选中的数据?',
success: (res) => {
if (res.confirm) {
this.loading = true;
const deleteIds = Array.isArray(ids) ? ids.join(',') : ids;
delReportDetail(deleteIds).then(() => {
this.loading = false;
this.getList();
uni.showToast({
title: '删除成功',
icon: 'success'
});
}).catch(() => {
this.loading = false;
uni.showToast({
title: '删除失败',
icon: 'error'
});
});
}
}
});
},
/** 查看图片详情 */
handleImageDetail(row) {
if (row.ossIds) {
this.srcList = row.ossIds.split(',');
this.imageDrawer = true;
}
},
// 预览图片
previewImage(current, index) {
uni.previewImage({
current: current,
urls: this.srcList
});
},
// 加载更多
loadMore() {
if (this.loadMoreStatus === 'nomore') return;
this.loadMoreStatus = 'loading';
this.queryParams.pageNum += 1;
listReportDetail(this.queryParams).then(response => {
const newData = response.rows || [];
this.reportDetailList = [...this.reportDetailList, ...newData];
this.total = response.total || 0;
// 判断是否还有更多数据:当前获取的数据总数 >= 总数据量
if (this.reportDetailList.length >= this.total) {
this.loadMoreStatus = 'nomore';
} else {
this.loadMoreStatus = 'loadmore';
}
}).catch(() => {
this.loadMoreStatus = 'loadmore';
this.queryParams.pageNum -= 1;
});
},
async handleChooseAndUpload() {
try {
const { tempFilePaths } = await new Promise((resolve, reject) => {
uni.chooseImage({
count: 9,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: resolve,
fail: reject
});
});
console.log('[handleChooseAndUpload] 选择图片:', tempFilePaths);
const uploadResults = [];
for (const path of tempFilePaths) {
const res = await uploadImage(path);
uploadResults.push({ url: res.url });
console.log('[handleChooseAndUpload] 上传成功:', res);
}
this.fileList = uploadResults;
this.form.ossIds = uploadResults.map(f => f.url).join(',');
console.log('[handleChooseAndUpload] 最终ossIds:', this.form.ossIds);
} catch (e) {
uni.showToast({ title: '图片选择或上传失败', icon: 'none' });
console.error('[handleChooseAndUpload] 失败:', e);
}
},
deleteFile(event) {
const { index } = event;
this.fileList.splice(index, 1);
this.form.ossIds = this.fileList.map(f => f.url).join(',');
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding: 20rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.info-card {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
border-radius: 10rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.info-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.info-title {
font-size: 32rpx;
font-weight: bold;
margin-left: 20rpx;
color: #333;
}
}
.info-content {
.info-item {
display: flex;
margin-bottom: 15rpx;
&:last-child {
margin-bottom: 0;
}
.info-label {
font-size: 28rpx;
color: #666;
width: 160rpx;
flex-shrink: 0;
}
.info-value {
font-size: 28rpx;
color: #333;
flex: 1;
}
}
}
}
.list-wrapper {
background: #fff;
border-radius: 10rpx;
padding: 10rpx 0;
}
.detail-list-item {
padding: 32rpx 24rpx;
background: #fff;
border-radius: 12rpx;
margin-bottom: 16rpx;
border: 1rpx solid #e9ecef;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
.item-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.item-label {
color: #888;
min-width: 140rpx;
}
.item-value {
color: #333;
flex: 1;
}
}
}
.form-popup {
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
overflow: hidden;
width: 100vw;
max-height: 80vh;
display: flex;
flex-direction: column;
}
.form-popup .popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #e9ecef;
}
.form-popup .popup-title {
font-size: 32rpx;
font-weight: bold;
}
.form-popup .form-content {
padding: 30rpx;
flex: 1;
overflow-y: auto;
min-height: 0;
max-height: 60vh;
}
.form-popup .popup-footer {
display: flex;
gap: 20rpx;
padding: 30rpx;
border-top: 1rpx solid #e9ecef;
background: #fff;
position: sticky;
bottom: 0;
z-index: 2;
}
.image-drawer {
background-color: #fff;
height: 100%;
.drawer-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #e9ecef;
.drawer-title {
font-size: 32rpx;
font-weight: bold;
}
}
.image-content {
padding: 30rpx;
height: calc(100% - 100rpx);
.image-grid {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
}
}
.form-popup .u-form-item__label, .form-popup .u-form-item__label__text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.add-button-wrapper {
position: fixed;
bottom: 40rpx;
right: 40rpx;
z-index: 999;
}
.add-button {
width: 120rpx;
height: 120rpx;
background: linear-gradient(135deg, #007bff, #0056b3);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 16rpx rgba(0, 123, 255, 0.3);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
box-shadow: 0 2rpx 8rpx rgba(0, 123, 255, 0.4);
}
}
</style>