Merge branch 'devlopment' of http://pms.dtyx.net:3000/wuxueyu/Industrial-image-management-system---web into devlopment
This commit is contained in:
commit
5f676536dd
|
@ -18,6 +18,8 @@ export interface ProjectResp {
|
|||
projectCategory?: string // 项目类型/服务
|
||||
projectManagerId?: string // 项目经理ID
|
||||
projectManagerName?: string // 项目经理姓名
|
||||
projectOrigin?: string // 项目来源
|
||||
|
||||
projectStaff?: string[] // 施工人员
|
||||
startDate?: string // 开始日期
|
||||
endDate?: string // 结束日期
|
||||
|
@ -28,10 +30,10 @@ export interface ProjectResp {
|
|||
coverUrl?: string // 封面URL
|
||||
createDt?: Date
|
||||
updateDt?: Date
|
||||
|
||||
|
||||
// 为了保持向后兼容,添加一些别名字段
|
||||
id?: string // projectId的别名
|
||||
fieldName?: string // farmName的别名
|
||||
fieldName?: string // farmName的别名
|
||||
fieldLocation?: string // farmAddress的别名
|
||||
commissionUnit?: string // client的别名
|
||||
commissionContact?: string // clientContact的别名
|
||||
|
@ -85,7 +87,7 @@ export interface TaskQuery {
|
|||
status?: string
|
||||
}
|
||||
|
||||
export interface TaskPageQuery extends TaskQuery, PageQuery {}
|
||||
export interface TaskPageQuery extends TaskQuery, PageQuery {}
|
||||
|
||||
// ==================== 人员调度相关类型 ====================
|
||||
|
||||
|
@ -339,4 +341,4 @@ export interface PageRes<T> {
|
|||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
}
|
|
@ -70,6 +70,6 @@ declare global {
|
|||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
|
|
|
@ -7,7 +7,70 @@ export {}
|
|||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ApprovalAssistant: typeof import('./../components/ApprovalAssistant/index.vue')['default']
|
||||
ApprovalMessageItem: typeof import('./../components/NotificationCenter/ApprovalMessageItem.vue')['default']
|
||||
Avatar: typeof import('./../components/Avatar/index.vue')['default']
|
||||
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
|
||||
CellCopy: typeof import('./../components/CellCopy/index.vue')['default']
|
||||
Chart: typeof import('./../components/Chart/index.vue')['default']
|
||||
CircularProgress: typeof import('./../components/CircularProgress/index.vue')['default']
|
||||
ColumnSetting: typeof import('./../components/GiTable/src/components/ColumnSetting.vue')['default']
|
||||
CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default']
|
||||
CronModal: typeof import('./../components/GenCron/CronModal/index.vue')['default']
|
||||
DateRangePicker: typeof import('./../components/DateRangePicker/index.vue')['default']
|
||||
DayForm: typeof import('./../components/GenCron/CronForm/component/day-form.vue')['default']
|
||||
FilePreview: typeof import('./../components/FilePreview/index.vue')['default']
|
||||
GiCellAvatar: typeof import('./../components/GiCell/GiCellAvatar.vue')['default']
|
||||
GiCellGender: typeof import('./../components/GiCell/GiCellGender.vue')['default']
|
||||
GiCellStatus: typeof import('./../components/GiCell/GiCellStatus.vue')['default']
|
||||
GiCellTag: typeof import('./../components/GiCell/GiCellTag.vue')['default']
|
||||
GiCellTags: typeof import('./../components/GiCell/GiCellTags.vue')['default']
|
||||
GiCodeView: typeof import('./../components/GiCodeView/index.vue')['default']
|
||||
GiDot: typeof import('./../components/GiDot/index.tsx')['default']
|
||||
GiEditTable: typeof import('./../components/GiEditTable/GiEditTable.vue')['default']
|
||||
GiFooter: typeof import('./../components/GiFooter/index.vue')['default']
|
||||
GiForm: typeof import('./../components/GiForm/src/GiForm.vue')['default']
|
||||
GiIconBox: typeof import('./../components/GiIconBox/index.vue')['default']
|
||||
GiIconSelector: typeof import('./../components/GiIconSelector/index.vue')['default']
|
||||
GiIframe: typeof import('./../components/GiIframe/index.vue')['default']
|
||||
GiOption: typeof import('./../components/GiOption/index.vue')['default']
|
||||
GiOptionItem: typeof import('./../components/GiOptionItem/index.vue')['default']
|
||||
GiPageLayout: typeof import('./../components/GiPageLayout/index.vue')['default']
|
||||
GiSpace: typeof import('./../components/GiSpace/index.vue')['default']
|
||||
GiSplitButton: typeof import('./../components/GiSplitButton/index.vue')['default']
|
||||
GiSplitPane: typeof import('./../components/GiSplitPane/index.vue')['default']
|
||||
GiSplitPaneFlexibleBox: typeof import('./../components/GiSplitPane/components/GiSplitPaneFlexibleBox.vue')['default']
|
||||
GiSvgIcon: typeof import('./../components/GiSvgIcon/index.vue')['default']
|
||||
GiTable: typeof import('./../components/GiTable/src/GiTable.vue')['default']
|
||||
GiTag: typeof import('./../components/GiTag/index.tsx')['default']
|
||||
GiThemeBtn: typeof import('./../components/GiThemeBtn/index.vue')['default']
|
||||
HourForm: typeof import('./../components/GenCron/CronForm/component/hour-form.vue')['default']
|
||||
Icon403: typeof import('./../components/icons/Icon403.vue')['default']
|
||||
Icon404: typeof import('./../components/icons/Icon404.vue')['default']
|
||||
Icon500: typeof import('./../components/icons/Icon500.vue')['default']
|
||||
IconBorders: typeof import('./../components/icons/IconBorders.vue')['default']
|
||||
IconTableSize: typeof import('./../components/icons/IconTableSize.vue')['default']
|
||||
IconTreeAdd: typeof import('./../components/icons/IconTreeAdd.vue')['default']
|
||||
IconTreeReduce: typeof import('./../components/icons/IconTreeReduce.vue')['default']
|
||||
ImageImport: typeof import('./../components/ImageImport/index.vue')['default']
|
||||
ImageImportWizard: typeof import('./../components/ImageImportWizard/index.vue')['default']
|
||||
IndustrialImageList: typeof import('./../components/IndustrialImageList/index.vue')['default']
|
||||
JsonPretty: typeof import('./../components/JsonPretty/index.vue')['default']
|
||||
MinuteForm: typeof import('./../components/GenCron/CronForm/component/minute-form.vue')['default']
|
||||
MonthForm: typeof import('./../components/GenCron/CronForm/component/month-form.vue')['default']
|
||||
NotificationCenter: typeof import('./../components/NotificationCenter/index.vue')['default']
|
||||
ParentView: typeof import('./../components/ParentView/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SecondForm: typeof import('./../components/GenCron/CronForm/component/second-form.vue')['default']
|
||||
SplitPanel: typeof import('./../components/SplitPanel/index.vue')['default']
|
||||
TextCopy: typeof import('./../components/TextCopy/index.vue')['default']
|
||||
TurbineGrid: typeof import('./../components/TurbineGrid/index.vue')['default']
|
||||
UserSelect: typeof import('./../components/UserSelect/index.vue')['default']
|
||||
Verify: typeof import('./../components/Verify/index.vue')['default']
|
||||
VerifyPoints: typeof import('./../components/Verify/Verify/VerifyPoints.vue')['default']
|
||||
VerifySlide: typeof import('./../components/Verify/Verify/VerifySlide.vue')['default']
|
||||
WeekForm: typeof import('./../components/GenCron/CronForm/component/week-form.vue')['default']
|
||||
YearForm: typeof import('./../components/GenCron/CronForm/component/year-form.vue')['default']
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<a-layout class="knowledge-container">
|
||||
<!-- 侧边栏 -->
|
||||
<a-layout-sider
|
||||
width="260"
|
||||
:width="sidebarWidth"
|
||||
:collapsed-width="80"
|
||||
theme="dark"
|
||||
class="folder-sidebar"
|
||||
|
@ -101,7 +101,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 拖拽分隔线 -->
|
||||
<div
|
||||
v-if="!sidebarCollapsed"
|
||||
class="sidebar-resizer"
|
||||
@mousedown="startResize"
|
||||
@touchstart="startResize"
|
||||
:title="`当前宽度: ${sidebarWidth}px (拖拽调整)`"
|
||||
>
|
||||
<div class="resizer-handle"></div>
|
||||
</div>
|
||||
</a-layout-sider>
|
||||
|
||||
<a-layout>
|
||||
|
@ -143,19 +152,22 @@
|
|||
|
||||
<a-layout-content class="file-content">
|
||||
<a-card :bordered="false" class="file-card">
|
||||
<a-descriptions :title="`文件列表 (${fileList.length})`" v-if="currentFolderId" />
|
||||
|
||||
<!-- 文件搜索功能 -->
|
||||
<div v-if="currentFolderId" class="file-search-container">
|
||||
<a-input-search
|
||||
v-model="fileSearchKeyword"
|
||||
placeholder="搜索文件名..."
|
||||
class="file-search-input"
|
||||
@search="handleFileSearch"
|
||||
@input="handleFileSearchInput"
|
||||
@clear="handleFileSearchClear"
|
||||
allow-clear
|
||||
/>
|
||||
<!-- 文件列表标题和搜索框在同一行 -->
|
||||
<div v-if="currentFolderId" class="file-header-container">
|
||||
<div class="file-title">
|
||||
<span class="file-list-title">文件列表 ({{ fileList.length }})</span>
|
||||
</div>
|
||||
<div class="file-search-container">
|
||||
<a-input-search
|
||||
v-model="fileSearchKeyword"
|
||||
placeholder="搜索文件名..."
|
||||
class="file-search-input"
|
||||
@search="handleFileSearch"
|
||||
@input="handleFileSearchInput"
|
||||
@clear="handleFileSearchClear"
|
||||
allow-clear
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-divider size="small" v-if="currentFolderId" />
|
||||
|
@ -336,7 +348,7 @@
|
|||
</div>
|
||||
|
||||
<!-- 文件分页 -->
|
||||
<div v-if="currentFolderId && !loading && totalFiles > 0" class="file-pagination">
|
||||
<div v-if="currentFolderId && !loading && totalFiles > 0" class="pagination-container">
|
||||
<a-pagination
|
||||
:total="totalFiles"
|
||||
:current="fileCurrentPage"
|
||||
|
@ -345,6 +357,8 @@
|
|||
:show-page-size="true"
|
||||
:page-size-options="[10, 20, 50, 100]"
|
||||
:show-jumper="true"
|
||||
:hide-on-single-page="false"
|
||||
size="default"
|
||||
@change="handleFilePageChange"
|
||||
@page-size-change="handleFilePageSizeChange"
|
||||
/>
|
||||
|
@ -2357,6 +2371,86 @@ const fileTypeText = (type) => {
|
|||
// 侧边栏控制
|
||||
const sidebarCollapsed = ref(false);
|
||||
|
||||
// 侧边栏宽度控制
|
||||
const sidebarWidth = ref(260); // 默认宽度
|
||||
const isResizing = ref(false);
|
||||
const startX = ref(0);
|
||||
const startWidth = ref(0);
|
||||
|
||||
// 从localStorage加载保存的宽度
|
||||
const loadSavedWidth = () => {
|
||||
try {
|
||||
const savedWidth = localStorage.getItem('bussiness-sidebar-width');
|
||||
if (savedWidth) {
|
||||
const width = parseInt(savedWidth);
|
||||
if (width >= 200 && width <= 500) {
|
||||
sidebarWidth.value = width;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('加载侧边栏宽度失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 保存宽度到localStorage
|
||||
const saveWidth = (width) => {
|
||||
try {
|
||||
localStorage.setItem('bussiness-sidebar-width', width.toString());
|
||||
} catch (error) {
|
||||
console.warn('保存侧边栏宽度失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 开始拖拽调整大小
|
||||
const startResize = (event) => {
|
||||
event.preventDefault();
|
||||
isResizing.value = true;
|
||||
startX.value = event.type === 'mousedown' ? event.clientX : event.touches[0].clientX;
|
||||
startWidth.value = sidebarWidth.value;
|
||||
|
||||
// 添加事件监听器
|
||||
if (event.type === 'mousedown') {
|
||||
document.addEventListener('mousemove', handleResize);
|
||||
document.addEventListener('mouseup', stopResize);
|
||||
} else {
|
||||
document.addEventListener('touchmove', handleResize);
|
||||
document.addEventListener('touchend', stopResize);
|
||||
}
|
||||
|
||||
// 添加样式
|
||||
document.body.classList.add('resizing');
|
||||
};
|
||||
|
||||
// 处理拖拽调整
|
||||
const handleResize = (event) => {
|
||||
if (!isResizing.value) return;
|
||||
|
||||
const currentX = event.type === 'mousemove' ? event.clientX : event.touches[0].clientX;
|
||||
const deltaX = currentX - startX.value;
|
||||
const newWidth = Math.max(200, Math.min(500, startWidth.value + deltaX));
|
||||
|
||||
sidebarWidth.value = newWidth;
|
||||
};
|
||||
|
||||
// 停止拖拽调整
|
||||
const stopResize = () => {
|
||||
isResizing.value = false;
|
||||
|
||||
// 移除事件监听器
|
||||
document.removeEventListener('mousemove', handleResize);
|
||||
document.removeEventListener('mouseup', stopResize);
|
||||
document.removeEventListener('touchmove', handleResize);
|
||||
document.removeEventListener('touchend', stopResize);
|
||||
|
||||
// 移除样式
|
||||
document.body.classList.remove('resizing');
|
||||
|
||||
// 保存宽度
|
||||
saveWidth(sidebarWidth.value);
|
||||
};
|
||||
|
||||
// 打开新建文件夹对话框
|
||||
const handleCreateFolder = () => {
|
||||
folderForm.id = '';
|
||||
|
@ -2443,6 +2537,7 @@ watch(uploadDialogVisible, (visible) => {
|
|||
|
||||
// 初始化加载
|
||||
onMounted(() => {
|
||||
loadSavedWidth(); // 加载保存的侧边栏宽度
|
||||
initData();
|
||||
});
|
||||
</script>
|
||||
|
@ -2629,6 +2724,7 @@ onMounted(() => {
|
|||
background: var(--color-bg-1);
|
||||
min-height: 0;
|
||||
max-height: calc(100vh - 120px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-card {
|
||||
|
@ -2640,6 +2736,7 @@ onMounted(() => {
|
|||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding-bottom: 80px; /* 为分页器留出空间 */
|
||||
}
|
||||
|
||||
/* 表格容器 */
|
||||
|
@ -2654,7 +2751,7 @@ onMounted(() => {
|
|||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 0;
|
||||
min-height: 300px;
|
||||
max-height: calc(100vh - 300px);
|
||||
max-height: calc(100vh - 380px); /* 调整高度为分页器留出空间 */
|
||||
}
|
||||
|
||||
/* 表头行样式 */
|
||||
|
@ -3100,14 +3197,65 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
/* 分页样式 */
|
||||
.pagination-container, .file-pagination {
|
||||
margin-top: 16px;
|
||||
text-align: right;
|
||||
padding: 0 16px 16px;
|
||||
}
|
||||
|
||||
.file-pagination {
|
||||
.pagination-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--color-bg-1);
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
|
||||
margin-top: 0;
|
||||
|
||||
.arco-pagination {
|
||||
margin: 0;
|
||||
|
||||
.arco-pagination-item {
|
||||
border-radius: 6px;
|
||||
margin: 0 4px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
&.arco-pagination-item-active {
|
||||
background: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.arco-pagination-prev,
|
||||
.arco-pagination-next {
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.arco-pagination-size-changer {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.arco-pagination-jumper {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.arco-pagination-total {
|
||||
color: var(--color-text-2);
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -3530,9 +3678,29 @@ onMounted(() => {
|
|||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
/* 文件头部容器样式 */
|
||||
.file-header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.file-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-list-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-1);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 文件搜索样式 */
|
||||
.file-search-container {
|
||||
margin: 16px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
@ -3601,55 +3769,7 @@ onMounted(() => {
|
|||
border-top-color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* 文件分页样式 */
|
||||
.file-pagination {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-top: 16px;
|
||||
padding: 16px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
border-top: 1px solid var(--color-border);
|
||||
background: var(--color-bg-1);
|
||||
flex-shrink: 0;
|
||||
z-index: 10;
|
||||
|
||||
.arco-pagination {
|
||||
.arco-pagination-total {
|
||||
color: var(--color-text-2);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.arco-pagination-item {
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
&.arco-pagination-item-active {
|
||||
background: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.arco-pagination-prev,
|
||||
.arco-pagination-next {
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 树形文件夹结构 */
|
||||
.folder-tree-container {
|
||||
|
@ -3756,4 +3876,52 @@ onMounted(() => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 拖拽分隔线样式 */
|
||||
.sidebar-resizer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 6px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
background: transparent;
|
||||
transition: background-color 0.2s ease;
|
||||
z-index: 10;
|
||||
|
||||
&:hover {
|
||||
background: rgba(var(--color-primary-6), 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba(var(--color-primary-6), 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.resizer-handle {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 2px;
|
||||
height: 40px;
|
||||
background: var(--color-primary);
|
||||
border-radius: 1px;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-resizer:hover .resizer-handle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 拖拽时的全局样式 */
|
||||
body.resizing {
|
||||
cursor: col-resize !important;
|
||||
user-select: none !important;
|
||||
}
|
||||
|
||||
body.resizing * {
|
||||
cursor: col-resize !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,19 +2,21 @@
|
|||
项目管理页面
|
||||
已完成接口对接:
|
||||
1. 项目列表查询 (listProject) - 支持分页和条件查询
|
||||
2. 项目新增 (addProject)
|
||||
2. 项目新增 (addProject)
|
||||
3. 项目修改 (updateProject)
|
||||
4. 项目删除 (deleteProject)
|
||||
5. 项目导出 (exportProject)
|
||||
6. 项目导入 (importProject)
|
||||
|
||||
|
||||
所有API调用都已添加错误处理和类型安全检查
|
||||
-->
|
||||
<template>
|
||||
<GiPageLayout>
|
||||
<GiTable row-key="id" :data="dataList" :columns="tableColumns" :loading="loading"
|
||||
<GiTable
|
||||
row-key="id" :data="dataList" :columns="tableColumns" :loading="loading"
|
||||
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }" :pagination="pagination" :disabled-tools="['size']"
|
||||
@page-change="onPageChange" @page-size-change="onPageSizeChange" @refresh="search">
|
||||
@page-change="onPageChange" @page-size-change="onPageSizeChange" @refresh="search"
|
||||
>
|
||||
<template #top>
|
||||
<GiForm v-model="searchForm" search :columns="queryFormColumns" size="medium" @search="search" @reset="reset">
|
||||
</GiForm>
|
||||
|
@ -71,10 +73,14 @@
|
|||
</GiTable>
|
||||
|
||||
<!-- 新增/编辑项目弹窗 -->
|
||||
<a-modal v-model:visible="addModalVisible" :title="modalTitle" @cancel="resetForm"
|
||||
:ok-button-props="{ loading: submitLoading }" @ok="handleSubmit" width="800px" modal-class="project-form-modal">
|
||||
<a-form ref="formRef" :model="form" :rules="formRules" layout="vertical"
|
||||
:style="{ maxHeight: '70vh', overflow: 'auto', padding: '0 10px' }">
|
||||
<a-modal
|
||||
v-model:visible="addModalVisible" :title="modalTitle" :ok-button-props="{ loading: submitLoading }"
|
||||
width="800px" modal-class="project-form-modal" @cancel="resetForm" @ok="handleSubmit"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef" :model="form" :rules="formRules" layout="vertical"
|
||||
:style="{ maxHeight: '70vh', overflow: 'auto', padding: '0 10px' }"
|
||||
>
|
||||
<!-- 基本信息 -->
|
||||
<a-divider orientation="left">基本信息</a-divider>
|
||||
<a-row :gutter="16">
|
||||
|
@ -88,10 +94,12 @@
|
|||
<a-input v-model="form.farmAddress" placeholder="请输入地址" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col><a-button size="mini" @click="() => { Message.info(`待开发`) }">
|
||||
<a-col>
|
||||
<a-button size="mini" @click="() => { Message.info(`待开发`) }">
|
||||
<template #icon><icon-location /></template>
|
||||
地图选点
|
||||
</a-button></a-col>
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
|
@ -108,7 +116,7 @@
|
|||
<a-col :span="12">
|
||||
<a-form-item field="inspectionUnit" label="业主">
|
||||
<a-input v-model="form.inspectionUnit" placeholder="请输入业主单位" @input="(val) => (form.farmName = val)" />
|
||||
<!--风场名称同步业主 -->
|
||||
<!-- 风场名称同步业主 -->
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
|
@ -145,10 +153,133 @@
|
|||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="16">
|
||||
<a-form-item field="projectContent" label="项目内容">
|
||||
<a-textarea v-model="form.coverUrl" placeholder="请输入项目内容" :rows="4" />
|
||||
</a-form-item>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="projectOrigin" label="项目来源" :rules="[{ required: true, message: '请输入项目来源' }]">
|
||||
<a-input v-model="form.projectOrigin" placeholder="请输入项目来源" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-divider orientation="left">任务设置</a-divider>
|
||||
<div class="mb-2">
|
||||
<a-button type="dashed" size="small" @click="addTask">
|
||||
<template #icon><icon-plus /></template>
|
||||
新增任务
|
||||
</a-button>
|
||||
</div>
|
||||
<div v-if="form.tasks.length === 0" class="text-gray-500 mb-2">暂无任务,请点击“新增任务”。</div>
|
||||
<a-space direction="vertical" fill>
|
||||
<a-card v-for="(task, tIndex) in form.tasks" :key="tIndex" size="small">
|
||||
<template #title>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>任务 {{ tIndex + 1 }}</span>
|
||||
<a-space>
|
||||
<a-button size="mini" @click="addSubtask(tIndex)">新增子任务</a-button>
|
||||
<a-button size="mini" status="danger" @click="removeTask(tIndex)">删除</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="8">
|
||||
<a-form-item :field="`tasks.${tIndex}.taskName`" label="任务名称" required>
|
||||
<a-input v-model="task.taskName" placeholder="请输入任务名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item :field="`tasks.${tIndex}.taskCode`" label="任务编号">
|
||||
<a-input v-model="task.taskCode" placeholder="编号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item :field="`tasks.${tIndex}.mainUserId`" label="负责人">
|
||||
<a-select v-model="task.mainUserId" placeholder="选择负责人" :loading="userLoading">
|
||||
<a-option v-for="u in userOptions" :key="u.value" :value="u.value">{{ u.label }}</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<a-form-item :field="`tasks.${tIndex}.scales`" label="工量">
|
||||
<a-input-number v-model="task.scales" :min="0" :max="9999" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="8">
|
||||
<a-form-item :field="`tasks.${tIndex}.planStartDate`" label="计划开始">
|
||||
<a-date-picker v-model="task.planStartDate" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item :field="`tasks.${tIndex}.planEndDate`" label="计划结束">
|
||||
<a-date-picker v-model="task.planEndDate" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item :field="`tasks.${tIndex}.taskGroupId`" label="任务组">
|
||||
<a-input-number v-model="task.taskGroupId" :min="0" placeholder="可选" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 子任务 -->
|
||||
<div v-if="task.children && task.children.length">
|
||||
<a-divider orientation="left">子任务</a-divider>
|
||||
<a-card
|
||||
v-for="(sub, sIndex) in task.children"
|
||||
:key="sIndex"
|
||||
size="small"
|
||||
class="mb-2"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>子任务 {{ tIndex + 1 }}-{{ sIndex + 1 }}</span>
|
||||
<a-button size="mini" status="danger" @click="removeSubtask(tIndex, sIndex)">删除</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="8">
|
||||
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.taskName`" label="任务名称" required>
|
||||
<a-input v-model="sub.taskName" placeholder="请输入任务名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.taskCode`" label="任务编号">
|
||||
<a-input v-model="sub.taskCode" placeholder="编号" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.mainUserId`" label="负责人">
|
||||
<a-select v-model="sub.mainUserId" placeholder="选择负责人" :loading="userLoading">
|
||||
<a-option v-for="u in userOptions" :key="u.value" :value="u.value">{{ u.label }}</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.scales`" label="工量">
|
||||
<a-input-number v-model="sub.scales" :min="0" :max="9999" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="8">
|
||||
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.planStartDate`" label="计划开始">
|
||||
<a-date-picker v-model="sub.planStartDate" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.planEndDate`" label="计划结束">
|
||||
<a-date-picker v-model="sub.planEndDate" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item :field="`tasks.${tIndex}.children.${sIndex}.taskGroupId`" label="任务组">
|
||||
<a-input-number v-model="sub.taskGroupId" :min="0" placeholder="可选" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-space>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="status" label="项目状态">
|
||||
|
@ -198,7 +329,6 @@
|
|||
</a-col>
|
||||
</a-row>
|
||||
<a-divider orientation="middle">地图</a-divider>
|
||||
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
|
@ -221,53 +351,53 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import { addProject, deleteProject, listProject, updateProject, exportProject, importProject } from '@/apis/project'
|
||||
import type { TableColumnData } from '@arco-design/web-vue'
|
||||
import TurbineGrid from './TurbineGrid.vue'
|
||||
import { addProject, deleteProject, exportProject, importProject, listProject, updateProject } from '@/apis/project'
|
||||
import { isMobile } from '@/utils'
|
||||
import has from '@/utils/has'
|
||||
import http from '@/utils/http'
|
||||
import type { ColumnItem } from '@/components/GiForm'
|
||||
import type { TableColumnData } from '@arco-design/web-vue'
|
||||
import type { ProjectResp, ProjectPageQuery } from '@/apis/project/type'
|
||||
import * as T from '@/apis/project/type'
|
||||
import TurbineGrid from './TurbineGrid.vue'
|
||||
import type { ProjectPageQuery } from '@/apis/project/type'
|
||||
import type * as T from '@/apis/project/type'
|
||||
|
||||
defineOptions({ name: 'ProjectManagement' })
|
||||
|
||||
// 项目状态常量定义 (API返回数字类型)
|
||||
const PROJECT_STATUS = {
|
||||
NOT_STARTED: 0, // 未开始/待施工
|
||||
IN_PROGRESS: 1, // 施工中
|
||||
COMPLETED: 2, // 已完成
|
||||
NOT_STARTED: 0, // 未开始/待施工
|
||||
IN_PROGRESS: 1, // 施工中
|
||||
COMPLETED: 2, // 已完成
|
||||
} as const
|
||||
|
||||
// 项目状态映射
|
||||
const PROJECT_STATUS_MAP = {
|
||||
0: '待施工',
|
||||
1: '施工中',
|
||||
2: '已完成'
|
||||
2: '已完成',
|
||||
} as const
|
||||
|
||||
// 项目状态选项
|
||||
const PROJECT_STATUS_OPTIONS = [
|
||||
{ label: '待施工', value: 0 },
|
||||
{ label: '施工中', value: 1 },
|
||||
{ label: '已完成', value: 2 }
|
||||
{ label: '已完成', value: 2 },
|
||||
]
|
||||
|
||||
// 项目类别常量定义
|
||||
const PROJECT_CATEGORY = {
|
||||
EXTERNAL_WORK: '外部工作',
|
||||
INTERNAL_PROJECT: '内部项目',
|
||||
TECHNICAL_SERVICE: '技术服务'
|
||||
TECHNICAL_SERVICE: '技术服务',
|
||||
} as const
|
||||
|
||||
// 项目类别选项
|
||||
const PROJECT_CATEGORY_OPTIONS = [
|
||||
{ label: PROJECT_CATEGORY.EXTERNAL_WORK, value: PROJECT_CATEGORY.EXTERNAL_WORK },
|
||||
{ label: PROJECT_CATEGORY.INTERNAL_PROJECT, value: PROJECT_CATEGORY.INTERNAL_PROJECT },
|
||||
{ label: PROJECT_CATEGORY.TECHNICAL_SERVICE, value: PROJECT_CATEGORY.TECHNICAL_SERVICE }
|
||||
{ label: PROJECT_CATEGORY.TECHNICAL_SERVICE, value: PROJECT_CATEGORY.TECHNICAL_SERVICE },
|
||||
]
|
||||
|
||||
const router = useRouter()
|
||||
|
@ -280,12 +410,12 @@ const currentId = ref<string | null>(null)
|
|||
const fileList = ref([])
|
||||
const dataList = ref<T.ProjectResp[]>([])
|
||||
const userLoading = ref(false)
|
||||
const userOptions = ref<{ label: string; value: string }[]>([])
|
||||
const userOptions = ref<{ label: string, value: string }[]>([])
|
||||
|
||||
let searchForm = reactive<Partial<ProjectPageQuery>>({
|
||||
const searchForm = reactive<Partial<ProjectPageQuery>>({
|
||||
projectName: '',
|
||||
status: undefined,
|
||||
fieldName: '', // 保持使用fieldName,因为API类型定义中使用的是这个字段名
|
||||
fieldName: '', // 保持使用fieldName,因为API类型定义中使用的是这个字段名
|
||||
})
|
||||
|
||||
const queryFormColumns: ColumnItem[] = reactive([
|
||||
|
@ -320,28 +450,48 @@ const queryFormColumns: ColumnItem[] = reactive([
|
|||
])
|
||||
|
||||
const form = reactive({
|
||||
projectId: '', // 项目id
|
||||
projectName: '', // 项目名称
|
||||
projectManagerId: '', // 项目经理id
|
||||
client: '', // 委托单位
|
||||
clientContact: '', // 委托单位联系人
|
||||
clientPhone: '', // 委托单位联系电话
|
||||
inspectionUnit: '', // 业主单位
|
||||
inspectionContact: '', // 业主单位联系人
|
||||
inspectionPhone: '', // 业主单位联系电话
|
||||
farmName: '', // 风场名称 现在等同业主单位
|
||||
farmAddress: '', // 风场地址 项目地址
|
||||
scale: '', // 项目规模 风机数量
|
||||
turbineModel: '', // 风机型号
|
||||
status: '', // 状态:0待施工,1施工中,2已完工,3已审核,4已验收
|
||||
startDate: '', // 开始时间
|
||||
endDate: '', // 结束时间
|
||||
coverUrl: '', // 项目封面 现在填的是项目内容
|
||||
projectId: '', // 项目id
|
||||
projectName: '', // 项目名称
|
||||
projectManagerId: '', // 项目经理id
|
||||
client: '', // 委托单位
|
||||
clientContact: '', // 委托单位联系人
|
||||
clientPhone: '', // 委托单位联系电话
|
||||
inspectionUnit: '', // 业主单位
|
||||
inspectionContact: '', // 业主单位联系人
|
||||
inspectionPhone: '', // 业主单位联系电话
|
||||
farmName: '', // 风场名称 现在等同业主单位
|
||||
farmAddress: '', // 风场地址 项目地址
|
||||
projectOrigin: '', // 项目来源
|
||||
scale: '', // 项目规模 风机数量
|
||||
turbineModel: '', // 风机型号
|
||||
status: '', // 状态:0待施工,1施工中,2已完工,3已审核,4已验收
|
||||
startDate: '', // 开始时间
|
||||
endDate: '', // 结束时间
|
||||
// coverUrl: '', // 项目封面 现在改为任务,不再使用
|
||||
constructionTeamLeaderId: '', // 施工组长id
|
||||
constructorIds: '', // 施工人员id
|
||||
qualityOfficerId: '', // 质量员id
|
||||
auditorId: '', // 安全员id
|
||||
turbineList: [] as { id: number; turbineNo: string; lat?: number; lng?: number; status: 0 | 1 | 2 }[], //风机组
|
||||
constructorIds: '', // 施工人员id
|
||||
qualityOfficerId: '', // 质量员id
|
||||
auditorId: '', // 安全员id
|
||||
// 任务集合(支持子任务)
|
||||
tasks: [] as Array<{
|
||||
taskName: string
|
||||
taskCode?: string
|
||||
mainUserId?: string | number
|
||||
planStartDate?: string
|
||||
planEndDate?: string
|
||||
scales?: number
|
||||
taskGroupId?: number | string
|
||||
children?: Array<{
|
||||
taskName: string
|
||||
taskCode?: string
|
||||
mainUserId?: string | number
|
||||
planStartDate?: string
|
||||
planEndDate?: string
|
||||
scales?: number
|
||||
taskGroupId?: number | string
|
||||
}>
|
||||
}>,
|
||||
turbineList: [] as { id: number, turbineNo: string, lat?: number, lng?: number, status: 0 | 1 | 2 }[], // 风机组
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
|
@ -350,7 +500,7 @@ const pagination = reactive({
|
|||
total: 0,
|
||||
showTotal: true,
|
||||
showJumper: true,
|
||||
showPageSize: true
|
||||
showPageSize: true,
|
||||
})
|
||||
const openMapModal = (item: any) => {
|
||||
Message.info(`地图选点功能待开发,当前机组编号:${item.turbineNo}`)
|
||||
|
@ -384,70 +534,70 @@ const tableColumns = ref<TableColumnData[]>([
|
|||
slotName: 'fieldInfo',
|
||||
minWidth: 180,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
align: 'center',
|
||||
width: 100
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '委托单位',
|
||||
dataIndex: 'commissionUnit',
|
||||
minWidth: 140,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '委托单位联系人/电话',
|
||||
slotName: 'commissionInfo',
|
||||
minWidth: 160,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '业主',
|
||||
dataIndex: 'inspectionUnit',
|
||||
minWidth: 140,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '业主联系人/电话',
|
||||
slotName: 'inspectionInfo',
|
||||
minWidth: 160,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '项目规模',
|
||||
dataIndex: 'projectScale',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '机组型号',
|
||||
dataIndex: 'orgNumber',
|
||||
width: 100,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '项目经理/施工人员',
|
||||
slotName: 'projectManager',
|
||||
minWidth: 160,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '项目周期',
|
||||
slotName: 'projectPeriod',
|
||||
minWidth: 180,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
|
@ -492,7 +642,7 @@ const fetchData = async () => {
|
|||
const params: ProjectPageQuery = {
|
||||
...searchForm,
|
||||
page: pagination.current,
|
||||
size: pagination.pageSize
|
||||
size: pagination.pageSize,
|
||||
}
|
||||
|
||||
const res = await listProject(params)
|
||||
|
@ -516,9 +666,9 @@ const fetchData = async () => {
|
|||
projectManager: item.projectManagerName,
|
||||
projectScale: item.scale,
|
||||
// 处理项目周期
|
||||
projectPeriod: item.startDate && item.endDate ? [item.startDate, item.endDate] : []
|
||||
};
|
||||
return mappedItem;
|
||||
projectPeriod: item.startDate && item.endDate ? [item.startDate, item.endDate] : [],
|
||||
}
|
||||
return mappedItem
|
||||
})
|
||||
|
||||
// 由于API没有返回total,使用当前数据长度
|
||||
|
@ -553,7 +703,7 @@ const reset = () => {
|
|||
// 重置搜索表单
|
||||
Object.assign(searchForm, {
|
||||
projectName: '',
|
||||
fieldName: '', // 保持使用fieldName,因为API类型定义中使用的是这个字段名
|
||||
fieldName: '', // 保持使用fieldName,因为API类型定义中使用的是这个字段名
|
||||
status: undefined,
|
||||
})
|
||||
|
||||
|
@ -576,27 +726,29 @@ const onPageSizeChange = (pageSize: number) => {
|
|||
const resetForm = () => {
|
||||
// 重置表单字段
|
||||
Object.assign(form, {
|
||||
projectId: '', // 项目id
|
||||
projectName: '', // 项目名称
|
||||
projectManagerId: '', // 项目经理id
|
||||
client: '', // 委托单位
|
||||
clientContact: '', // 委托单位联系人
|
||||
clientPhone: '', // 委托单位联系电话
|
||||
inspectionUnit: '', // 检查单位
|
||||
inspectionContact: '', // 检查单位联系人
|
||||
inspectionPhone: '', // 检查单位联系电话
|
||||
farmName: '', // 风场名称
|
||||
farmAddress: '', // 风场地址
|
||||
scale: '', // 项目规模
|
||||
turbineModel: '', // 风机型号
|
||||
status: 0, // 状态:0待施工,1施工中,2已完工,3已审核,4已验收
|
||||
startDate: '', // 开始时间
|
||||
endDate: '', // 结束时间
|
||||
coverUrl: '', // 项目封面
|
||||
projectId: '', // 项目id
|
||||
projectName: '', // 项目名称
|
||||
projectManagerId: '', // 项目经理id
|
||||
client: '', // 委托单位
|
||||
clientContact: '', // 委托单位联系人
|
||||
clientPhone: '', // 委托单位联系电话
|
||||
inspectionUnit: '', // 检查单位
|
||||
inspectionContact: '', // 检查单位联系人
|
||||
inspectionPhone: '', // 检查单位联系电话
|
||||
farmName: '', // 风场名称
|
||||
farmAddress: '', // 风场地址
|
||||
projectOrigin: '', // 项目来源(必填)
|
||||
scale: '', // 项目规模
|
||||
turbineModel: '', // 风机型号
|
||||
status: 0, // 状态:0待施工,1施工中,2已完工,3已审核,4已验收
|
||||
startDate: '', // 开始时间
|
||||
endDate: '', // 结束时间
|
||||
// coverUrl: '', // 项目封面(已废弃)
|
||||
constructionTeamLeaderId: '', // 施工组长id
|
||||
constructorIds: '', // 施工人员id
|
||||
qualityOfficerId: '', // 质量员id
|
||||
auditorId: '' // 安全员id
|
||||
constructorIds: '', // 施工人员id
|
||||
qualityOfficerId: '', // 质量员id
|
||||
auditorId: '', // 安全员id
|
||||
tasks: [],
|
||||
})
|
||||
|
||||
isEdit.value = false
|
||||
|
@ -607,21 +759,35 @@ const openAddModal = () => {
|
|||
resetForm()
|
||||
addModalVisible.value = true
|
||||
}
|
||||
// 任务增删改(仅在新增/编辑弹窗内部使用)
|
||||
const addTask = () => {
|
||||
;(form.tasks as any[]).push({ taskName: '', taskCode: '', mainUserId: undefined, planStartDate: '', planEndDate: '', scales: undefined, taskGroupId: undefined, children: [] })
|
||||
}
|
||||
const removeTask = (index: number) => {
|
||||
;(form.tasks as any[]).splice(index, 1)
|
||||
}
|
||||
const addSubtask = (parentIndex: number) => {
|
||||
const list = (form.tasks as any[])
|
||||
if (!list[parentIndex].children) list[parentIndex].children = []
|
||||
list[parentIndex].children!.push({ taskName: '', taskCode: '', mainUserId: undefined, planStartDate: '', planEndDate: '', scales: undefined, taskGroupId: undefined })
|
||||
}
|
||||
const removeSubtask = (parentIndex: number, index: number) => {
|
||||
const list = (form.tasks as any[])
|
||||
list[parentIndex].children!.splice(index, 1)
|
||||
}
|
||||
|
||||
|
||||
const openEditModal = (record: T.ProjectResp) => {
|
||||
isEdit.value = true
|
||||
currentId.value = record.id || record.projectId || null
|
||||
|
||||
// 重置表单
|
||||
Object.keys(form).forEach(key => {
|
||||
// @ts-ignore
|
||||
form[key] = ''
|
||||
})
|
||||
// 重置表单为默认(确保 tasks、turbineList 等为数组初始值)
|
||||
resetForm()
|
||||
|
||||
// 填充表单数据
|
||||
Object.keys(form).forEach(key => {
|
||||
Object.keys(form).forEach((key) => {
|
||||
if (key in record && record[key as keyof T.ProjectResp] !== undefined) {
|
||||
// @ts-ignore - 这里需要处理类型转换
|
||||
// @ts-expect-error - 这里需要处理类型转换
|
||||
form[key] = record[key as keyof T.ProjectResp]
|
||||
}
|
||||
})
|
||||
|
@ -645,6 +811,7 @@ const openEditModal = (record: T.ProjectResp) => {
|
|||
// 添加表单验证规则
|
||||
const formRules = {
|
||||
projectName: [{ required: true, message: '请输入项目名称' }],
|
||||
projectOrigin: [{ required: true, message: '请输入项目来源' }],
|
||||
}
|
||||
|
||||
// 添加提交加载状态
|
||||
|
@ -660,15 +827,32 @@ const handleSubmit = async () => {
|
|||
await formRef.value.validate()
|
||||
|
||||
// 准备提交的数据
|
||||
const normalizeDate = (d: any) => (d ? (typeof d === 'string' ? d : new Date(d).toISOString().split('T')[0]) : '')
|
||||
|
||||
// 任务结构转换(只转换日期,其余保持)
|
||||
const mapTasks = (tasks: any[]) =>
|
||||
(tasks || []).map(t => ({
|
||||
...t,
|
||||
planStartDate: normalizeDate(t.planStartDate),
|
||||
planEndDate: normalizeDate(t.planEndDate),
|
||||
children: (t.children || []).map((c: any) => ({
|
||||
...c,
|
||||
planStartDate: normalizeDate(c.planStartDate),
|
||||
planEndDate: normalizeDate(c.planEndDate),
|
||||
}))
|
||||
}))
|
||||
|
||||
const submitData = {
|
||||
...form,
|
||||
// 确保projectId字段正确
|
||||
projectId: isEdit.value && currentId.value ? currentId.value : form.projectId,
|
||||
// 处理日期格式 - 确保是字符串格式 YYYY-MM-DD
|
||||
startDate: form.startDate ? (typeof form.startDate === 'string' ? form.startDate : new Date(form.startDate).toISOString().split('T')[0]) : '',
|
||||
endDate: form.endDate ? (typeof form.endDate === 'string' ? form.endDate : new Date(form.endDate).toISOString().split('T')[0]) : '',
|
||||
startDate: normalizeDate(form.startDate),
|
||||
endDate: normalizeDate(form.endDate),
|
||||
// 处理施工人员ID - 如果是数组,转换为逗号分隔的字符串
|
||||
constructorIds: Array.isArray(form.constructorIds) ? form.constructorIds.join(',') : form.constructorIds
|
||||
constructorIds: Array.isArray(form.constructorIds) ? form.constructorIds.join(',') : form.constructorIds,
|
||||
// 新字段:任务
|
||||
tasks: mapTasks(form.tasks as any[]),
|
||||
}
|
||||
|
||||
console.log('提交数据:', submitData)
|
||||
|
@ -753,8 +937,8 @@ const viewDetail = (record: T.ProjectResp) => {
|
|||
router.push({
|
||||
name: 'ProjectDetail',
|
||||
params: {
|
||||
id: projectId.toString()
|
||||
}
|
||||
id: projectId.toString(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -811,7 +995,7 @@ const exportData = async () => {
|
|||
const params = {
|
||||
projectName: searchForm.projectName,
|
||||
status: searchForm.status,
|
||||
fieldName: searchForm.fieldName, // 保持使用fieldName,因为API类型定义中使用的是这个字段名
|
||||
fieldName: searchForm.fieldName, // 保持使用fieldName,因为API类型定义中使用的是这个字段名
|
||||
}
|
||||
|
||||
await exportProject(params)
|
||||
|
@ -829,9 +1013,9 @@ const fetchUserList = async () => {
|
|||
// 调用用户列表接口
|
||||
const res = await http.get('/user/list')
|
||||
if (res.data && Array.isArray(res.data)) {
|
||||
userOptions.value = res.data.map(item => ({
|
||||
userOptions.value = res.data.map((item) => ({
|
||||
label: item.userName || item.username || item.name || item.nickName || item.account || '未命名用户',
|
||||
value: item.userId || item.id || ''
|
||||
value: item.userId || item.id || '',
|
||||
}))
|
||||
} else {
|
||||
userOptions.value = []
|
||||
|
|
Loading…
Reference in New Issue