Files
td_official/src/views/biddingManagemen/listOfWinningBids/index copy 2.vue
2025-08-22 19:48:09 +08:00

525 lines
18 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="p-4 bg-gray-50 min-h-screen">
<!-- 卡片容器控制最大宽度+居中+圆角阴影 -->
<el-card shadow="hover" class="max-w-6xl mx-auto rounded-xl overflow-hidden border-0" style="background-color: #ffffff">
<!-- 卡片头部项目信息展示区非表单布局 -->
<template #header>
<div class="bg-blue-50 px-6 rounded-t-xl" style="padding: 10px 20px">
<h3 class="el-card__header-title text-lg font-semibold text-blue-800">投标项目信息</h3>
<h4>{{ currentProject.name }}</h4>
<!-- 项目信息部分 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="project-info-item">
<span>负责人</span>
<span> {{ projectInfo.principal || '-' }}</span>
</div>
<div class="project-info-item">
<span>负责人电话</span>
<span> {{ projectInfo.principalPhone || '-' }}</span>
</div>
<div class="project-info-item">
<span>项目类型</span>
<span> {{ getDictLabel(project_type, projectInfo.projectType) || '-' }}</span>
</div>
<div class="project-info-item">
<span>项目阶段</span>
<span> {{ getDictLabel(project_stage, projectInfo.projectStage) || '-' }}</span>
</div>
<div class="project-info-item">
<span>开工时间</span>
<span> {{ projectInfo.onStreamTime || '-' }}</span>
</div>
<div class="project-info-item">
<span>经纬度</span>
<span> {{ projectInfo.lng || '-' }}{{ projectInfo.lat || '-' }}</span>
</div>
<div class="project-info-item md:col-span-2 lg:col-span-3">
<span>项目地址</span>
<span> {{ projectInfo.projectSite || '-' }}</span>
</div>
<div class="project-info-item">
<span>计划容量(M)</span>
<span> {{ projectInfo.plan || '-' }}</span>
</div>
<div class="project-info-item">
<span>实际容量(M)</span>
<span> {{ projectInfo.actual || '-' }}</span>
</div>
<div class="project-info-item">
<span>设计总量(M)</span>
<span> {{ projectInfo.designTotal || '-' }}</span>
</div>
<div class="project-info-item md:col-span-2 lg:col-span-3">
<span>备注</span>
<span> {{ projectInfo.remark || '-' }}</span>
</div>
</div>
</div>
<div class="mt-4 mb-6">
<el-button @click="isDisabled = false" type="primary" class="px-8 py-2.5 transition-all duration-300 font-medium" v-if="isDisabled">
点击编辑
</el-button>
</div>
</template>
<!-- 中标信息表单区域保持原有逻辑 -->
<el-form
:disabled="isDisabled"
ref="listOfWinningBidsFormRef"
:model="form"
:rules="rules"
label-width="150px"
class="p-6 pt-4"
style="background-color: #ffffff"
>
<el-row :gutter="32">
<el-col :span="12">
<el-form-item label="中标价(美元)" prop="winningBidOriginal" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model.number="form.winningBidOriginal" type="number" placeholder="请输入中标价" @input="calculateWinningBid" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="汇率" prop="exchangeRate" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model.number="form.exchangeRate" type="number" placeholder="请输入汇率" step="0.0001" @input="calculateWinningBid" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="币种" prop="currency" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model="form.currency" placeholder="请输入币种" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="中标价(人民币)" prop="winningBid" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model="form.winningBid" type="number" placeholder="根据美元中标价和汇率自动计算" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="中标日期" prop="bidWinningDate" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-date-picker
clearable
v-model="form.bidWinningDate"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择中标日期"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="投标保证金(人民币)" prop="bidDeposit" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model="form.bidDeposit" type="number" placeholder="请输入投标保证金" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否退还" prop="whetherSendBack" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-radio-group v-model="form.whetherSendBack">
<el-radio label="是" border></el-radio>
<el-radio label="否" border></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属主体" prop="subject" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model="form.subject" placeholder="请输入所属主体" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="建设单位" prop="construction" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model="form.construction" placeholder="请输入建设单位" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="总造价" prop="totalCost" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model="form.totalCost" placeholder="请输入总造价" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="立项申请人" prop="projectApplicant" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model="form.projectApplicant" placeholder="请输入立项申请人" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="立项部门" prop="projectApplicantDept" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model="form.projectApplicantDept" placeholder="请输入立项部门" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="立项申请日期" prop="projectApplicantTime" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-date-picker
clearable
v-model="form.projectApplicantTime"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择立项申请日期"
/>
</el-form-item>
</el-col>
<!-- <el-col :span="12">
<el-form-item label="项目编号" prop="projectNumbering" class="rounded-lg border border-gray-100 p-1 mb-5">
<el-input v-model="form.projectNumbering" placeholder="请输入项目编号" />
</el-form-item>
</el-col> -->
<el-col :span="12">
<el-form-item label="中标通知书" prop="projectNumbering" class="rounded-lg border border-gray-100 p-1 mb-5">
<file-upload v-model="form.bidFile" :limit="10" :file-type="['pdf']" :file-size="50" />
</el-form-item>
</el-col>
</el-row>
<!-- 操作按钮区域 -->
<el-row v-if="!isDisabled" class="mt-8">
<el-col :span="24" class="text-center">
<el-button
:loading="buttonLoading"
type="primary"
@click="submitForm"
v-hasPermi="['bidding:listOfWinningBids:add', 'bidding:listOfWinningBids:edit']"
class="rounded-full px-8"
size="large"
>
确认提交
</el-button>
<el-button type="default" @click="resetForm" class="ml-6 rounded-full px-8" size="large"> 重置 </el-button>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</template>
<script setup name="ListOfWinningBidsForm" lang="ts">
import { ref, reactive, toRefs, watch, onMounted, onUnmounted, getCurrentInstance, ComponentInternalInstance, computed } from 'vue';
import { addListOfWinningBids, updateListOfWinningBids, listListOfWinningBids, getListOfWinningBids } from '@/api/bidding/listOfWinningBids';
import { ListOfWinningBidsVO, ListOfWinningBidsForm } from '@/api/bidding/listOfWinningBids/types';
import { useUserStoreHook } from '@/store/modules/user';
import { ElFormInstance, ElMessage } from 'element-plus';
import { getProject, updateProject } from '@/api/project/project'; // 补充项目信息更新接口
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { project_type, project_stage } = toRefs<any>(proxy?.useDict('project_type', 'project_stage'));
// 用户状态管理
const userStore = useUserStoreHook();
// 当前选中项目从store获取
const currentProject = computed(() => userStore.selectedProject);
// 项目信息(非表单绑定,直接响应式数据)
const projectInfo = reactive({
projectName: undefined,
shortName: undefined,
pId: undefined,
status: undefined,
picUrl: undefined,
remark: undefined,
projectType: undefined,
projectCategory: undefined,
deletedAt: undefined,
projectSite: undefined,
principal: undefined,
principalPhone: undefined,
actual: undefined,
lng: undefined,
lat: undefined,
plan: undefined,
onStreamTime: undefined,
playCardStart: undefined,
playCardEnd: undefined,
designTotal: undefined,
securityAgreement: undefined,
sort: 0,
showHidden: undefined,
isDelete: undefined
});
// 表单相关引用
const listOfWinningBidsFormRef = ref<ElFormInstance>();
// 加载状态
const buttonLoading = ref(false);
// 编辑/查看状态控制
const isDisabled = ref(false);
// 表单初始数据
const initFormData: ListOfWinningBidsForm = {
id: undefined,
projectId: currentProject.value?.id,
projectStatus: undefined,
projectName: undefined,
winningBidOriginal: undefined,
exchangeRate: undefined,
currency: undefined,
subject: undefined,
winningBid: undefined,
bidWinningDate: undefined,
bidDeposit: undefined,
whetherSendBack: undefined,
construction: undefined,
totalCost: undefined,
projectApplicant: undefined,
projectApplicantDept: undefined,
projectApplicantTime: undefined,
processStatus: undefined,
projectNumbering: undefined
};
// 表单数据与验证规则
const data = reactive({
form: { ...initFormData } as ListOfWinningBidsForm,
rules: {
projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }],
projectName: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
winningBidOriginal: [{ required: true, message: '请输入原始中标价', trigger: 'blur' }],
exchangeRate: [
{ required: true, message: '请输入汇率', trigger: 'blur' },
{ type: 'number', min: 0.001, message: '汇率需大于0', trigger: 'blur' }
],
currency: [{ required: true, message: '请输入币种', trigger: 'blur' }],
subject: [{ required: true, message: '请输入所属主体', trigger: 'blur' }],
winningBid: [{ required: true, message: '请输入中标价', trigger: 'blur' }],
bidWinningDate: [{ required: true, message: '请选择中标日期', trigger: 'blur' }],
projectNumbering: [{ required: true, message: '请输入项目编号', trigger: 'blur' }]
} as Record<string, any>
});
// 解构响应式数据
const { form, rules } = toRefs(data);
/**
* 根据字典值获取字典标签(用于项目类型/阶段的文本展示)
*/
const getDictLabel = (dictList: any[], value: any) => {
if (!dictList || !value) return '';
const dictItem = dictList.find((item) => item.value === value);
return dictItem ? dictItem.label : '';
};
/**
* 计算人民币中标价
*/
const calculateWinningBid = () => {
const dollarAmount = Number(form.value.winningBidOriginal);
const rate = Number(form.value.exchangeRate);
if (isNaN(dollarAmount) || isNaN(rate) || dollarAmount <= 0 || rate <= 0) {
form.value.winningBid = undefined;
return;
}
const result = dollarAmount * rate;
form.value.winningBid = Number(result.toFixed(2));
};
/**
* 页面初始化 - 获取已有数据(如存在)
*/
const initData = async () => {
try {
if (currentProject.value?.id) {
const res = await listListOfWinningBids({ projectId: currentProject.value.id });
if (res.code === 200) {
resetForm();
if (!res.data) {
isDisabled.value = false;
return;
}
Object.assign(form.value, res.data);
isDisabled.value = true;
}
}
} catch (error) {
console.error('初始化中标数据失败:', error);
}
};
/**
* 提交表单(含项目信息+中标信息同步提交)
*/
const submitForm = () => {
listOfWinningBidsFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
try {
// 1. 计算人民币中标价
calculateWinningBid();
// 2. 绑定项目ID和项目名称从项目信息同步
form.value.projectId = currentProject.value?.id;
form.value.projectName = projectInfo.projectName;
// 3. 先更新项目信息(若项目信息有修改)
if (currentProject.value?.id) {
await updateProject({ id: currentProject.value.id, ...projectInfo });
}
// 4. 再提交中标信息(新增/编辑逻辑)
const isEdit = !!form.value.id;
if (isEdit) {
await updateListOfWinningBids(form.value);
} else {
await addListOfWinningBids(form.value);
}
// 5. 提交成功后切换为查看状态
isDisabled.value = true;
ElMessage.success(isEdit ? '编辑成功' : '提交成功');
} catch (error) {
ElMessage.error('提交失败,请重试');
console.error('提交表单失败:', error);
} finally {
buttonLoading.value = false;
}
}
});
};
/**
* 获取项目详细信息
*/
const getProjectDetail = async () => {
try {
if (currentProject.value?.id) {
const res = await getProject(currentProject.value.id);
Object.assign(projectInfo, res.data);
}
} catch (error) {
console.error('获取项目详情失败:', error);
ElMessage.error('获取项目信息失败');
}
};
/**
* 重置表单(含项目信息重置)
*/
const resetForm = () => {
// 重置中标表单
form.value = { ...initFormData, projectId: currentProject.value?.id };
listOfWinningBidsFormRef.value?.resetFields();
// 重置项目信息(恢复为当前项目的原始数据)
getProjectDetail();
};
/**
* 监听项目ID变化 - 重新初始化数据
*/
const projectIdWatcher = watch(
() => currentProject.value?.id,
(newId) => {
if (newId) {
form.value.projectId = newId;
getProjectDetail();
initData();
}
}
);
// 页面挂载时初始化
onMounted(() => {
getProjectDetail();
initData();
});
// 页面卸载时清除监听
onUnmounted(() => {
projectIdWatcher();
});
</script>
<style scoped>
/* 全局背景色 */
.bg-gray-50 {
background-color: #f9fafb;
}
/* 项目信息项布局样式 */
.project-info-item {
transition: all 0.2s ease;
padding: 4px 0;
color: #696969;
}
/* 输入框/选择器统一样式 */
.el-input__wrapper,
.el-date-editor .el-input__wrapper,
.el-select__wrapper {
border-radius: 6px !important;
transition: all 0.2s ease;
}
/* 输入框hover效果 */
.el-input__wrapper:hover,
.el-date-editor .el-input__wrapper:hover,
.el-select__wrapper:hover {
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
}
/* 卡片头部文字样式 */
.el-card__header-title {
display: flex;
align-items: center;
height: 100%;
}
/* 按钮样式优化 */
.el-button {
transition: all 0.2s ease;
}
.el-button:hover {
transform: translateY(-1px);
}
/* 表单项样式优化 */
.el-form-item {
transition: all 0.2s ease;
}
.el-form-item:hover {
border-color: #e6f7ff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
/* 响应式网格布局适配 */
@media (max-width: 767px) {
.grid {
display: grid;
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
}
@media (min-width: 768px) and (max-width: 1023px) {
.md\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.md\:col-span-2 {
grid-column: span 2 / span 2;
}
}
@media (min-width: 1024px) {
.lg\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.lg\:col-span-3 {
grid-column: span 3 / span 3;
}
}
/* 间距样式 */
.gap-6 {
gap: 1.5rem;
}
.mb-1 {
margin-bottom: 0.25rem;
}
.mb-6 {
margin-bottom: 1.5rem;
}
.mt-4 {
margin-top: 1rem;
}
.mt-6 {
margin-top: 1.5rem;
}
.mt-8 {
margin-top: 2rem;
}
</style>