明树Git Lab

Commit d9d21c69 authored by zhanghan's avatar zhanghan

投后管理开发完毕

parent 30abc7bb
...@@ -47,7 +47,6 @@ ...@@ -47,7 +47,6 @@
<el-button <el-button
link link
type="danger" type="danger"
size="small"
@click="handleDelete($index)" @click="handleDelete($index)"
:disabled="disabled" :disabled="disabled"
class="delete-btn" class="delete-btn"
......
<template>
<div class="dynamic-table-container">
<!-- 操作按钮区(新增 + 导入导出 + 自定义头部按钮) -->
<div
class="tab-handle"
v-if="!disabled && (showAddButton || showImportExport)"
>
<!-- 新增按钮 -->
<el-button
v-if="showAddButton"
type="primary"
@click="handleAdd"
:disabled="disabled"
>
{{ addButtonText || "新增" }}
</el-button>
<!-- 导入导出功能(直接内置) -->
<div
v-if="showImportExport && !disabled"
class="table-import-export"
style="margin-left: 12px"
>
<!-- 导出按钮 -->
<el-button
type="primary"
@click="handleExport"
:disabled="!modelValue.length"
:icon="Download"
>
导出
</el-button>
<!-- 导入按钮 -->
<el-upload
ref="uploadRef"
class="upload-demo"
action=""
:auto-upload="false"
:on-change="handleImport"
:show-file-list="false"
style="margin: 0 12px"
>
<template #trigger>
<el-button type="primary" :icon="Upload"> 导入 </el-button>
</template>
</el-upload>
</div>
<!-- 顶部自定义按钮插槽(可选) -->
<slot name="header-actions" />
</div>
<!-- 表格主体 -->
<el-table
:data="modelValue"
style="width: 100%"
empty-text="暂无数据"
border
:disabled="disabled"
:scrollbar-always-on="scrollbarAlwaysOn"
>
<!-- 序号列 -->
<el-table-column type="index" fixed="left" width="60" label="序号" />
<!-- 动态列 -->
<el-table-column
v-for="column in columns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth || 140"
:align="column.align || 'left'"
>
<template #default="scope">
<!-- 普通输入框 -->
<el-input
v-if="column.type === 'input'"
v-model="scope.row[column.prop]"
:placeholder="column.placeholder"
:disabled="disabled"
:maxlength="column.maxlength"
/>
<!-- 数字输入框 -->
<el-input
v-else-if="column.type === 'number'"
v-model.number="scope.row[column.prop]"
type="number"
:placeholder="column.placeholder"
:disabled="disabled"
:precision="column.precision"
:min="column.min"
:max="column.max"
/>
<!-- 文本域 -->
<el-input
v-else-if="column.type === 'textarea'"
v-model="scope.row[column.prop]"
type="textarea"
:placeholder="column.placeholder"
:disabled="disabled"
:rows="column.rows || 3"
:maxlength="column.maxlength"
:show-word-limit="column.showWordLimit || false"
/>
<!-- 下拉选择框 -->
<el-select
v-else-if="column.type === 'select'"
v-model="scope.row[column.prop]"
:placeholder="column.placeholder"
:disabled="disabled"
:multiple="column.multiple || false"
>
<el-option
v-for="item in selectOptions?.[column.optionKey] || []"
:key="item.key || item.value"
:label="item.name || item.label"
:value="item.key || item.value"
/>
</el-select>
<!-- 单选框组 -->
<el-radio-group
v-else-if="column.type === 'radio'"
v-model="scope.row[column.prop]"
:disabled="disabled"
>
<el-radio
v-for="item in selectOptions?.[column.optionKey] || []"
:key="item.key || item.value"
:label="item.key || item.value"
style="margin-right: 12px"
>
{{ item.name || item.label }}
</el-radio>
</el-radio-group>
<!-- 时间选择器 -->
<el-date-picker
v-else-if="column.type === 'date' || column.type === 'datetime'"
v-model="scope.row[column.prop]"
:type="column.type === 'datetime' ? 'datetime' : 'date'"
:format="column.format || 'YYYY-MM-DD'"
:value-format="column.valueFormat || 'YYYY-MM-DD'"
:placeholder="column.placeholder"
:disabled="disabled"
style="width: 100%"
/>
<!-- 文件上传组件 -->
<FileUploader
v-else-if="column.type === 'upload'"
v-model="scope.row[column.prop]"
:isInline="true"
:disabled="disabled"
/>
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column
label="操作"
fixed="right"
:width="operationColumnWidth"
:min-width="operationColumnMinWidth"
v-if="
!disabled &&
(showDeleteButton || operationButtons.length > 0 || $slots.operations)
"
>
<template #default="scope">
<!-- 默认删除按钮 -->
<el-button
v-if="showDeleteButton"
link
type="danger"
size="small"
@click="handleDelete(scope.$index)"
:disabled="disabled"
class="operation-btn"
>
删除
</el-button>
<!-- 自定义操作按钮 -->
<el-button
v-for="(btn, btnIndex) in operationButtons"
:key="btnIndex"
:link="btn.link !== false"
:type="btn.type || 'primary'"
:size="btn.size || 'small'"
@click="handleCustomOperation(btn, scope)"
:disabled="disabled || btn.disabled"
class="operation-btn"
>
{{ btn.text }}
</el-button>
<!-- 操作列自定义插槽 -->
<slot
name="operations"
:row="scope.row"
:index="scope.$index"
:disabled="disabled"
/>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { defineProps, defineEmits, ref } from "vue";
import { ElMessageBox, ElMessage } from "element-plus";
import { Download, Upload } from "@element-plus/icons-vue";
import FileUploader from "@/components/FileUploader/index.vue";
import { getTableFileData, exportTableFile } from "@/common/tableFileHandle";
// 定义Props
const props = defineProps({
// 核心配置
modelValue: {
type: Array,
required: true,
default: () => [],
},
columns: {
type: Array,
required: true,
default: () => [],
},
defaultRow: {
type: Object,
required: true,
default: () => ({}),
},
// 基础配置
addButtonText: {
type: String,
default: "新增",
},
disabled: {
type: Boolean,
default: false,
},
selectOptions: {
type: Object,
default: () => ({}),
},
scrollbarAlwaysOn: {
type: Boolean,
default: false,
},
// 新增/删除按钮配置
showAddButton: {
type: Boolean,
default: true,
},
showDeleteButton: {
type: Boolean,
default: true,
},
// 导入导出功能配置
showImportExport: {
type: Boolean,
default: false,
},
exportName: {
type: String,
default: "表格数据",
},
// 自定义操作按钮配置
operationButtons: {
type: Array,
default: () => [],
},
operationColumnWidth: {
type: [String, Number],
default: "auto",
},
operationColumnMinWidth: {
type: [String, Number],
default: 60,
},
});
// 定义Emits
const emit = defineEmits([
"update:modelValue",
"add",
"delete",
"custom-operation",
"import-success",
]);
// 导入上传组件Ref
const uploadRef = ref(null);
/**
* 导出数据转换:将select/radio的key值转为文字描述
* @param {Array} data 原始表格数据
* @returns {Array} 转换后的导出数据
*/
const convertDataForExport = (data) => {
if (!data || !data.length) return [];
return data.map((row) => {
const newRow = { ...row };
// 遍历列配置,处理select/radio类型字段
props.columns.forEach((column) => {
if (column.type === "select" || column.type === "radio") {
const prop = column.prop;
const optionKey = column.optionKey;
const options = props.selectOptions?.[optionKey] || [];
if (
newRow[prop] !== undefined &&
newRow[prop] !== null &&
newRow[prop] !== ""
) {
// 处理多选(select multiple)
if (column.multiple && Array.isArray(newRow[prop])) {
newRow[prop] = newRow[prop]
.map((key) => {
const option = options.find(
(item) => (item.key || item.value) === key
);
return option ? item.name || item.label : key;
})
.join(","); // 多选用中文逗号分隔
} else {
// 单选处理
const option = options.find(
(item) => (item.key || item.value) === newRow[prop]
);
newRow[prop] = option ? option.name || option.label : newRow[prop];
}
}
}
});
return newRow;
});
};
/**
* 导入数据转换:将文字描述转回对应的key值
* @param {Array} data 导入的原始数据
* @returns {Array} 转换后的表格数据
*/
const convertDataForImport = (data) => {
if (!data || !data.length) return [];
return data.map((row) => {
const newRow = { ...row };
// 遍历列配置,处理select/radio类型字段
props.columns.forEach((column) => {
if (column.type === "select" || column.type === "radio") {
const prop = column.prop;
const optionKey = column.optionKey;
const options = props.selectOptions?.[optionKey] || [];
if (
newRow[prop] !== undefined &&
newRow[prop] !== null &&
newRow[prop] !== ""
) {
// 处理多选(select multiple)
if (column.multiple) {
const textList = newRow[prop]
.split(",")
.map((item) => item.trim());
newRow[prop] = textList.map((text) => {
const option = options.find(
(item) => (item.name || item.label) === text
);
return option ? option.key || item.value : text;
});
} else {
// 单选处理
const option = options.find(
(item) => (item.name || item.label) === newRow[prop]
);
newRow[prop] = option ? option.key || option.value : newRow[prop];
}
} else {
// 空值处理为默认空
newRow[prop] = "";
}
}
});
return newRow;
});
};
// 1. 新增行
const handleAdd = () => {
try {
const newRow = { ...props.defaultRow };
const newData = [...props.modelValue, newRow];
emit("update:modelValue", newData);
emit("add", newRow);
} catch (e) {
console.error("新增行失败:", e);
ElMessage.error("新增失败,请稍后重试");
}
};
// 2. 删除行
const handleDelete = (index) => {
ElMessageBox.confirm("确认删除该项?", "提示", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
try {
const newData = [...props.modelValue];
newData.splice(index, 1);
emit("update:modelValue", newData);
emit("delete", index);
ElMessage.success("删除成功");
} catch (e) {
console.error("删除行失败:", e);
ElMessage.error("删除失败,请稍后重试");
}
})
.catch(() => {});
};
// 3. 自定义操作按钮点击
const handleCustomOperation = (btn, scope) => {
if (btn.click) {
try {
btn.click(scope.row, scope.$index);
} catch (e) {
console.error("自定义按钮点击事件执行失败:", e);
ElMessage.error("操作执行失败,请稍后重试");
}
}
emit("custom-operation", btn, scope.row, scope.$index);
};
// 4. 导出表格数据(修改:添加数据转换)
const handleExport = () => {
try {
// 先转换数据,再导出
const exportData = convertDataForExport(props.modelValue);
exportTableFile(exportData, props.columns, "", props.exportName);
ElMessage.success("导出成功");
} catch (e) {
console.error("导出失败:", e);
ElMessage.error("导出失败,请稍后重试");
}
};
// 5. 导入表格数据(修改:添加数据转换)
const handleImport = (file) => {
uploadRef.value?.clearFiles();
getTableFileData(file, props.columns)
.then((rawImportData) => {
const convertedData = convertDataForImport(rawImportData);
handleImportSuccess(convertedData);
ElMessage.success(`导入成功,共${convertedData.length}条数据`);
})
.catch((e) => {
console.error("导入失败:", e);
ElMessage.error("导入失败,请检查文件格式是否正确");
});
};
// 6. 导入成功后的数据处理
const handleImportSuccess = (importData) => {
try {
const newData = [...props.modelValue, ...importData];
emit("update:modelValue", newData);
emit("import-success", importData);
} catch (e) {
console.error("导入数据合并失败:", e);
ElMessage.error("导入数据处理失败,请稍后重试");
}
};
</script>
<style scoped lang="less">
.dynamic-table-container {
width: 100%;
}
.tab-handle {
margin-bottom: 10px;
text-align: left;
display: flex;
align-items: center;
:deep(.el-button) {
margin-right: 8px;
}
}
.table-import-export {
display: inline-flex;
align-items: center;
:deep(.el-button) {
margin-right: 8px;
}
}
.operation-btn {
margin-right: 4px;
}
:deep(.el-table) {
--el-table-header-text-color: #303133;
--el-table-row-hover-bg-color: #f8f9fa;
}
:deep(.el-upload) {
width: 100%;
}
:deep(.el-textarea) {
width: 100%;
}
:deep(.el-date-picker) {
width: 100%;
}
</style>
...@@ -173,6 +173,18 @@ const routes = [ ...@@ -173,6 +173,18 @@ const routes = [
title: "投资后评价", title: "投资后评价",
component: () => import("@/views/castbehind/evaluateAdd.vue"), component: () => import("@/views/castbehind/evaluateAdd.vue"),
}, },
{
path: "/turnover",
name: "turnover",
title: "移交管理",
component: () => import("@/views/castbehind/turnover.vue"),
},
{
path: "/turnoverAdd",
name: "turnoverAdd",
title: "移交管理",
component: () => import("@/views/castbehind/turnoverAdd.vue"),
},
], ],
}, },
{ {
......
...@@ -137,7 +137,7 @@ ...@@ -137,7 +137,7 @@
text-align: center; text-align: center;
} }
::v-deep.add-project-content .tab-content .tab-handle { ::v-deep.add-project-content .tab-content .tab-handle {
margin: 10px 0; margin-bottom: 16px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
...@@ -274,3 +274,20 @@ ...@@ -274,3 +274,20 @@
font-weight: bold; font-weight: bold;
background-color: #f5f7fa; background-color: #f5f7fa;
} }
.add-project-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
::v-deep .el-collapse-item__title {
flex: auto;
text-align: left;
}
:deep(.el-table) {
--el-table-border-color: #ebeef5;
width: 100%;
}
:deep(.el-upload) {
width: 100%;
}
...@@ -139,7 +139,7 @@ ...@@ -139,7 +139,7 @@
text-align: center; text-align: center;
} }
.tab-handle{ .tab-handle{
margin: 10px 0; margin-bottom: 16px;;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
...@@ -291,4 +291,30 @@ ...@@ -291,4 +291,30 @@
font-weight: bold; font-weight: bold;
background-color: #f5f7fa; background-color: #f5f7fa;
} }
}
.add-project-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
// 动态表格样式适配
:deep(.el-collapse-item__content) {
// padding: 16px;
}
::v-deep .el-collapse-item__title{
flex: auto;
text-align: left;
}
:deep(.el-table) {
--el-table-border-color: #ebeef5;
width: 100%;
}
:deep(.el-upload) {
width: 100%;
} }
\ No newline at end of file
...@@ -178,5 +178,5 @@ onMounted(() => { ...@@ -178,5 +178,5 @@ onMounted(() => {
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@import "@/styles/manage.less"; @import "@/styles/verticalManages.less";
</style> </style>
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
<FileUploader v-model="formData.xmgszcbg" /> <FileUploader v-model="formData.xmgszcbg" />
</el-form-item> </el-form-item>
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="投资后评价报告" name="投资后评价报告"> <el-collapse-item title="投资后评价报告" name="投资后评价报告">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="24"> <el-col :span="24">
...@@ -92,124 +93,18 @@ ...@@ -92,124 +93,18 @@
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="问题整改" name="问题整改"> <el-collapse-item title="问题整改" name="问题整改">
<div class="tab-handle"> <DynamicTable
<el-button type="primary" @click="addJsqtzjcZxjcs" v-model="formData.tzhpjwtzgs"
>新增</el-button :columns="problemColumns"
> :default-row="problemDefaultRow"
</div> :select-options="selectOptions"
:show-import-export="true"
<el-table export-name="问题整改数据"
:data="formData.tzhpjwtzgs"
style="width: 100%"
empty-text="暂无数据"
:scrollbar-always-on="true" :scrollbar-always-on="true"
border :disabled="isPreview"
> />
<el-table-column
type="index"
width="60"
fixed="left"
label="序号"
/>
<el-table-column
prop="zxjcfl"
min-width="140"
label="存在的问题"
>
<template #default="scope">
<el-input
v-model="scope.row.ccwt"
placeholder="请输入存在的问题"
/>
</template>
</el-table-column>
<el-table-column min-width="140" label="问题详述">
<template #default="scope">
<el-input
type="textarea"
v-model="scope.row.wtxs"
placeholder="请输入问题详述"
/>
</template>
</el-table-column>
<el-table-column min-width="140" label="整改措施">
<template #default="scope">
<el-input
v-model="scope.row.zgcs"
type="textarea"
placeholder="请输入整改措施"
/>
</template>
</el-table-column>
<el-table-column min-width="140" label="落实情况">
<template #default="scope">
<el-input
v-model="scope.row.lsqk"
type="textarea"
placeholder="请输入落实情况"
/>
</template>
</el-table-column>
<el-table-column min-width="140" label="责任部门">
<template #default="scope">
<el-input
v-model="scope.row.zrbm"
placeholder="请输入责任部门"
/>
</template>
</el-table-column>
<el-table-column min-width="140" label="责任人">
<template #default="scope">
<el-input
v-model="scope.row.zrr"
placeholder="请输入责任人"
/>
</template>
</el-table-column>
<el-table-column min-width="140" label="整改期限">
<template #default="scope">
<el-date-picker
v-model="scope.row.zgqx"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择整改期限"
/>
</template>
</el-table-column>
<el-table-column
min-width="140"
align="center"
label="是否关闭"
>
<template #default="scope">
<el-radio-group
placeholder="请选择是否关闭"
v-model="scope.row.sfgb"
>
<el-radio
v-for="item in options?.sf"
:key="item.id"
:id="item.name"
:value="item.key"
>{{ item.name }}</el-radio
>
</el-radio-group>
</template>
</el-table-column>
<el-table-column label="操作" width="60" fixed="right">
<template #default="scope">
<el-button
link
type="danger"
size="small"
@click="deleteJsqtzjcZxjcs(scope.$index)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-collapse-item> </el-collapse-item>
<el-collapse-item <el-collapse-item
title="全生命周期评价报告" title="全生命周期评价报告"
name="全生命周期评价报告" name="全生命周期评价报告"
...@@ -271,6 +166,8 @@ import { reactive, ref, onMounted, getCurrentInstance } from "vue"; ...@@ -271,6 +166,8 @@ import { reactive, ref, onMounted, getCurrentInstance } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import FileUploader from "../../components/FileUploader/index.vue"; import FileUploader from "../../components/FileUploader/index.vue";
// 引入增强后的DynamicTable组件
import DynamicTable from "../../components/FormDynamicTable/index.vue";
// 初始化全局变量 // 初始化全局变量
const router = useRouter(); const router = useRouter();
...@@ -286,16 +183,103 @@ const activeCollapse = ref([ ...@@ -286,16 +183,103 @@ const activeCollapse = ref([
"全生命周期评价报告", "全生命周期评价报告",
]); ]);
// 表单数据 - 补全所有字段结构 // ========== 问题整改表格配置 ==========
// 列配置(包含input、textarea、datetime、radio类型)
const problemColumns = ref([
{
prop: "ccwt",
label: "存在的问题",
type: "input",
placeholder: "请输入存在的问题",
minWidth: 140,
},
{
prop: "wtxs",
label: "问题详述",
type: "textarea",
placeholder: "请输入问题详述",
rows: 2,
minWidth: 140,
},
{
prop: "zgcs",
label: "整改措施",
type: "textarea",
placeholder: "请输入整改措施",
rows: 2,
minWidth: 140,
},
{
prop: "lsqk",
label: "落实情况",
type: "textarea",
placeholder: "请输入落实情况",
rows: 2,
minWidth: 140,
},
{
prop: "zrbm",
label: "责任部门",
type: "input",
placeholder: "请输入责任部门",
minWidth: 140,
},
{
prop: "zrr",
label: "责任人",
type: "input",
placeholder: "请输入责任人",
minWidth: 140,
},
{
prop: "zgqx",
label: "整改期限",
type: "datetime", // 日期时间选择器
placeholder: "请选择整改期限",
format: "YYYY-MM-DD HH:mm:ss",
valueFormat: "YYYY-MM-DD HH:mm:ss",
minWidth: 140,
},
{
prop: "sfgb",
label: "是否关闭",
type: "radio", // 单选框组
optionKey: "sf", // 对应selectOptions中的sf选项
align: "center",
minWidth: 140,
},
]);
// 问题整改默认行数据
const problemDefaultRow = ref({
ccwt: "",
wtxs: "",
zgcs: "",
lsqk: "",
zrbm: "",
zrr: "",
zgqx: "",
sfgb: "",
fjcl: [],
});
// 单选框选项配置
const selectOptions = ref({
sf: [
{ key: 1, name: "是" },
{ key: 2, name: "否" },
],
});
// ========== 表单数据 ==========
const formData = reactive({ const formData = reactive({
xmgszcbg: [], xmgszcbg: [],
hpjbg: [], hpjbg: [],
qsmzqpj: [], qsmzqpj: [],
// 问题整改列表(由DynamicTable自动维护)
// 问题整改列表
tzhpjwtzgs: [], tzhpjwtzgs: [],
}); });
// 加载状态 // 加载状态
const loading = ref(false); const loading = ref(false);
// 是否预览模式 // 是否预览模式
...@@ -305,33 +289,7 @@ const projectList = ref([]); ...@@ -305,33 +289,7 @@ const projectList = ref([]);
// 当前编辑的记录ID // 当前编辑的记录ID
const rcCgqyglId = ref(route.query.id || ""); const rcCgqyglId = ref(route.query.id || "");
// ========== 问题整改 操作方法 ========== // ========== 原有方法保留(移除手动新增/删除) ==========
const addJsqtzjcZxjcs = () => {
// 新增时添加正确的字段结构
formData.tzhpjwtzgs.push({
ccwt: "",
wtxs: "",
zgcs: "",
lsqk: "",
zrbm: "",
zrr: "",
zgqx: "",
sfgb: "",
fjcl: [],
});
};
const deleteJsqtzjcZxjcs = (index) => {
ElMessageBox.confirm("确认删除该项?", "提示", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
formData.tzhpjwtzgs.splice(index, 1);
ElMessage.success("删除成功");
});
};
// 获取项目列表 // 获取项目列表
const getProjectData = () => { const getProjectData = () => {
proxy.$post({ proxy.$post({
...@@ -356,7 +314,7 @@ const changeProject = (val) => { ...@@ -356,7 +314,7 @@ const changeProject = (val) => {
} }
}; };
// 获取单条记录详情(编辑/预览) // 获取单条记录详情
const getJsqtzjcDetail = () => { const getJsqtzjcDetail = () => {
if (!rcCgqyglId.value) return; if (!rcCgqyglId.value) return;
...@@ -366,13 +324,17 @@ const getJsqtzjcDetail = () => { ...@@ -366,13 +324,17 @@ const getJsqtzjcDetail = () => {
data: { id: rcCgqyglId.value }, data: { id: rcCgqyglId.value },
callback: (data) => { callback: (data) => {
loading.value = false; loading.value = false;
// 赋值基础表单数据
Object.assign(formData, data); Object.assign(formData, data);
// 确保数组字段有默认值,避免空指针 // 确保数组字段有默认值
formData.tzhpjwtzgs = data.tzhpjwtzgs || []; formData.tzhpjwtzgs = data.tzhpjwtzgs || [];
formData.xmgszcbg = data.xmgszcbg || []; formData.xmgszcbg = data.xmgszcbg || [];
formData.hpjbg = data.hpjbg || []; formData.hpjbg = data.hpjbg || [];
formData.qsmzqpj = data.qsmzqpj || []; formData.qsmzqpj = data.qsmzqpj || [];
// 同步单选选项(如果从接口获取)
if (data.sfOptions) {
selectOptions.value.sf = data.sfOptions;
}
}, },
}); });
}; };
...@@ -384,23 +346,19 @@ const backClick = () => { ...@@ -384,23 +346,19 @@ const backClick = () => {
// 保存/提交表单 // 保存/提交表单
const saveClick = () => { const saveClick = () => {
// 基础校验
if (!formData.projectId) { if (!formData.projectId) {
ElMessage.warning("请选择项目信息"); ElMessage.warning("请选择项目信息");
return; return;
} }
loading.value = true; loading.value = true;
// 区分新增/编辑
const url = rcCgqyglId.value const url = rcCgqyglId.value
? "/api/project/updateTzhpj" ? "/api/project/updateTzhpj"
: "/api/project/createTzhpj"; : "/api/project/createTzhpj";
// 组装提交数据 - 包含所有字段
const submitData = { const submitData = {
...formData, ...formData,
projectId: formData.projectId + "", // 确保为字符串类型 projectId: formData.projectId + "",
// 问题整改列表直接从formData获取
tzhpjwtzgs: formData.tzhpjwtzgs, tzhpjwtzgs: formData.tzhpjwtzgs,
}; };
...@@ -412,7 +370,6 @@ const saveClick = () => { ...@@ -412,7 +370,6 @@ const saveClick = () => {
ElMessage.success(rcCgqyglId.value ? "编辑成功" : "新增成功"); ElMessage.success(rcCgqyglId.value ? "编辑成功" : "新增成功");
router.back(-1); router.back(-1);
}, },
// 添加错误处理
error: () => { error: () => {
loading.value = false; loading.value = false;
ElMessage.error("操作失败,请稍后重试"); ElMessage.error("操作失败,请稍后重试");
...@@ -424,17 +381,18 @@ let options = ref(); ...@@ -424,17 +381,18 @@ let options = ref();
// 页面初始化 // 页面初始化
onMounted(() => { onMounted(() => {
// 获取项目列表
getProjectData(); getProjectData();
// 处理options数据,避免空指针
try { try {
options.value = JSON.parse(sessionStorage.getItem("resourceData")) || {}; options.value = JSON.parse(sessionStorage.getItem("resourceData")) || {};
// 合并全局选项到selectOptions
if (options.value.sf) {
selectOptions.value.sf = options.value.sf;
}
} catch (e) { } catch (e) {
options.value = {}; options.value = {};
console.warn("解析resourceData失败:", e); console.warn("解析resourceData失败:", e);
} }
// 如果有ID则加载详情
if (rcCgqyglId.value) { if (rcCgqyglId.value) {
getJsqtzjcDetail(); getJsqtzjcDetail();
} }
......
...@@ -92,8 +92,6 @@ const getStatementData = () => { ...@@ -92,8 +92,6 @@ const getStatementData = () => {
pagesize: pageSize.value, pagesize: pageSize.value,
}, },
callback: (data) => { callback: (data) => {
console.log(data, "data");
tableData.value = data.rows; tableData.value = data.rows;
total.value = data.count; total.value = data.count;
loading.value = false; loading.value = false;
......
...@@ -35,43 +35,7 @@ ...@@ -35,43 +35,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- <el-col :span="12">
<el-form-item label="决策总投资 一次填报锁定">
<el-input
v-model="formData.jcztz"
placeholder="请输入决策总投资 一次填报锁定"
/>
</el-form-item>
</el-col> -->
</el-row>
<!-- <el-row :gutter="20">
<el-col :span="12">
<el-form-item label="资本金内部收益率(%) 一次填报锁定">
<el-input
v-model="formData.zbjnbsyl"
placeholder="请输入资本金内部收益率(%) 一次填报锁定"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预测总投资">
<el-input
v-model="formData.ycztz"
placeholder="请输入预测总投资"
/>
</el-form-item>
</el-col>
</el-row> </el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="确认总投资">
<el-input
v-model="formData.qrztz"
placeholder="请输入确认总投资"
/>
</el-form-item>
</el-col>
</el-row> -->
</el-collapse-item> </el-collapse-item>
<!-- 投资(成本)分析会资料 --> <!-- 投资(成本)分析会资料 -->
...@@ -79,143 +43,27 @@ ...@@ -79,143 +43,27 @@
title="投资(成本)分析会资料" title="投资(成本)分析会资料"
name="投资(成本)分析会资料" name="投资(成本)分析会资料"
> >
<div class="tab-handle"> <DynamicTable
<el-button type="primary" @click="addyyqtzjcTzfxs" v-model="yyqtzjcTzfxsList"
>新增</el-button :columns="tzcbfxhzlColumns"
> :default-row="tzcbfxhzlDefaultRow"
</div> :disabled="isPreview"
<el-table />
:data="yyqtzjcTzfxsList"
style="width: 100%"
empty-text="暂无数据"
border
>
<el-table-column type="index" width="60" label="序号" />
<el-table-column prop="jd" label="时间 ">
<template #default="scope">
<!-- <el-date-picker
v-model="scope.row.jd"
type="datetime"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择"
/> -->
<el-input
v-model="scope.row.jd"
placeholder="请按照 xx年xx季度 格式填写"
/>
</template>
</el-table-column>
<el-table-column prop="ycztz" label="当期预测总投资">
<template #default="scope">
<el-input
type="number"
v-model.number="scope.row.ycztz"
placeholder="请输入当期预测总投资"
/>
</template>
</el-table-column>
<el-table-column
prop="zbjnbsyl"
label="资本金内部收益率(%)"
>
<template #default="scope">
<el-input
v-model.number="scope.row.zbjnbsyl"
placeholder="请输入资本金内部收益率(%)"
/>
</template>
</el-table-column>
<el-table-column
prop="tzcbfxhzl"
label="投资(成本)分析会资料"
>
<template #default="scope">
<FileUploader
v-model="scope.row.tzcbfxhzl"
:isInline="true"
></FileUploader>
</template>
</el-table-column>
<el-table-column label="操作" width="60">
<template #default="scope">
<el-button
link
type="danger"
size="small"
@click="deleteyyqtzjcTzfxs(scope.$index)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-collapse-item> </el-collapse-item>
<!-- 专项检查 --> <!-- 专项检查 -->
<el-collapse-item title="专项检查" name="专项检查"> <el-collapse-item title="专项检查" name="专项检查">
<div class="tab-handle"> <DynamicTable
<el-button type="primary" @click="addyyqtzjcZxjcs" v-model="yyqtzjcZxjcsList"
>新增</el-button :columns="zxjcColumns"
> add-button-text="新增"
</div> :default-row="zxjcDefaultRow"
<el-table :disabled="isPreview"
:data="yyqtzjcZxjcsList" :select-options="{
style="width: 100%" construction_classify:
empty-text="暂无数据" options?.construction_classify || [],
border }"
> />
<el-table-column type="index" width="60" label="序号" />
<el-table-column prop="zxjcfl" label="专项检查类型">
<template #default="scope">
<el-select
v-model="scope.row.zxjcfl"
placeholder="请选择专项检查类型"
>
<el-option
v-for="item in options?.construction_classify"
:key="item.key"
:label="item.name"
:value="item.key"
></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="jcjg" label="检查结果">
<template #default="scope">
<el-input
v-model.number="scope.row.jcjg"
placeholder="请输入检查结果"
/>
</template>
</el-table-column>
<el-table-column prop="zgcsqd" label="整改措施清单">
<template #default="scope">
<el-input
v-model.number="scope.row.zgcsqd"
placeholder="请输入整改措施清单"
/>
</template>
</el-table-column>
<el-table-column prop="fjcl" label="附件材料">
<template #default="scope">
<FileUploader
v-model="scope.row.fjcl"
:isInline="true"
></FileUploader>
</template>
</el-table-column>
<el-table-column label="操作" width="60">
<template #default="scope">
<el-button
link
type="danger"
size="small"
@click="deleteyyqtzjcZxjcs(scope.$index)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
</el-form> </el-form>
...@@ -230,8 +78,7 @@ ...@@ -230,8 +78,7 @@
import { reactive, ref, onMounted, getCurrentInstance } from "vue"; import { reactive, ref, onMounted, getCurrentInstance } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import FileUploader from "../../components/FileUploader/index.vue"; import DynamicTable from "@/components/FormDynamicTable/index.vue"; // 引入通用组件
// 初始化全局变量 // 初始化全局变量
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
...@@ -239,6 +86,73 @@ const { proxy } = getCurrentInstance(); ...@@ -239,6 +86,73 @@ const { proxy } = getCurrentInstance();
// 折叠面板默认展开项 // 折叠面板默认展开项
const activeCollapse = ref(["基本信息", "投资(成本)分析会资料", "专项检查"]); const activeCollapse = ref(["基本信息", "投资(成本)分析会资料", "专项检查"]);
const tzcbfxhzlColumns = ref([
{
prop: "jd",
label: "时间",
type: "input",
placeholder: "请按照 xx年xx季度 格式填写",
},
{
prop: "ycztz",
label: "当期预测总投资",
type: "input",
placeholder: "请输入当期预测总投资",
},
{
prop: "zbjnbsyl",
label: "资本金内部收益率(%)",
type: "number",
placeholder: "请输入资本金内部收益率(%)",
},
{
prop: "tzcbfxhzl",
label: "投资(成本)分析会资料",
type: "upload",
placeholder: "",
},
]);
const tzcbfxhzlDefaultRow = ref({
jd: "",
ycztz: "",
zbjnbsyl: "",
tzcbfxhzl: [],
});
const zxjcColumns = ref([
{
prop: "zxjcfl",
label: "专项检查类型",
type: "select",
optionKey: "construction_classify",
placeholder: "请选择专项检查类型",
},
{
prop: "jcjg",
label: "检查结果",
type: "input",
placeholder: "请输入检查结果",
},
{
prop: "zgcsqd",
label: "整改措施清单",
type: "input",
placeholder: "请输入整改措施清单",
},
{
prop: "fjcl",
label: "附件材料",
type: "upload",
placeholder: "",
},
]);
// 专项检查默认行数据
const zxjcDefaultRow = ref({
zxjcfl: "",
jcjg: "",
zgcsqd: "",
fjcl: [],
});
// 表单数据 // 表单数据
const formData = reactive({ const formData = reactive({
...@@ -403,27 +317,4 @@ onMounted(() => { ...@@ -403,27 +317,4 @@ onMounted(() => {
<style scoped lang="less"> <style scoped lang="less">
@import "@/styles/verticalManages.less"; @import "@/styles/verticalManages.less";
.tab-handle {
margin-bottom: 10px;
text-align: left;
}
.subtotal {
background-color: #f5f7fa;
height: 40px;
display: flex;
.label {
width: 100px;
height: 40px;
text-align: center;
line-height: 40px;
border-right: 1px solid #ebeef5;
}
.value {
padding-left: 16px;
width: 100%;
line-height: 40px;
}
}
</style> </style>
<template>
<div class="manage-container">
<div class="manage-wrap">
<div class="manage-header">
<div class="header-left"></div>
<div class="header-right">
<el-button type="primary" @click="turnoverAdd">新增</el-button>
</div>
</div>
<div class="manage-content" v-loading="loading">
<common-table
:autoHeight="true"
:maxRows="10"
:data="tableData"
:columns="tableColumns"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:index="true"
:indexLabel="'序号'"
title=""
:border="true"
@size-change="handleSizeChange"
@current-page-change="handleCurrentPageChange"
>
<template #operations="{ row, index }">
<el-button
link
type="primary"
size="small"
@click="previewStatement(row)"
>查看</el-button
>
<el-button
link
type="primary"
size="small"
@click="editStatement(row)"
>编辑</el-button
>
<el-button
link
type="danger"
size="small"
@click="deleteStatement(row)"
>删除</el-button
>
</template>
</common-table>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, getCurrentInstance } from "vue";
import { useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import CommonTable from "@/components/common/commonTable.vue";
const router = useRouter();
const { proxy } = getCurrentInstance();
let tableData = ref([]);
let tableColumns = ref([
{
prop: "projectName",
label: "项目信息",
showOverflowTooltip: true,
},
{
prop: "zbyzyjLen",
label: "资本处置移交",
showOverflowTooltip: true,
},
{
prop: "qzlyzzyjLen",
label: "期中履约终止移交",
showOverflowTooltip: true,
},
{
prop: "qmyjLen",
label: "期满移交",
showOverflowTooltip: true,
},
{
prop: "operations",
label: "操作",
width: 170,
slot: "operations",
fixed: "right",
align: "center",
},
]);
let loading = ref(false);
let total = ref(0);
let currentPage = ref(1);
let pageSize = ref(10);
// 获取列表数据
const getStatementData = () => {
loading.value = true;
proxy.$post({
url: "/api/project/getYjglList",
data: {
page: currentPage.value,
pagesize: pageSize.value,
},
callback: (data) => {
const countValidRows = (arr) => {
if (!Array.isArray(arr)) return 0;
return arr.reduce((count, item) => {
if (Array.isArray(item?.fj) && item.fj.length > 0) {
count++;
}
return count;
}, 0);
};
tableData.value = data.rows.map((it) => {
return {
...it,
qmyjLen: `${countValidRows(it.qmyj)}行`,
qzlyzzyjLen: `${countValidRows(it.qzlyzzyj)}行`,
zbyzyjLen: `${countValidRows(it.zbyzyj)}行`,
};
});
total.value = data.count;
loading.value = false;
},
});
};
// 分页
const handleSizeChange = (size) => {
pageSize.value = size;
currentPage.value = 1;
getStatementData();
};
const handleCurrentPageChange = (page) => {
currentPage.value = page;
getStatementData();
};
const turnoverAdd = () => {
router.push("/turnoverAdd");
};
const editStatement = (item) => {
router.push({
name: "turnoverAdd",
query: {
id: item.id,
},
});
};
const previewStatement = (item) => {
router.push({
name: "turnoverAdd",
query: {
isPreview: true,
id: item.id,
},
});
};
const deleteStatement = (item) => {
ElMessageBox.confirm("确认删除该项?", "提示", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
proxy.$post({
url: "/api/project/deleteYjgl",
data: {
id: item.id,
},
callback: (data) => {
ElMessage.success("删除成功");
getStatementData();
},
});
})
.catch(() => {});
};
onMounted(() => {
getStatementData();
});
</script>
<style scoped lang="less">
@import "@/styles/manage.less";
</style>
<template>
<div class="add-project-container">
<div class="add-project-content" v-loading="loading">
<div class="add-project-header">
<div class="header-left"></div>
<div class="header-right">
<el-button type="default" @click="backClick">返回</el-button>
<template v-if="!loading && !isPreview">
<el-button type="primary" @click="saveClick">保存</el-button>
</template>
</div>
</div>
<div class="tabs-content">
<div class="project-tab-content">
<div class="tab-content">
<el-form :model="formData" :label-width="200" :disabled="isPreview">
<el-collapse v-model="activeCollapse">
<!-- 项目信息 -->
<el-collapse-item title="项目信息" name="项目信息">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目信息" required>
<el-select
v-model="formData.projectId"
placeholder="请选择项目信息"
no-data-text="暂无数据"
@change="changeProject"
>
<el-option
v-for="item in projectList"
:key="item.key"
:label="item.projectName"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-collapse-item>
<!-- 资本处置移交 -->
<el-collapse-item title="资本处置移交" name="资本处置移交">
<DynamicTable
:showAddButton="false"
v-model="zbyzyj"
:show-delete-button="false"
:columns="transferColumns"
:default-row="transferDefaultRow"
:disabled="isPreview"
/>
</el-collapse-item>
<!-- 期中履约终止移交 -->
<el-collapse-item
title="期中履约终止移交"
name="期中履约终止移交"
>
<DynamicTable
:showAddButton="false"
v-model="qzlyzzyj"
:show-delete-button="false"
:columns="transferColumns"
:default-row="transferDefaultRow"
:disabled="isPreview"
/>
</el-collapse-item>
<!-- 期满移交 -->
<el-collapse-item title="期满移交" name="期满移交">
<DynamicTable
:showAddButton="false"
v-model="qmyj"
:show-delete-button="false"
:columns="transferColumns"
:default-row="transferDefaultRow"
:disabled="isPreview"
/>
</el-collapse-item>
</el-collapse>
</el-form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onMounted, getCurrentInstance } from "vue";
import { useRouter, useRoute } from "vue-router";
import { ElMessage } from "element-plus";
import DynamicTable from "@/components/FormDynamicTable/index.vue";
// 路由与全局实例
const router = useRouter();
const route = useRoute();
const { proxy } = getCurrentInstance();
// 折叠面板默认展开项
const activeCollapse = ref([
"项目信息",
"资本处置移交",
"期中履约终止移交",
"期满移交",
]);
// 移交类表格列配置
const transferColumns = ref([
{ prop: "mc", label: "名称", type: "input", placeholder: "请填写文件名称" },
{ prop: "fj", label: "附件", type: "upload", placeholder: "" },
{
prop: "bz",
label: "备注",
type: "textarea",
rows: 2,
placeholder: "请输入备注信息",
},
]);
// 移交类默认行数据
const transferDefaultRow = ref({ mc: "", fj: [], bz: "" });
// 表单核心数据
const formData = reactive({
projectName: "",
projectId: "",
del: 0,
createdAt: "",
updatedAt: "",
});
// 状态管理
const loading = ref(false);
const isPreview = ref(!!route.query.isPreview);
const projectList = ref([]);
const rcCgqyglId = ref(route.query.id || "");
let options = ref();
// 初始化移交列表数据
const zbyzyj = ref([
{ mc: "股权转让协议", fj: [], bz: "" },
{ mc: "移交工作方案", fj: [], bz: "" },
{ mc: "资产移交清单", fj: [], bz: "" },
{ mc: "运营交接验收报告", fj: [], bz: "" },
]);
const qzlyzzyj = ref([
{ mc: "终止协议", fj: [], bz: "" },
{ mc: "移交工作方案", fj: [], bz: "" },
{ mc: "资产移交清单", fj: [], bz: "" },
{ mc: "运营交接验收报告", fj: [], bz: "" },
]);
const qmyj = ref([
{ mc: "移交工作方案", fj: [], bz: "" },
{ mc: "资产移交清单", fj: [], bz: "" },
{ mc: "运营交接验收报告", fj: [], bz: "" },
]);
// 可新增的期中履约终止移交列表
const yyqtzjcZxjcsList = ref([]);
// 获取项目列表数据
const getProjectData = () => {
proxy.$post({
url: "/api/project/listProject",
data: { page: 1, pagesize: 1000, attributes: [], menuType: "xmjc" },
callback: (data) => {
projectList.value = data.rows || [];
},
});
};
// 选择项目同步名称
const changeProject = (val) => {
const selectItem = projectList.value.find((item) => item.id === val);
if (selectItem) formData.projectName = selectItem.projectName;
};
// 获取单条记录详情(编辑/预览)
const getJsqtzjcDetail = () => {
if (!rcCgqyglId.value) return;
loading.value = true;
proxy.$post({
url: "/api/project/getYjglInfo",
data: { id: rcCgqyglId.value },
callback: (data) => {
loading.value = false;
Object.assign(formData, data);
zbyzyj.value = data.zbyzyj || zbyzyj.value;
qzlyzzyj.value = data.qzlyzzyj || qzlyzzyj.value;
qmyj.value = data.qmyj || qmyj.value;
yyqtzjcZxjcsList.value = data.yyqtzjcZxjcs || [];
},
});
};
// 返回上一页
const backClick = () => {
router.back(-1);
};
// 保存/提交表单
const saveClick = () => {
if (!formData.projectId) {
ElMessage.warning("请选择项目信息");
return;
}
loading.value = true;
const url = rcCgqyglId.value
? "/api/project/updateYjgl"
: "/api/project/createYjgl";
const submitData = {
...formData,
projectId: formData.projectId + "",
zbyzyj: zbyzyj.value,
qzlyzzyj: qzlyzzyj.value,
qmyj: qmyj.value,
yyqtzjcZxjcs: yyqtzjcZxjcsList.value,
};
proxy.$post({
url: url,
data: submitData,
callback: () => {
loading.value = false;
ElMessage.success(rcCgqyglId.value ? "编辑成功" : "新增成功");
router.back(-1);
},
});
};
// 页面初始化
onMounted(() => {
getProjectData();
options.value = JSON.parse(sessionStorage.getItem("resourceData"));
if (rcCgqyglId.value) getJsqtzjcDetail();
});
</script>
<style scoped lang="less">
@import "@/styles/verticalManages.less";
</style>
...@@ -92,8 +92,6 @@ const getStatementData = () => { ...@@ -92,8 +92,6 @@ const getStatementData = () => {
pagesize: pageSize.value, pagesize: pageSize.value,
}, },
callback: (data) => { callback: (data) => {
console.log(data, "data");
tableData.value = data.rows; tableData.value = data.rows;
total.value = data.count; total.value = data.count;
loading.value = false; loading.value = false;
......
import { defineConfig } from 'vite' import { defineConfig } from "vite";
import vue from '@vitejs/plugin-vue' import vue from "@vitejs/plugin-vue";
import path from 'path' import path from "path";
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [vue()], plugins: [vue()],
resolve: { resolve: {
alias: { alias: {
'@': path.resolve(__dirname, './src') "@": path.resolve(__dirname, "./src"),
} },
}, },
css: { css: {
preprocessorOptions: { preprocessorOptions: {
less: { less: {
javascriptEnabled: true, javascriptEnabled: true,
additionalData: '@import "./src/styles/utils.less";', additionalData: '@import "./src/styles/utils.less";',
} },
} },
}, },
}) });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment