Merge branch 'devlopment' of http://pms.dtyx.net:3000/wuxueyu/Industrial-image-management-system---web into devlopment
This commit is contained in:
commit
05f09ad207
|
@ -117,17 +117,22 @@ export interface ProjectKanbanStats {
|
||||||
|
|
||||||
/** 项目看板数据 */
|
/** 项目看板数据 */
|
||||||
export interface ProjectKanbanData {
|
export interface ProjectKanbanData {
|
||||||
inProgressProjects: never[]
|
inProgressProjects: ProjectCard[]
|
||||||
preparingProjects: ProjectCard[]
|
preparingProjects: ProjectCard[]
|
||||||
ongoingProjects: ProjectCard[]
|
ongoingProjects: ProjectCard[]
|
||||||
pendingProjects: ProjectCard[]
|
pendingProjects: ProjectCard[]
|
||||||
|
suspendedProjects: ProjectCard[]
|
||||||
|
completedProjects: ProjectCard[]
|
||||||
|
acceptanceProjects: ProjectCard[]
|
||||||
|
collectionProjects: ProjectCard[]
|
||||||
|
settledProjects: ProjectCard[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 项目卡片信息 */
|
/** 项目卡片信息 */
|
||||||
export interface ProjectCard {
|
export interface ProjectCard {
|
||||||
id: string | number
|
id: string | number
|
||||||
name: string
|
name: string
|
||||||
status: 'preparing' | 'ongoing' | 'pending'
|
status: number | 'preparing' | 'ongoing' | 'pending' | 'inProgress' | 'suspended' | 'completed' | 'acceptance' | 'collection' | 'settled'
|
||||||
budget: number
|
budget: number
|
||||||
manager: string
|
manager: string
|
||||||
teamSize: number
|
teamSize: number
|
||||||
|
@ -139,6 +144,9 @@ export interface ProjectCard {
|
||||||
alerts?: ProjectAlert[]
|
alerts?: ProjectAlert[]
|
||||||
teamMembers: TeamMemberResp[]
|
teamMembers: TeamMemberResp[]
|
||||||
requirements: ProjectRequirementResp[]
|
requirements: ProjectRequirementResp[]
|
||||||
|
farmName?: string
|
||||||
|
scale?: string
|
||||||
|
createTime?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 项目异常信息 */
|
/** 项目异常信息 */
|
||||||
|
|
|
@ -70,6 +70,6 @@ declare global {
|
||||||
// for type re-export
|
// for type re-export
|
||||||
declare global {
|
declare global {
|
||||||
// @ts-ignore
|
// @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')
|
import('vue')
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,69 @@ export {}
|
||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
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']
|
||||||
|
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']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
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']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,9 @@
|
||||||
<a-layout-sider
|
<a-layout-sider
|
||||||
width="260"
|
width="260"
|
||||||
:collapsed-width="80"
|
:collapsed-width="80"
|
||||||
theme="light"
|
theme="dark"
|
||||||
class="folder-sidebar"
|
class="folder-sidebar"
|
||||||
:collapsed="sidebarCollapsed"
|
:collapsed="sidebarCollapsed"
|
||||||
collapsible
|
|
||||||
@collapse="handleSidebarCollapse"
|
@collapse="handleSidebarCollapse"
|
||||||
@expand="handleSidebarExpand"
|
@expand="handleSidebarExpand"
|
||||||
>
|
>
|
||||||
|
@ -102,29 +101,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 侧边栏底部分页控件 -->
|
|
||||||
<div class="sidebar-footer" v-if="!sidebarCollapsed && folderList.length > 0">
|
|
||||||
<div class="pagination-info">
|
|
||||||
<a-typography-text type="secondary" size="small">
|
|
||||||
共 {{ totalFolders }} 个文件夹
|
|
||||||
</a-typography-text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 隐藏分页控件,因为现在获取所有文件夹 -->
|
|
||||||
<!-- <div class="pagination-controls">
|
|
||||||
<a-pagination
|
|
||||||
:current="currentPage"
|
|
||||||
:page-size="pageSize"
|
|
||||||
:total="totalFolders"
|
|
||||||
:show-size-changer="true"
|
|
||||||
:page-size-options="['10', '20', '50', '100']"
|
|
||||||
@change="handlePageChange"
|
|
||||||
@showSizeChange="handlePageSizeChange"
|
|
||||||
size="small"
|
|
||||||
show-total
|
|
||||||
/>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
</a-layout-sider>
|
</a-layout-sider>
|
||||||
|
|
||||||
<a-layout>
|
<a-layout>
|
||||||
|
@ -305,7 +282,7 @@
|
||||||
|
|
||||||
<!-- 大小列 -->
|
<!-- 大小列 -->
|
||||||
<a-col :span="3" class="table-column size-column">
|
<a-col :span="3" class="table-column size-column">
|
||||||
<div class="cell-content">{{ formatFileSize(file.fileSize || file.size) }}</div>
|
<div class="cell-content">{{ formatFileListSize(file.fileSize || file.size) }}</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
|
||||||
<!-- 时间列 -->
|
<!-- 时间列 -->
|
||||||
|
@ -682,7 +659,7 @@ const fileListTemp = ref([]);
|
||||||
const folderFormRef = ref(null);
|
const folderFormRef = ref(null);
|
||||||
const uploadFormRef = ref(null);
|
const uploadFormRef = ref(null);
|
||||||
const uploadRef = ref(null);
|
const uploadRef = ref(null);
|
||||||
const folderColor = '#165DFF';
|
const folderColor = 'var(--color-primary)';
|
||||||
const refreshing = ref(false);
|
const refreshing = ref(false);
|
||||||
const folderSubmitting = ref(false);
|
const folderSubmitting = ref(false);
|
||||||
const uploading = ref(false);
|
const uploading = ref(false);
|
||||||
|
@ -1624,7 +1601,7 @@ const fileColor = (extension) => {
|
||||||
bmp: '#722ed1',
|
bmp: '#722ed1',
|
||||||
webp: '#13c2c2'
|
webp: '#13c2c2'
|
||||||
};
|
};
|
||||||
return colorMap[extension.toLowerCase()] || '#8c8c8c';
|
return colorMap[extension.toLowerCase()] || 'var(--color-text-3)';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1881,8 +1858,8 @@ const showTextPreview = async (blob, fileName) => {
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
maxHeight: '70vh',
|
maxHeight: '70vh',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
backgroundColor: '#f8f9fa',
|
backgroundColor: 'var(--color-fill-1)',
|
||||||
border: '1px solid #e9ecef',
|
border: '1px solid var(--color-border)',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
fontFamily: "'Consolas', 'Monaco', 'Courier New', monospace",
|
fontFamily: "'Consolas', 'Monaco', 'Courier New', monospace",
|
||||||
|
@ -1890,7 +1867,7 @@ const showTextPreview = async (blob, fileName) => {
|
||||||
lineHeight: '1.6',
|
lineHeight: '1.6',
|
||||||
whiteSpace: 'pre-wrap',
|
whiteSpace: 'pre-wrap',
|
||||||
wordBreak: 'break-word',
|
wordBreak: 'break-word',
|
||||||
color: '#333',
|
color: 'var(--color-text-1)',
|
||||||
textAlign: 'left'
|
textAlign: 'left'
|
||||||
}
|
}
|
||||||
}, text)
|
}, text)
|
||||||
|
@ -2336,6 +2313,21 @@ const formatFileSize = (fileSize) => {
|
||||||
return `${(size / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
return `${(size / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 专门用于文件列表的格式化函数(假设后端返回的是KB单位)
|
||||||
|
const formatFileListSize = (fileSize) => {
|
||||||
|
const size = Number(fileSize);
|
||||||
|
if (isNaN(size) || size < 0) return '未知';
|
||||||
|
|
||||||
|
// 假设后端返回的是KB单位
|
||||||
|
if (size < 1024) {
|
||||||
|
return `${size} KB`;
|
||||||
|
} else if (size < 1024 * 1024) {
|
||||||
|
return `${(size / 1024).toFixed(1)} MB`;
|
||||||
|
} else {
|
||||||
|
return `${(size / (1024 * 1024)).toFixed(1)} GB`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const fileTypeText = (type) => {
|
const fileTypeText = (type) => {
|
||||||
|
@ -2458,7 +2450,7 @@ onMounted(() => {
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.knowledge-container {
|
.knowledge-container {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: var(--color-bg-2);
|
background-color: var(--color-bg-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2470,13 +2462,15 @@ onMounted(() => {
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: linear-gradient(180deg, #ffffff 0%, #fafbfc 100%);
|
background: var(--color-bg-1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
padding: 20px 16px;
|
padding: 20px 16px;
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
background: var(--color-bg-1);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
|
@ -2492,12 +2486,18 @@ onMounted(() => {
|
||||||
|
|
||||||
.folder-content {
|
.folder-content {
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
height: calc(100vh - 320px); /* 为底部分页控件留出更多空间,因为文件夹项现在更高 */
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: var(--color-bg-1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
max-height: calc(100vh - 200px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.folder-content::-webkit-scrollbar {
|
.folder-content::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
}
|
}
|
||||||
|
@ -2537,7 +2537,7 @@ onMounted(() => {
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
|
background: linear-gradient(135deg, var(--color-fill-2) 0%, var(--color-fill-3) 100%);
|
||||||
border-color: var(--color-primary-light-2);
|
border-color: var(--color-primary-light-2);
|
||||||
transform: translateX(2px);
|
transform: translateX(2px);
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
@ -2606,7 +2606,7 @@ onMounted(() => {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
background: var(--color-bg-2);
|
background: var(--color-bg-1);
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
height: 64px;
|
height: 64px;
|
||||||
}
|
}
|
||||||
|
@ -2623,10 +2623,12 @@ onMounted(() => {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
overflow: auto;
|
overflow: hidden;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
background: var(--color-bg-2);
|
background: var(--color-bg-1);
|
||||||
|
min-height: 0;
|
||||||
|
max-height: calc(100vh - 120px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-card {
|
.file-card {
|
||||||
|
@ -2637,6 +2639,7 @@ onMounted(() => {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 表格容器 */
|
/* 表格容器 */
|
||||||
|
@ -2649,7 +2652,9 @@ onMounted(() => {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background-color: var(--color-bg-1);
|
background-color: var(--color-bg-1);
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||||
padding-bottom: 80px; /* 为分页留出空间 */
|
margin-bottom: 0;
|
||||||
|
min-height: 300px;
|
||||||
|
max-height: calc(100vh - 300px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 表头行样式 */
|
/* 表头行样式 */
|
||||||
|
@ -2729,8 +2734,8 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.folder-icon {
|
.folder-icon {
|
||||||
color: #165DFF;
|
color: var(--color-primary);
|
||||||
background-color: #E8F3FF;
|
background-color: var(--color-primary-light-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-name {
|
.file-name {
|
||||||
|
@ -2744,7 +2749,7 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-data-row:hover .file-name {
|
.table-data-row:hover .file-name {
|
||||||
color: #165DFF;
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.type-column, .size-column, .time-column {
|
.type-column, .size-column, .time-column {
|
||||||
|
@ -2789,7 +2794,7 @@ onMounted(() => {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
.table-data-row:hover & {
|
.table-data-row:hover & {
|
||||||
color: #165DFF;
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2922,6 +2927,35 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 浏览器缩放调整 */
|
||||||
|
@media (max-height: 800px) {
|
||||||
|
.folder-content {
|
||||||
|
max-height: calc(100vh - 180px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-content {
|
||||||
|
max-height: calc(100vh - 100px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-grid-container {
|
||||||
|
max-height: calc(100vh - 280px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height: 600px) {
|
||||||
|
.folder-content {
|
||||||
|
max-height: calc(100vh - 160px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-content {
|
||||||
|
max-height: calc(100vh - 80px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-grid-container {
|
||||||
|
max-height: calc(100vh - 260px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 空状态样式 */
|
/* 空状态样式 */
|
||||||
.initial-state, .empty-state {
|
.initial-state, .empty-state {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -2930,7 +2964,7 @@ onMounted(() => {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 64px 0;
|
padding: 64px 0;
|
||||||
color: var(--color-text-3);
|
color: var(--color-text-3);
|
||||||
background-color: #fafafa;
|
background-color: var(--color-fill-1);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -2944,7 +2978,7 @@ onMounted(() => {
|
||||||
:deep(.empty-state .arco-btn) {
|
:deep(.empty-state .arco-btn) {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
background-color: #165DFF;
|
background-color: var(--color-primary);
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: none;
|
border: none;
|
||||||
|
@ -2954,7 +2988,7 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.empty-state .arco-btn:hover) {
|
:deep(.empty-state .arco-btn:hover) {
|
||||||
background-color: #0E42D2;
|
background-color: var(--color-primary-dark-1);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
@ -3076,50 +3110,7 @@ onMounted(() => {
|
||||||
border-top: 1px solid var(--color-border);
|
border-top: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 侧边栏底部分页样式 */
|
|
||||||
.sidebar-footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: linear-gradient(180deg, #f8fafc 0%, #e2e8f0 100%);
|
|
||||||
border-top: 1px solid var(--color-border);
|
|
||||||
padding: 20px 16px;
|
|
||||||
z-index: 10;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-info {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
text-align: center;
|
|
||||||
padding: 8px 12px;
|
|
||||||
background: rgba(255, 255, 255, 0.8);
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-controls {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
:deep(.arco-pagination) {
|
|
||||||
.arco-pagination-item {
|
|
||||||
border-radius: 6px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保文件夹内容区域不被底部分页遮挡 */
|
|
||||||
.folder-content {
|
|
||||||
/* 高度已调整,无需额外padding */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 动画效果 */
|
/* 动画效果 */
|
||||||
:deep(.arco-icon-refresh.spin) {
|
:deep(.arco-icon-refresh.spin) {
|
||||||
|
@ -3250,8 +3241,8 @@ onMounted(() => {
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid #e2e8f0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #e2e8f0;
|
background: var(--color-fill-2);
|
||||||
color: #165DFF;
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3260,8 +3251,8 @@ onMounted(() => {
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: #165DFF;
|
border-color: var(--color-primary);
|
||||||
color: #165DFF;
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
|
@ -3370,7 +3361,7 @@ onMounted(() => {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: rgba(var(--color-bg-1-rgb), 0.9);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: var(--color-primary-light-2);
|
border-color: var(--color-primary-light-2);
|
||||||
|
@ -3388,7 +3379,7 @@ onMounted(() => {
|
||||||
.search-result-tip {
|
.search-result-tip {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
margin: 12px 16px;
|
margin: 12px 16px;
|
||||||
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
background: linear-gradient(135deg, var(--color-primary-light-1) 0%, var(--color-primary-light-2) 100%);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border-left: 4px solid var(--color-primary);
|
border-left: 4px solid var(--color-primary);
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
@ -3402,7 +3393,7 @@ onMounted(() => {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.1) 50%, transparent 70%);
|
background: linear-gradient(45deg, transparent 30%, rgba(var(--color-bg-1-rgb), 0.1) 50%, transparent 70%);
|
||||||
animation: shimmer 2s infinite;
|
animation: shimmer 2s infinite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3440,7 +3431,7 @@ onMounted(() => {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--color-bg-2);
|
background: var(--color-bg-1);
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
@ -3612,17 +3603,18 @@ onMounted(() => {
|
||||||
|
|
||||||
/* 文件分页样式 */
|
/* 文件分页样式 */
|
||||||
.file-pagination {
|
.file-pagination {
|
||||||
position: absolute;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 10;
|
margin-top: 16px;
|
||||||
margin-top: 0;
|
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-top: 1px solid var(--color-border);
|
border-top: 1px solid var(--color-border);
|
||||||
background: var(--color-bg-1);
|
background: var(--color-bg-1);
|
||||||
|
flex-shrink: 0;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
.arco-pagination {
|
.arco-pagination {
|
||||||
.arco-pagination-total {
|
.arco-pagination-total {
|
||||||
|
@ -3662,7 +3654,7 @@ onMounted(() => {
|
||||||
/* 树形文件夹结构 */
|
/* 树形文件夹结构 */
|
||||||
.folder-tree-container {
|
.folder-tree-container {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
background: var(--color-bg-2);
|
background: var(--color-bg-1);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -35,11 +35,11 @@
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item field="contractStatus" label="合同状态">
|
<a-form-item field="contractStatus" label="合同状态">
|
||||||
<a-select v-model="contractData.contractStatus">
|
<a-select v-model="contractData.contractStatus">
|
||||||
<a-option value="未确认">未确认</a-option>
|
<a-option value="未执行">未执行</a-option>
|
||||||
<a-option value="待审批">待审批</a-option>
|
|
||||||
<a-option value="已签署">已签署</a-option>
|
|
||||||
<a-option value="执行中">执行中</a-option>
|
<a-option value="执行中">执行中</a-option>
|
||||||
<a-option value="已完成">已完成</a-option>
|
<a-option value="验收中">验收中</a-option>
|
||||||
|
<a-option value="结算中">结算中</a-option>
|
||||||
|
<a-option value="已结算">已结算</a-option>
|
||||||
<a-option value="已终止">已终止</a-option>
|
<a-option value="已终止">已终止</a-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
|
@ -48,18 +48,23 @@
|
||||||
<span class="font-medium text-green-600">¥{{ (record.amount || 0).toLocaleString() }}</span>
|
<span class="font-medium text-green-600">¥{{ (record.amount || 0).toLocaleString() }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 已收款金额 -->
|
<!-- 已结算金额(支出合同) -->
|
||||||
<template #receivedAmount="{ record }">
|
<template #settlementAmount="{ record }">
|
||||||
<span class="font-medium text-blue-600">¥{{ (record.receivedAmount || 0).toLocaleString() }}</span>
|
<span class="font-medium text-blue-600">¥{{ (record.settlementAmount || record.receivedAmount || 0).toLocaleString() }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-link @click="viewDetail(record)">查看</a-link>
|
<a-link @click="viewDetail(record)">查看</a-link>
|
||||||
<a-link @click="editRecord(record)">编辑</a-link>
|
<a-link @click="editRecord(record)">编辑</a-link>
|
||||||
|
<a-link :disabled="record.contractStatus === '已结算'" @click="!(record.contractStatus === '已结算') && openSettlement(record)">结算</a-link>
|
||||||
<a-link status="danger" @click="deleteContract(record)">删除</a-link>
|
<a-link status="danger" @click="deleteContract(record)">删除</a-link>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- 日期展示:仅年月日(放在 action 模板之外作为独立列插槽) -->
|
||||||
|
<template #signDate="{ record }">{{ (record.signDate || '').slice(0,10) }}</template>
|
||||||
|
<template #performanceDeadline="{ record }">{{ (record.performanceDeadline || '').slice(0,10) }}</template>
|
||||||
|
<template #paymentDate="{ record }">{{ (record.paymentDate || '').slice(0,10) }}</template>
|
||||||
</GiTable>
|
</GiTable>
|
||||||
|
|
||||||
<!-- 合同详情弹窗 -->
|
<!-- 合同详情弹窗 -->
|
||||||
|
@ -102,6 +107,15 @@
|
||||||
@update:contract-data="handleNewContractDataUpdate"
|
@update:contract-data="handleNewContractDataUpdate"
|
||||||
/>
|
/>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
<!-- 合同结算弹窗 -->
|
||||||
|
<a-modal v-model:visible="showSettlementModal" title="合同结算" :width="520" @before-ok="submitSettlement">
|
||||||
|
<a-form :model="settlementForm" layout="vertical">
|
||||||
|
<a-form-item field="amount" label="结算金额"><a-input-number v-model="settlementForm.amount" style="width:100%" /></a-form-item>
|
||||||
|
<a-form-item field="paymentDate" label="付款日期"><a-date-picker v-model="settlementForm.paymentDate" show-time value-format="YYYY-MM-DD HH:mm:ss" style="width:100%"/></a-form-item>
|
||||||
|
<a-form-item field="paymentPeriod" label="账期"><a-date-picker v-model="settlementForm.paymentPeriod" show-time value-format="YYYY-MM-DD HH:mm:ss" style="width:100%"/></a-form-item>
|
||||||
|
<a-form-item field="notes" label="备注"><a-textarea v-model="settlementForm.notes" /></a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
</GiPageLayout>
|
</GiPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -173,11 +187,11 @@ const queryFormColumns = [
|
||||||
props: {
|
props: {
|
||||||
placeholder: '请选择合同状态',
|
placeholder: '请选择合同状态',
|
||||||
options: [
|
options: [
|
||||||
{ label: '未确认', value: '未确认' },
|
{ label: '未执行', value: '未执行' },
|
||||||
{ label: '待审批', value: '待审批' },
|
|
||||||
{ label: '已签署', value: '已签署' },
|
|
||||||
{ label: '执行中', value: '执行中' },
|
{ label: '执行中', value: '执行中' },
|
||||||
{ label: '已完成', value: '已完成' },
|
{ label: '验收中', value: '验收中' },
|
||||||
|
{ label: '结算中', value: '结算中' },
|
||||||
|
{ label: '已结算', value: '已结算' },
|
||||||
{ label: '已终止', value: '已终止' },
|
{ label: '已终止', value: '已终止' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -187,9 +201,8 @@ const queryFormColumns = [
|
||||||
label: '签署时间',
|
label: '签署时间',
|
||||||
type: 'range-picker' as const,
|
type: 'range-picker' as const,
|
||||||
props: {
|
props: {
|
||||||
placeholder: ['开始时间', '结束时间'],
|
placeholder: ['开始日期', '结束日期'],
|
||||||
showTime: true,
|
format: 'YYYY-MM-DD',
|
||||||
format: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -200,11 +213,11 @@ const tableColumns: TableColumnData[] = [
|
||||||
{ title: '项目名称', dataIndex: 'projectName', width: 250, ellipsis: true, tooltip: true },
|
{ title: '项目名称', dataIndex: 'projectName', width: 250, ellipsis: true, tooltip: true },
|
||||||
{ title: '客户名称', dataIndex: 'customer', width: 200, ellipsis: true, tooltip: true },
|
{ title: '客户名称', dataIndex: 'customer', width: 200, ellipsis: true, tooltip: true },
|
||||||
{ title: '合同金额', dataIndex: 'amount', slotName: 'contractAmount', width: 120 },
|
{ title: '合同金额', dataIndex: 'amount', slotName: 'contractAmount', width: 120 },
|
||||||
{ title: '已收款金额', dataIndex: 'receivedAmount', slotName: 'receivedAmount', width: 120 },
|
{ title: '已结算金额', dataIndex: 'settlementAmount', slotName: 'settlementAmount', width: 120 },
|
||||||
{ title: '未收款金额', dataIndex: 'pendingAmount', width: 120 },
|
{ title: '未结算金额', dataIndex: 'pendingAmount', width: 120 },
|
||||||
{ title: '签署日期', dataIndex: 'signDate', width: 120 },
|
{ title: '签署日期', dataIndex: 'signDate', slotName: 'signDate', width: 120 },
|
||||||
{ title: '履约期限', dataIndex: 'performanceDeadline', width: 120 },
|
{ title: '履约期限', dataIndex: 'performanceDeadline', slotName: 'performanceDeadline', width: 120 },
|
||||||
{ title: '付款日期', dataIndex: 'paymentDate', width: 120 },
|
{ title: '付款日期', dataIndex: 'paymentDate', slotName: 'paymentDate', width: 120 },
|
||||||
{ title: '合同状态', dataIndex: 'contractStatus', slotName: 'status', width: 100 },
|
{ title: '合同状态', dataIndex: 'contractStatus', slotName: 'status', width: 100 },
|
||||||
{ title: '销售人员', dataIndex: 'salespersonName', width: 100 },
|
{ title: '销售人员', dataIndex: 'salespersonName', width: 100 },
|
||||||
{ title: '销售部门', dataIndex: 'salespersonDeptName', width: 100 },
|
{ title: '销售部门', dataIndex: 'salespersonDeptName', width: 100 },
|
||||||
|
@ -252,10 +265,10 @@ const fetchContractList = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算未收款金额
|
// 计算未结算金额(支出合同)
|
||||||
dataList.value = filtered.map((item: ContractItem) => ({
|
dataList.value = filtered.map((item: ContractItem) => ({
|
||||||
...item,
|
...item,
|
||||||
pendingAmount: (item.amount || 0) - (item.receivedAmount || 0),
|
pendingAmount: (item.amount || 0) - ((item.settlementAmount || item.receivedAmount || 0)),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 更新分页总数(前端过滤后以过滤结果数为准)
|
// 更新分页总数(前端过滤后以过滤结果数为准)
|
||||||
|
@ -284,11 +297,11 @@ const pagination = reactive({
|
||||||
// 获取状态颜色
|
// 获取状态颜色
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
const colorMap: Record<string, string> = {
|
const colorMap: Record<string, string> = {
|
||||||
未确认: 'gray',
|
未执行: 'gray',
|
||||||
待审批: 'orange',
|
|
||||||
已签署: 'blue',
|
|
||||||
执行中: 'cyan',
|
执行中: 'cyan',
|
||||||
已完成: 'green',
|
验收中: 'arcoblue',
|
||||||
|
结算中: 'orange',
|
||||||
|
已结算: 'green',
|
||||||
已终止: 'red',
|
已终止: 'red',
|
||||||
}
|
}
|
||||||
return colorMap[status] || 'gray'
|
return colorMap[status] || 'gray'
|
||||||
|
@ -381,7 +394,7 @@ const openAddModal = () => {
|
||||||
amount: 0,
|
amount: 0,
|
||||||
accountNumber: '',
|
accountNumber: '',
|
||||||
notes: '',
|
notes: '',
|
||||||
contractStatus: '未确认',
|
contractStatus: '未执行',
|
||||||
contractText: '',
|
contractText: '',
|
||||||
projectName: '',
|
projectName: '',
|
||||||
salespersonName: null,
|
salespersonName: null,
|
||||||
|
@ -477,8 +490,8 @@ const editRecord = (record: ContractItem) => {
|
||||||
...record,
|
...record,
|
||||||
amount: record.amount || 0,
|
amount: record.amount || 0,
|
||||||
projectId: record.projectId || '',
|
projectId: record.projectId || '',
|
||||||
type: record.type || '收入合同',
|
type: record.type || '支出合同',
|
||||||
contractStatus: record.contractStatus || '未确认',
|
contractStatus: record.contractStatus || '未执行',
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedContractData.value = completeRecord
|
selectedContractData.value = completeRecord
|
||||||
|
@ -548,6 +561,91 @@ const handleEditSubmit = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 合同结算
|
||||||
|
const showSettlementModal = ref(false)
|
||||||
|
const settlementForm = reactive({
|
||||||
|
// 核心字段
|
||||||
|
contractId: '',
|
||||||
|
amount: 0,
|
||||||
|
paymentDate: '',
|
||||||
|
paymentPeriod: '',
|
||||||
|
notes: '',
|
||||||
|
// 其余接口可选字段(用于兼容接口入参)
|
||||||
|
accountNumber: '',
|
||||||
|
code: '',
|
||||||
|
customer: '',
|
||||||
|
departmentId: '',
|
||||||
|
duration: '',
|
||||||
|
productService: '',
|
||||||
|
projectId: '',
|
||||||
|
salespersonId: '',
|
||||||
|
settlementId: '',
|
||||||
|
settlementStatus: '',
|
||||||
|
})
|
||||||
|
const settlementRecord = ref<ContractItem | null>(null)
|
||||||
|
const openSettlement = (record: ContractItem) => {
|
||||||
|
if (record.contractStatus === '已结算') return
|
||||||
|
settlementRecord.value = record
|
||||||
|
Object.assign(settlementForm, {
|
||||||
|
// 核心
|
||||||
|
contractId: record.contractId,
|
||||||
|
amount: record.amount || 0,
|
||||||
|
paymentDate: record.paymentDate || '',
|
||||||
|
paymentPeriod: '',
|
||||||
|
notes: '',
|
||||||
|
// 其余字段从合同记录回填,便于直接提交
|
||||||
|
accountNumber: (record as any).accountNumber || '',
|
||||||
|
code: (record as any).code || '',
|
||||||
|
customer: (record as any).customer || '',
|
||||||
|
departmentId: (record as any).departmentId || '',
|
||||||
|
duration: (record as any).duration || '',
|
||||||
|
productService: (record as any).productService || '',
|
||||||
|
projectId: (record as any).projectId || '',
|
||||||
|
salespersonId: (record as any).salespersonId || '',
|
||||||
|
settlementId: (record as any).settlementId || '',
|
||||||
|
settlementStatus: (record as any).settlementStatus || '',
|
||||||
|
})
|
||||||
|
showSettlementModal.value = true
|
||||||
|
}
|
||||||
|
const submitSettlement = async () => {
|
||||||
|
try {
|
||||||
|
const payload:any = {
|
||||||
|
accountNumber: settlementForm.accountNumber || '',
|
||||||
|
amount: settlementForm.amount,
|
||||||
|
code: settlementForm.code || '',
|
||||||
|
contractId: settlementForm.contractId,
|
||||||
|
customer: settlementForm.customer || '',
|
||||||
|
departmentId: settlementForm.departmentId || '',
|
||||||
|
duration: settlementForm.duration || '',
|
||||||
|
notes: settlementForm.notes || '',
|
||||||
|
paymentDate: settlementForm.paymentDate || null,
|
||||||
|
paymentPeriod: settlementForm.paymentPeriod || '',
|
||||||
|
productService: settlementForm.productService || '',
|
||||||
|
projectId: settlementForm.projectId || '',
|
||||||
|
salespersonId: settlementForm.salespersonId || '',
|
||||||
|
settlementId: settlementForm.settlementId || '',
|
||||||
|
settlementStatus: settlementForm.settlementStatus || '',
|
||||||
|
}
|
||||||
|
const res = await http.post('/contract-settlement', payload)
|
||||||
|
if (res.code === 200 || (res as any).status === 200) {
|
||||||
|
Message.success('合同结算成功')
|
||||||
|
// 根据未结算金额是否为 0 决定状态:0 => 已结算,否则保留/置为“结算中”
|
||||||
|
const origin = settlementRecord.value
|
||||||
|
const settledAmount = (origin?.settlementAmount ?? origin?.receivedAmount ?? 0) + (settlementForm.amount || 0)
|
||||||
|
const pendingAfter = Math.max((origin?.amount || 0) - settledAmount, 0)
|
||||||
|
const targetStatus = pendingAfter === 0 ? '已结算' : '结算中'
|
||||||
|
await http.put('/contract', { contractId: settlementForm.contractId, contractStatus: targetStatus, type: origin?.type || '支出合同' })
|
||||||
|
showSettlementModal.value = false
|
||||||
|
search()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
Message.error(res.msg || '合同结算失败')
|
||||||
|
return false
|
||||||
|
} catch (e:any) {
|
||||||
|
Message.error(e?.message || '合同结算失败')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 删除合同:只有在确认框点击确定后才执行删除
|
// 删除合同:只有在确认框点击确定后才执行删除
|
||||||
const deleteContract = (record: ContractItem) => {
|
const deleteContract = (record: ContractItem) => {
|
||||||
|
|
|
@ -35,11 +35,11 @@
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item field="contractStatus" label="合同状态">
|
<a-form-item field="contractStatus" label="合同状态">
|
||||||
<a-select v-model="contractData.contractStatus">
|
<a-select v-model="contractData.contractStatus">
|
||||||
<a-option value="未确认">未确认</a-option>
|
<a-option value="未执行">未执行</a-option>
|
||||||
<a-option value="待审批">待审批</a-option>
|
|
||||||
<a-option value="已签署">已签署</a-option>
|
|
||||||
<a-option value="执行中">执行中</a-option>
|
<a-option value="执行中">执行中</a-option>
|
||||||
<a-option value="已完成">已完成</a-option>
|
<a-option value="验收中">验收中</a-option>
|
||||||
|
<a-option value="结算中">结算中</a-option>
|
||||||
|
<a-option value="已结算">已结算</a-option>
|
||||||
<a-option value="已终止">已终止</a-option>
|
<a-option value="已终止">已终止</a-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
|
@ -48,18 +48,24 @@
|
||||||
<span class="font-medium text-green-600">¥{{ (record.amount || 0).toLocaleString() }}</span>
|
<span class="font-medium text-green-600">¥{{ (record.amount || 0).toLocaleString() }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 已收款金额 -->
|
<!-- 已回款金额(收入合同) -->
|
||||||
<template #receivedAmount="{ record }">
|
<template #receivedAmount="{ record }">
|
||||||
<span class="font-medium text-blue-600">¥{{ (record.receivedAmount || 0).toLocaleString() }}</span>
|
<span class="font-medium text-blue-600">¥{{ (record.receivedAmount || record.settlementAmount || 0).toLocaleString() }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-link @click="viewDetail(record)">查看</a-link>
|
<a-link @click="viewDetail(record)">查看</a-link>
|
||||||
<a-link @click="editRecord(record)">编辑</a-link>
|
<a-link @click="editRecord(record)">编辑</a-link>
|
||||||
|
<a-link :disabled="record.contractStatus === '已结算'" @click="!(record.contractStatus === '已结算') && openSettlement(record)">结算</a-link>
|
||||||
<a-link status="danger" @click="deleteContract(record)">删除</a-link>
|
<a-link status="danger" @click="deleteContract(record)">删除</a-link>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 日期展示:仅年月日(放在 action 模板之外作为独立列插槽) -->
|
||||||
|
<template #signDate="{ record }">{{ (record.signDate || '').slice(0,10) }}</template>
|
||||||
|
<template #performanceDeadline="{ record }">{{ (record.performanceDeadline || '').slice(0,10) }}</template>
|
||||||
|
<template #paymentDate="{ record }">{{ (record.paymentDate || '').slice(0,10) }}</template>
|
||||||
</GiTable>
|
</GiTable>
|
||||||
|
|
||||||
<!-- 合同详情弹窗 -->
|
<!-- 合同详情弹窗 -->
|
||||||
|
@ -87,8 +93,6 @@
|
||||||
@update:contract-data="handleContractDataUpdate"
|
@update:contract-data="handleContractDataUpdate"
|
||||||
/>
|
/>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</GiPageLayout>
|
|
||||||
|
|
||||||
<!-- 新建合同弹窗 -->
|
<!-- 新建合同弹窗 -->
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="showAddModal"
|
v-model:visible="showAddModal"
|
||||||
|
@ -102,7 +106,18 @@
|
||||||
:contract-data="newContractData"
|
:contract-data="newContractData"
|
||||||
@update:contract-data="handleNewContractDataUpdate"
|
@update:contract-data="handleNewContractDataUpdate"
|
||||||
/>
|
/>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 合同结算弹窗 -->
|
||||||
|
<a-modal v-model:visible="showSettlementModal" title="合同结算" :width="520" @before-ok="submitSettlement">
|
||||||
|
<a-form :model="settlementForm" layout="vertical">
|
||||||
|
<a-form-item field="amount" label="结算金额"><a-input-number v-model="settlementForm.amount" style="width:100%" /></a-form-item>
|
||||||
|
<a-form-item field="paymentDate" label="付款日期"><a-date-picker v-model="settlementForm.paymentDate" show-time value-format="YYYY-MM-DD HH:mm:ss" style="width:100%"/></a-form-item>
|
||||||
|
<a-form-item field="paymentPeriod" label="账期"><a-date-picker v-model="settlementForm.paymentPeriod" show-time value-format="YYYY-MM-DD HH:mm:ss" style="width:100%"/></a-form-item>
|
||||||
|
<a-form-item field="notes" label="备注"><a-textarea v-model="settlementForm.notes" /></a-form-item>
|
||||||
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
</GiPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -162,17 +177,17 @@ const queryFormColumns = [
|
||||||
{ field: 'status', label: '合同状态', type: 'select' as const, props: {
|
{ field: 'status', label: '合同状态', type: 'select' as const, props: {
|
||||||
placeholder: '请选择合同状态',
|
placeholder: '请选择合同状态',
|
||||||
options: [
|
options: [
|
||||||
{ label: '未确认', value: '未确认' },
|
{ label: '未执行', value: '未执行' },
|
||||||
{ label: '待审批', value: '待审批' },
|
|
||||||
{ label: '已签署', value: '已签署' },
|
|
||||||
{ label: '执行中', value: '执行中' },
|
{ label: '执行中', value: '执行中' },
|
||||||
{ label: '已完成', value: '已完成' },
|
{ label: '验收中', value: '验收中' },
|
||||||
|
{ label: '结算中', value: '结算中' },
|
||||||
|
{ label: '已结算', value: '已结算' },
|
||||||
{ label: '已终止', value: '已终止' },
|
{ label: '已终止', value: '已终止' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ field: 'signDateRange', label: '签署时间', type: 'range-picker' as const, props: {
|
{ field: 'signDateRange', label: '签署时间', type: 'range-picker' as const, props: {
|
||||||
placeholder: ['开始时间', '结束时间'], showTime: true, format: 'YYYY-MM-DD HH:mm:ss',
|
placeholder: ['开始日期', '结束日期'], format: 'YYYY-MM-DD',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -185,16 +200,105 @@ const tableColumns: TableColumnData[] = [
|
||||||
{ title: '合同金额', dataIndex: 'amount', slotName: 'contractAmount', width: 120 },
|
{ title: '合同金额', dataIndex: 'amount', slotName: 'contractAmount', width: 120 },
|
||||||
{ title: '已收款金额', dataIndex: 'receivedAmount', slotName: 'receivedAmount', width: 120 },
|
{ title: '已收款金额', dataIndex: 'receivedAmount', slotName: 'receivedAmount', width: 120 },
|
||||||
{ title: '未收款金额', dataIndex: 'pendingAmount', width: 120 },
|
{ title: '未收款金额', dataIndex: 'pendingAmount', width: 120 },
|
||||||
{ title: '签署日期', dataIndex: 'signDate', width: 120 },
|
{ title: '签署日期', dataIndex: 'signDate', slotName: 'signDate', width: 120 },
|
||||||
{ title: '履约期限', dataIndex: 'performanceDeadline', width: 120 },
|
{ title: '履约期限', dataIndex: 'performanceDeadline', slotName: 'performanceDeadline', width: 120 },
|
||||||
{ title: '付款日期', dataIndex: 'paymentDate', width: 120 },
|
{ title: '付款日期', dataIndex: 'paymentDate', slotName: 'paymentDate', width: 120 },
|
||||||
{ title: '合同状态', dataIndex: 'contractStatus', slotName: 'status', width: 100 },
|
{ title: '合同状态', dataIndex: 'contractStatus', slotName: 'status', width: 120 },
|
||||||
{ title: '销售人员', dataIndex: 'salespersonName', width: 100 },
|
{ title: '销售人员', dataIndex: 'salespersonName', width: 100 },
|
||||||
{ title: '销售部门', dataIndex: 'salespersonDeptName', width: 100 },
|
{ title: '销售部门', dataIndex: 'salespersonDeptName', width: 100 },
|
||||||
{ title: '产品服务', dataIndex: 'productService', width: 120, ellipsis: true, tooltip: true },
|
{ title: '产品服务', dataIndex: 'productService', width: 120, ellipsis: true, tooltip: true },
|
||||||
{ title: '备注', dataIndex: 'notes', width: 200, ellipsis: true, tooltip: true },
|
{ title: '备注', dataIndex: 'notes', width: 220, ellipsis: true, tooltip: true },
|
||||||
{ title: '操作', slotName: 'action', width: 200, fixed: 'right' },
|
{ title: '操作', slotName: 'action', width: 240, fixed: 'right' },
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
// 合同结算(收入合同也支持)
|
||||||
|
const showSettlementModal = ref(false)
|
||||||
|
const settlementForm = reactive({
|
||||||
|
// 核心字段
|
||||||
|
contractId: '',
|
||||||
|
amount: 0,
|
||||||
|
paymentDate: '',
|
||||||
|
paymentPeriod: '',
|
||||||
|
notes: '',
|
||||||
|
// 其余接口可选字段(用于兼容接口入参)
|
||||||
|
accountNumber: '',
|
||||||
|
code: '',
|
||||||
|
customer: '',
|
||||||
|
departmentId: '',
|
||||||
|
duration: '',
|
||||||
|
productService: '',
|
||||||
|
projectId: '',
|
||||||
|
salespersonId: '',
|
||||||
|
settlementId: '',
|
||||||
|
settlementStatus: '',
|
||||||
|
})
|
||||||
|
const settlementRecord = ref<ContractItem | null>(null)
|
||||||
|
const openSettlement = (record: ContractItem) => {
|
||||||
|
if (record.contractStatus === '已结算') return
|
||||||
|
settlementRecord.value = record
|
||||||
|
Object.assign(settlementForm, {
|
||||||
|
// 核心
|
||||||
|
contractId: record.contractId,
|
||||||
|
amount: record.amount || 0,
|
||||||
|
paymentDate: record.paymentDate || '',
|
||||||
|
paymentPeriod: '',
|
||||||
|
notes: '',
|
||||||
|
// 其余字段从合同记录回填,便于直接提交
|
||||||
|
accountNumber: (record as any).accountNumber || '',
|
||||||
|
code: (record as any).code || '',
|
||||||
|
customer: (record as any).customer || '',
|
||||||
|
departmentId: (record as any).departmentId || '',
|
||||||
|
duration: (record as any).duration || '',
|
||||||
|
productService: (record as any).productService || '',
|
||||||
|
projectId: (record as any).projectId || '',
|
||||||
|
salespersonId: (record as any).salespersonId || '',
|
||||||
|
settlementId: (record as any).settlementId || '',
|
||||||
|
settlementStatus: (record as any).settlementStatus || '',
|
||||||
|
})
|
||||||
|
showSettlementModal.value = true
|
||||||
|
}
|
||||||
|
const submitSettlement = async () => {
|
||||||
|
try {
|
||||||
|
const payload:any = {
|
||||||
|
accountNumber: settlementForm.accountNumber || '',
|
||||||
|
amount: settlementForm.amount,
|
||||||
|
code: settlementForm.code || '',
|
||||||
|
contractId: settlementForm.contractId,
|
||||||
|
customer: settlementForm.customer || '',
|
||||||
|
departmentId: settlementForm.departmentId || '',
|
||||||
|
duration: settlementForm.duration || '',
|
||||||
|
notes: settlementForm.notes || '',
|
||||||
|
paymentDate: settlementForm.paymentDate || null,
|
||||||
|
paymentPeriod: settlementForm.paymentPeriod || '',
|
||||||
|
productService: settlementForm.productService || '',
|
||||||
|
projectId: settlementForm.projectId || '',
|
||||||
|
salespersonId: settlementForm.salespersonId || '',
|
||||||
|
settlementId: settlementForm.settlementId || '',
|
||||||
|
settlementStatus: settlementForm.settlementStatus || '',
|
||||||
|
}
|
||||||
|
const res = await http.post('/contract-settlement', payload)
|
||||||
|
if (res.code === 200 || (res as any).status === 200) {
|
||||||
|
Message.success('合同结算成功')
|
||||||
|
// 根据未结算金额是否为 0 决定状态:0 => 已结算,否则设为“结算中”
|
||||||
|
const origin = settlementRecord.value
|
||||||
|
const settledAmount = (origin?.settlementAmount ?? origin?.receivedAmount ?? 0) + (settlementForm.amount || 0)
|
||||||
|
const pendingAfter = Math.max((origin?.amount || 0) - settledAmount, 0)
|
||||||
|
const targetStatus = pendingAfter === 0 ? '已结算' : '结算中'
|
||||||
|
await http.put('/contract', { contractId: settlementForm.contractId, contractStatus: targetStatus, type: origin?.type || '收入合同' })
|
||||||
|
showSettlementModal.value = false
|
||||||
|
search()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
Message.error(res.msg || '合同结算失败')
|
||||||
|
return false
|
||||||
|
} catch (e:any) {
|
||||||
|
Message.error(e?.message || '合同结算失败')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 数据状态
|
// 数据状态
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const dataList = ref<ContractItem[]>([])
|
const dataList = ref<ContractItem[]>([])
|
||||||
|
@ -235,7 +339,7 @@ const fetchContractList = async () => {
|
||||||
|
|
||||||
dataList.value = revenueContracts.map((item: ContractItem) => ({
|
dataList.value = revenueContracts.map((item: ContractItem) => ({
|
||||||
...item,
|
...item,
|
||||||
pendingAmount: (item.amount || 0) - (item.receivedAmount || 0),
|
pendingAmount: (item.amount || 0) - ((item.receivedAmount || item.settlementAmount || 0)),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
pagination.total = dataList.value.length
|
pagination.total = dataList.value.length
|
||||||
|
@ -263,11 +367,11 @@ const pagination = reactive({
|
||||||
// 获取状态颜色
|
// 获取状态颜色
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
const colorMap: Record<string, string> = {
|
const colorMap: Record<string, string> = {
|
||||||
未确认: 'gray',
|
未执行: 'gray',
|
||||||
待审批: 'orange',
|
|
||||||
已签署: 'blue',
|
|
||||||
执行中: 'cyan',
|
执行中: 'cyan',
|
||||||
已完成: 'green',
|
验收中: 'arcoblue',
|
||||||
|
结算中: 'orange',
|
||||||
|
已结算: 'green',
|
||||||
已终止: 'red',
|
已终止: 'red',
|
||||||
}
|
}
|
||||||
return colorMap[status] || 'gray'
|
return colorMap[status] || 'gray'
|
||||||
|
@ -318,7 +422,7 @@ const newContractData = ref<ContractItem>({
|
||||||
contractId: '', customer: '', code: '', projectId: '', type: '收入合同',
|
contractId: '', customer: '', code: '', projectId: '', type: '收入合同',
|
||||||
productService: '', paymentDate: null, performanceDeadline: null,
|
productService: '', paymentDate: null, performanceDeadline: null,
|
||||||
paymentAddress: '', amount: 0, accountNumber: '', notes: '',
|
paymentAddress: '', amount: 0, accountNumber: '', notes: '',
|
||||||
contractStatus: '未确认', contractText: '', projectName: '',
|
contractStatus: '未执行', contractText: '', projectName: '',
|
||||||
salespersonName: null, salespersonDeptName: '', settlementAmount: null,
|
salespersonName: null, salespersonDeptName: '', settlementAmount: null,
|
||||||
receivedAmount: null, contractStatusLabel: null, createBy: null, updateBy: null,
|
receivedAmount: null, contractStatusLabel: null, createBy: null, updateBy: null,
|
||||||
createTime: '', updateTime: '', page: 1, pageSize: 10, signDate: '', duration: '',
|
createTime: '', updateTime: '', page: 1, pageSize: 10, signDate: '', duration: '',
|
||||||
|
@ -329,7 +433,7 @@ const openAddModal = () => {
|
||||||
contractId: '', customer: '', code: '', projectId: '', type: '收入合同',
|
contractId: '', customer: '', code: '', projectId: '', type: '收入合同',
|
||||||
productService: '', paymentDate: null, performanceDeadline: null,
|
productService: '', paymentDate: null, performanceDeadline: null,
|
||||||
paymentAddress: '', amount: 0, accountNumber: '', notes: '',
|
paymentAddress: '', amount: 0, accountNumber: '', notes: '',
|
||||||
contractStatus: '未确认', contractText: '', projectName: '',
|
contractStatus: '未执行', contractText: '', projectName: '',
|
||||||
salespersonName: null, salespersonDeptName: '', settlementAmount: null,
|
salespersonName: null, salespersonDeptName: '', settlementAmount: null,
|
||||||
receivedAmount: null, contractStatusLabel: null, createBy: null, updateBy: null,
|
receivedAmount: null, contractStatusLabel: null, createBy: null, updateBy: null,
|
||||||
createTime: '', updateTime: '', page: 1, pageSize: 10, signDate: '', duration: '',
|
createTime: '', updateTime: '', page: 1, pageSize: 10, signDate: '', duration: '',
|
||||||
|
@ -347,7 +451,7 @@ const handleAddSubmit = async () => {
|
||||||
accountNumber: newContractData.value.accountNumber || '',
|
accountNumber: newContractData.value.accountNumber || '',
|
||||||
amount: newContractData.value.amount || 0,
|
amount: newContractData.value.amount || 0,
|
||||||
code: newContractData.value.code || '',
|
code: newContractData.value.code || '',
|
||||||
contractStatus: newContractData.value.contractStatus || '',
|
contractStatus: newContractData.value.contractStatus || '未执行',
|
||||||
contractText: newContractData.value.contractText || '',
|
contractText: newContractData.value.contractText || '',
|
||||||
customer: newContractData.value.customer || '',
|
customer: newContractData.value.customer || '',
|
||||||
departmentId: (newContractData.value as any).departmentId || '',
|
departmentId: (newContractData.value as any).departmentId || '',
|
||||||
|
@ -391,7 +495,7 @@ const editRecord = (record: ContractItem) => {
|
||||||
amount: record.amount || 0,
|
amount: record.amount || 0,
|
||||||
projectId: record.projectId || '',
|
projectId: record.projectId || '',
|
||||||
type: record.type || '收入合同',
|
type: record.type || '收入合同',
|
||||||
contractStatus: record.contractStatus || '未确认',
|
contractStatus: record.contractStatus || '未执行',
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedContractData.value = completeRecord
|
selectedContractData.value = completeRecord
|
||||||
|
@ -418,7 +522,7 @@ const handleEditSubmit = async () => {
|
||||||
amount: editedContractData.value.amount || 0,
|
amount: editedContractData.value.amount || 0,
|
||||||
code: editedContractData.value.code || '',
|
code: editedContractData.value.code || '',
|
||||||
contractId: editedContractData.value.contractId,
|
contractId: editedContractData.value.contractId,
|
||||||
contractStatus: editedContractData.value.contractStatus || '',
|
contractStatus: editedContractData.value.contractStatus || '未执行',
|
||||||
contractText: editedContractData.value.contractText || '',
|
contractText: editedContractData.value.contractText || '',
|
||||||
customer: editedContractData.value.customer || '',
|
customer: editedContractData.value.customer || '',
|
||||||
departmentId: editedContractData.value.departmentId || '',
|
departmentId: editedContractData.value.departmentId || '',
|
||||||
|
|
|
@ -1,11 +1,163 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
|
import type { TableColumnData } from '@arco-design/web-vue'
|
||||||
|
|
||||||
|
// 订单类型与状态
|
||||||
|
type OrderStatus = '待支付' | '进行中' | '已完成' | '已取消'
|
||||||
|
interface OrderItem { id: string; orderNo: string; customer: string; amount: number; status: OrderStatus; createTime: string }
|
||||||
|
const StatusOptions: { label: string; value: OrderStatus }[] = [
|
||||||
|
{ label: '待支付', value: '待支付' },
|
||||||
|
{ label: '进行中', value: '进行中' },
|
||||||
|
{ label: '已完成', value: '已完成' },
|
||||||
|
{ label: '已取消', value: '已取消' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 假数据源与统计
|
||||||
|
const allOrders = ref<OrderItem[]>([])
|
||||||
|
const stats = computed(() => ({
|
||||||
|
total: allOrders.value.length,
|
||||||
|
pending: allOrders.value.filter(o=>o.status==='待支付').length,
|
||||||
|
progress: allOrders.value.filter(o=>o.status==='进行中').length,
|
||||||
|
done: allOrders.value.filter(o=>o.status==='已完成').length,
|
||||||
|
cancel: allOrders.value.filter(o=>o.status==='已取消').length,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 统计卡片渲染数据(去趋势化)
|
||||||
|
const statCards = computed(() => [
|
||||||
|
{ key: 'total', title: '订单总数', value: stats.value.total, color: '#2f54eb', iconChar: 'T' },
|
||||||
|
{ key: 'pending', title: '待支付', value: stats.value.pending, color: '#faad14', iconChar: 'P' },
|
||||||
|
{ key: 'progress', title: '进行中', value: stats.value.progress, color: '#1677ff', iconChar: 'R' },
|
||||||
|
{ key: 'done', title: '已完成', value: stats.value.done, color: '#52c41a', iconChar: 'D' },
|
||||||
|
{ key: 'cancel', title: '已取消', value: stats.value.cancel, color: '#8c8c8c', iconChar: 'C' },
|
||||||
|
])
|
||||||
|
|
||||||
|
// 查询表单与分页
|
||||||
|
const searchForm = reactive({ orderNo: '', status: '' as ''|OrderStatus, timeRange: [] as [string,string]|[], page: 1, size: 10 })
|
||||||
|
const filtered = computed(() => {
|
||||||
|
let list = allOrders.value.slice()
|
||||||
|
if (searchForm.orderNo) list = list.filter(o=>o.orderNo.includes(searchForm.orderNo.trim()))
|
||||||
|
if (searchForm.status) list = list.filter(o=>o.status===searchForm.status)
|
||||||
|
if (Array.isArray(searchForm.timeRange) && searchForm.timeRange.length===2) {
|
||||||
|
const [s,e] = searchForm.timeRange; const S=+new Date(s as any), E=+new Date(e as any)
|
||||||
|
list = list.filter(o=>{ const t=+new Date(o.createTime as any); return t>=S && t<=E })
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
})
|
||||||
|
const paged = computed(() => {
|
||||||
|
const start = (searchForm.page-1)*searchForm.size
|
||||||
|
return filtered.value.slice(start, start+searchForm.size)
|
||||||
|
})
|
||||||
|
const pagination = reactive({ current: 1, pageSize: 10, total: 0, showTotal: true, showPageSize: true })
|
||||||
|
|
||||||
|
// 表格列
|
||||||
|
const columns: TableColumnData[] = [
|
||||||
|
{ title: '订单编号', dataIndex: 'orderNo', width: 180 },
|
||||||
|
{ title: '客户', dataIndex: 'customer', width: 160 },
|
||||||
|
{ title: '金额', dataIndex: 'amount', width: 120, render: ({record}:any)=>`¥${(record.amount||0).toLocaleString()}` },
|
||||||
|
{ title: '状态', dataIndex: 'status', width: 100, slotName: 'status' },
|
||||||
|
{ title: '下单时间', dataIndex: 'createTime', width: 180 },
|
||||||
|
{ title: '操作', slotName: 'action', width: 140, fixed: 'right' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 操作
|
||||||
|
const loading = ref(false)
|
||||||
|
const search = () => { pagination.total = filtered.value.length }
|
||||||
|
const reset = () => { Object.assign(searchForm, { orderNo: '', status: '', timeRange: [], page: 1, size: 10 }); pagination.current=1; pagination.pageSize=10; search() }
|
||||||
|
const onPageChange = (p:number)=>{ searchForm.page=p; pagination.current=p }
|
||||||
|
const onPageSizeChange=(s:number)=>{ searchForm.size=s; searchForm.page=1; pagination.pageSize=s; pagination.current=1; search() }
|
||||||
|
const getStatusColor = (s:OrderStatus)=>({ '待支付':'orange','进行中':'blue','已完成':'green','已取消':'gray' }[s])
|
||||||
|
|
||||||
|
// 新增订单(本地)
|
||||||
|
const showAdd = ref(false)
|
||||||
|
const newOrder = reactive<{orderNo:string;customer:string;amount:number;status:OrderStatus;createTime:string}>(
|
||||||
|
{ orderNo:'', customer:'', amount:0, status:'待支付', createTime:'' }
|
||||||
|
)
|
||||||
|
const openAdd = ()=>{ Object.assign(newOrder,{ orderNo:'',customer:'',amount:0,status:'待支付',createTime:new Date().toISOString().slice(0,19).replace('T',' ') }); showAdd.value=true }
|
||||||
|
const submitAdd = ()=>{
|
||||||
|
const id = Math.random().toString(36).slice(2)
|
||||||
|
allOrders.value.unshift({ id, ...newOrder })
|
||||||
|
showAdd.value=false; search()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成假数据
|
||||||
|
const genMock = (n=60)=>{
|
||||||
|
const pick=<T,>(arr:T[])=>arr[Math.floor(Math.random()*arr.length)]
|
||||||
|
const now=Date.now()
|
||||||
|
return Array.from({length:n}).map((_,i)=>{
|
||||||
|
const offset = Math.floor(Math.random()*60)*86400000
|
||||||
|
const dt = new Date(now-offset).toISOString().slice(0,19).replace('T',' ')
|
||||||
|
const st = pick<OrderStatus>(['待支付','进行中','已完成','已取消'])
|
||||||
|
return { id:`O${i+1}`, orderNo:`NO${100000+i}`, customer:`客户${(i%20)+1}`, amount: Math.floor(Math.random()*50000)+1000, status: st, createTime: dt }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onMounted(()=>{ allOrders.value = genMock(88); search() })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<GiPageLayout>
|
||||||
|
<!-- 顶部统计(美化 + 居中 + 独立容器) -->
|
||||||
|
<a-row :gutter="12" class="mb-3 stats-row">
|
||||||
|
<a-col :span="24">
|
||||||
|
<div class="stats-wrap">
|
||||||
|
<a-row :gutter="12" justify="center">
|
||||||
|
<a-col v-for="card in statCards" :key="card.key" :xs="12" :sm="12" :md="6" :lg="4">
|
||||||
|
<a-card hoverable class="stat-card">
|
||||||
|
<div class="stat-inner">
|
||||||
|
<div class="stat-icon" :style="{ background: card.color }">
|
||||||
|
<span>{{ card.iconChar }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value">{{ card.value }}</div>
|
||||||
|
<div class="stat-title">{{ card.title }}</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<!-- 查询表单 + 新增按钮 -->
|
||||||
|
<GiForm :columns="[
|
||||||
|
{ field:'orderNo', label:'订单编号', type:'input', props:{placeholder:'支持模糊查询'} },
|
||||||
|
{ field:'status', label:'订单状态', type:'select', props:{ placeholder:'全部', options: StatusOptions } },
|
||||||
|
{ field:'timeRange', label:'订单时间', type:'range-picker', props:{ showTime:true, format:'YYYY-MM-DD HH:mm:ss' } },
|
||||||
|
]" v-model="searchForm" search size="medium" @search="search" @reset="reset">
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="primary" @click="openAdd"><icon-plus/> 新增订单</a-button>
|
||||||
|
</template>
|
||||||
|
</GiForm>
|
||||||
|
|
||||||
|
<!-- 订单列表 -->
|
||||||
|
<GiTable :data="paged" :columns="columns" :loading="loading" :pagination="pagination"
|
||||||
|
@page-change="onPageChange" @page-size-change="onPageSizeChange" @refresh="search">
|
||||||
|
<template #status="{ record }"><a-tag :color="getStatusColor(record.status)">{{ record.status }}</a-tag></template>
|
||||||
|
<template #action="{ record }"><a-space>
|
||||||
|
<a-link @click="()=>{}">查看</a-link>
|
||||||
|
<a-link @click="()=>{}">编辑</a-link>
|
||||||
|
</a-space></template>
|
||||||
|
</GiTable>
|
||||||
|
|
||||||
|
<!-- 新增订单弹窗(本地) -->
|
||||||
|
<a-modal v-model:visible="showAdd" title="新增订单" :width="520" @before-ok="submitAdd">
|
||||||
|
<a-form :model="newOrder" layout="vertical">
|
||||||
|
<a-form-item field="orderNo" label="订单编号"><a-input v-model="newOrder.orderNo" placeholder="NO123456"/></a-form-item>
|
||||||
|
<a-form-item field="customer" label="客户"><a-input v-model="newOrder.customer"/></a-form-item>
|
||||||
|
<a-form-item field="amount" label="金额"><a-input-number v-model="newOrder.amount" style="width:100%"/></a-form-item>
|
||||||
|
<a-form-item field="status" label="状态"><a-select v-model="newOrder.status" :options="StatusOptions"/></a-form-item>
|
||||||
|
<a-form-item field="createTime" label="下单时间"><a-date-picker v-model="newOrder.createTime" show-time style="width:100%"/></a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</GiPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped>
|
||||||
|
.mb-3{ margin-bottom:12px; }
|
||||||
|
.stats-row{ }
|
||||||
|
.stats-wrap{ max-width: 1200px; margin: 0 auto; }
|
||||||
|
.stat-card{ border: 1px solid var(--color-border-2,#f0f0f0); background: linear-gradient(180deg,#ffffff 0%, #fafbff 100%); }
|
||||||
|
.stat-inner{ display:flex; flex-direction:column; align-items:center; justify-content:center; padding:18px 10px; text-align:center; }
|
||||||
|
.stat-icon{ width:44px; height:44px; border-radius:12px; color:#fff; display:flex; align-items:center; justify-content:center; font-weight:700; margin-bottom:8px; box-shadow: 0 6px 16px rgba(0,0,0,0.08); }
|
||||||
|
.stat-value{ font-size:28px; font-weight:700; color:#1d2129; line-height:1.2; }
|
||||||
|
.stat-title{ margin-top:6px; color:#86909c; }
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -41,20 +41,6 @@
|
||||||
<!-- 简洁搜索区域 -->
|
<!-- 简洁搜索区域 -->
|
||||||
<div class="search-section">
|
<div class="search-section">
|
||||||
<a-card :bordered="false" class="search-card">
|
<a-card :bordered="false" class="search-card">
|
||||||
<!-- 搜索状态提示 -->
|
|
||||||
<div v-if="hasActiveFilters" class="search-status">
|
|
||||||
<icon-info-circle style="color: #667eea; margin-right: 8px;" />
|
|
||||||
<span>当前应用了搜索筛选条件</span>
|
|
||||||
<a-button
|
|
||||||
type="text"
|
|
||||||
size="small"
|
|
||||||
@click="handleReset"
|
|
||||||
style="margin-left: 8px; color: #667eea;"
|
|
||||||
>
|
|
||||||
清除所有筛选
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-form layout="inline" :model="searchForm">
|
<a-form layout="inline" :model="searchForm">
|
||||||
<a-form-item label="姓名">
|
<a-form-item label="姓名">
|
||||||
<a-input
|
<a-input
|
||||||
|
@ -63,7 +49,6 @@
|
||||||
style="width: 180px"
|
style="width: 180px"
|
||||||
allow-clear
|
allow-clear
|
||||||
@press-enter="handleSearch"
|
@press-enter="handleSearch"
|
||||||
@clear="handleSearch"
|
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="项目岗位">
|
<a-form-item label="项目岗位">
|
||||||
|
@ -72,7 +57,6 @@
|
||||||
placeholder="请选择项目岗位"
|
placeholder="请选择项目岗位"
|
||||||
style="width: 150px"
|
style="width: 150px"
|
||||||
allow-clear
|
allow-clear
|
||||||
@change="handleSearch"
|
|
||||||
>
|
>
|
||||||
<a-option
|
<a-option
|
||||||
v-for="option in ROLE_TYPE_OPTIONS"
|
v-for="option in ROLE_TYPE_OPTIONS"
|
||||||
|
@ -89,7 +73,6 @@
|
||||||
placeholder="请选择状态"
|
placeholder="请选择状态"
|
||||||
style="width: 120px"
|
style="width: 120px"
|
||||||
allow-clear
|
allow-clear
|
||||||
@change="handleSearch"
|
|
||||||
>
|
>
|
||||||
<a-option
|
<a-option
|
||||||
v-for="option in STATUS_OPTIONS"
|
v-for="option in STATUS_OPTIONS"
|
||||||
|
@ -102,12 +85,7 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button
|
<a-button type="primary" @click="handleSearch">
|
||||||
type="primary"
|
|
||||||
@click="handleSearch"
|
|
||||||
:loading="loading"
|
|
||||||
:disabled="!hasActiveFilters"
|
|
||||||
>
|
|
||||||
<template #icon><icon-search /></template>
|
<template #icon><icon-search /></template>
|
||||||
搜索
|
搜索
|
||||||
</a-button>
|
</a-button>
|
||||||
|
@ -121,7 +99,6 @@
|
||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- 数据表格 -->
|
<!-- 数据表格 -->
|
||||||
<GiTable
|
<GiTable
|
||||||
row-key="id"
|
row-key="id"
|
||||||
|
@ -135,23 +112,6 @@
|
||||||
@page-size-change="onPageSizeChange"
|
@page-size-change="onPageSizeChange"
|
||||||
@refresh="loadData"
|
@refresh="loadData"
|
||||||
>
|
>
|
||||||
<!-- 空状态显示 -->
|
|
||||||
<template #empty>
|
|
||||||
<div class="empty-state">
|
|
||||||
<icon-user-group style="font-size: 48px; color: #c9cdd4; margin-bottom: 16px;" />
|
|
||||||
<div class="empty-text">
|
|
||||||
<h3>{{ hasActiveFilters ? '没有找到匹配的团队成员' : '暂无团队成员数据' }}</h3>
|
|
||||||
<p v-if="hasActiveFilters">
|
|
||||||
请尝试调整搜索条件或
|
|
||||||
<a-link @click="handleReset">清除所有筛选</a-link>
|
|
||||||
</p>
|
|
||||||
<p v-else>
|
|
||||||
点击右上角的"新增成员"按钮来添加第一个团队成员
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 项目岗位列 -->
|
<!-- 项目岗位列 -->
|
||||||
<template #roleType="{ record }">
|
<template #roleType="{ record }">
|
||||||
<span class="role-type-text">
|
<span class="role-type-text">
|
||||||
|
@ -161,9 +121,9 @@
|
||||||
|
|
||||||
<!-- 状态列 -->
|
<!-- 状态列 -->
|
||||||
<template #status="{ record }">
|
<template #status="{ record }">
|
||||||
<span class="status-tag" :class="`status-${record.status || 'INACTIVE'}`">
|
<a-tag :color="getStatusColor(record.status || 'INACTIVE')">
|
||||||
{{ getStatusText(record.status || 'INACTIVE') }}
|
{{ getStatusText(record.status || 'INACTIVE') }}
|
||||||
</span>
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
@ -380,7 +340,7 @@ const ROLE_TYPE_OPTIONS = [
|
||||||
|
|
||||||
const STATUS_OPTIONS = [
|
const STATUS_OPTIONS = [
|
||||||
{ label: '可用', value: 'ACTIVE' },
|
{ label: '可用', value: 'ACTIVE' },
|
||||||
{ label: '忙碌', value: 'SUSPENDED' },
|
{ label: '忙碌', value: 'SUSPENDEN' },
|
||||||
{ label: '离线', value: 'INACTIVE' }
|
{ label: '离线', value: 'INACTIVE' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -441,7 +401,7 @@ const statusForm = reactive<{
|
||||||
id: string | number
|
id: string | number
|
||||||
name: string
|
name: string
|
||||||
currentStatus: string
|
currentStatus: string
|
||||||
newStatus: 'ACTIVE' | 'SUSPENDED' | 'INACTIVE'
|
newStatus: 'ACTIVE' | 'SUSPENDEN' | 'INACTIVE'
|
||||||
}>({
|
}>({
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -463,80 +423,56 @@ const loadData = async () => {
|
||||||
try {
|
try {
|
||||||
console.log('正在加载项目团队成员数据,项目ID:', projectId)
|
console.log('正在加载项目团队成员数据,项目ID:', projectId)
|
||||||
|
|
||||||
// 构建查询参数,字段名要与后端接口匹配
|
// 构建查询参数
|
||||||
const queryParams: TeamMemberQuery = {
|
const queryParams: TeamMemberQuery = {
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
page: pagination.current,
|
page: pagination.current,
|
||||||
pageSize: pagination.pageSize,
|
pageSize: pagination.pageSize,
|
||||||
name: searchForm.name || undefined,
|
name: searchForm.name || undefined,
|
||||||
position: searchForm.roleType || undefined, // 后端使用position字段
|
position: searchForm.roleType || undefined,
|
||||||
status: searchForm.status || undefined
|
status: searchForm.status || undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('查询参数:', queryParams)
|
|
||||||
|
|
||||||
const response = await getProjectTeamMembers(queryParams)
|
const response = await getProjectTeamMembers(queryParams)
|
||||||
|
|
||||||
console.log('API响应数据:', response)
|
console.log('API响应数据:', response.data)
|
||||||
|
|
||||||
// 处理后端返回的数据
|
// 确保response.data是数组
|
||||||
let mappedData: TeamMemberResp[] = []
|
const rawData = Array.isArray(response.data) ? response.data : [response.data]
|
||||||
let total = 0
|
|
||||||
|
|
||||||
if (response.data) {
|
console.log('处理后的原始数据:', rawData)
|
||||||
// 如果后端返回的是分页数据
|
|
||||||
if (response.data.list && Array.isArray(response.data.list)) {
|
// 处理后端返回的数据,将后端字段映射到前端期望的字段
|
||||||
mappedData = response.data.list.map((item: BackendTeamMemberResp) => {
|
const mappedData = rawData.map((item: BackendTeamMemberResp) => {
|
||||||
return {
|
const mappedItem: TeamMemberResp = {
|
||||||
id: item.memberId,
|
id: item.memberId,
|
||||||
name: item.name || '',
|
name: item.name || '',
|
||||||
phone: item.phone || '',
|
phone: item.phone || '',
|
||||||
email: item.email || '',
|
email: item.email || '',
|
||||||
roleType: item.roleType || item.position || '', // 优先使用roleType,fallback到position
|
roleType: item.roleType || '', // 映射项目岗位
|
||||||
status: (item.status === 'ACTIVE' ? 'ACTIVE' : item.status === 'SUSPENDED' ? 'SUSPENDED' : 'INACTIVE') as 'ACTIVE' | 'SUSPENDED' | 'INACTIVE',
|
status: (item.status === 'ACTIVE' ? 'ACTIVE' : item.status === 'SUSPENDEN' ? 'SUSPENDEN' : 'INACTIVE') as 'ACTIVE' | 'SUSPENDEN' | 'INACTIVE',
|
||||||
joinDate: item.joinDate || '',
|
joinDate: item.joinDate || '',
|
||||||
remark: item.remark || '',
|
remark: item.remark || '',
|
||||||
avatar: item.userAvatar || ''
|
avatar: item.userAvatar || ''
|
||||||
}
|
}
|
||||||
})
|
console.log('映射后的数据项:', mappedItem)
|
||||||
total = response.data.total || mappedData.length
|
return mappedItem
|
||||||
}
|
})
|
||||||
// 如果后端直接返回数组
|
|
||||||
else if (Array.isArray(response.data)) {
|
|
||||||
mappedData = response.data.map((item: BackendTeamMemberResp) => {
|
|
||||||
return {
|
|
||||||
id: item.memberId,
|
|
||||||
name: item.name || '',
|
|
||||||
phone: item.phone || '',
|
|
||||||
email: item.email || '',
|
|
||||||
roleType: item.roleType || item.position || '',
|
|
||||||
status: (item.status === 'ACTIVE' ? 'ACTIVE' : item.status === 'SUSPENDED' ? 'SUSPENDED' : 'INACTIVE') as 'ACTIVE' | 'SUSPENDED' | 'INACTIVE',
|
|
||||||
joinDate: item.joinDate || '',
|
|
||||||
remark: item.remark || '',
|
|
||||||
avatar: item.userAvatar || ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
total = mappedData.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dataList.value = mappedData
|
dataList.value = mappedData
|
||||||
pagination.total = total
|
pagination.total = mappedData.length
|
||||||
|
|
||||||
console.log('团队成员数据加载完成,显示数据:', dataList.value.length, '条,总计:', pagination.total, '条')
|
console.log('团队成员数据加载完成,显示数据:', dataList.value.length, '条,总计:', pagination.total, '条')
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('团队成员数据加载失败:', error)
|
console.error('团队成员数据加载失败:', error)
|
||||||
Message.error('团队成员数据加载失败')
|
Message.error('团队成员数据加载失败')
|
||||||
dataList.value = []
|
|
||||||
pagination.total = 0
|
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
console.log('执行搜索,搜索条件:', searchForm)
|
|
||||||
pagination.current = 1
|
pagination.current = 1
|
||||||
await loadData()
|
await loadData()
|
||||||
}
|
}
|
||||||
|
@ -549,18 +485,15 @@ const handleReset = () => {
|
||||||
status: ''
|
status: ''
|
||||||
})
|
})
|
||||||
pagination.current = 1
|
pagination.current = 1
|
||||||
// 重置后自动加载数据
|
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPageChange = (page: number) => {
|
const onPageChange = (page: number) => {
|
||||||
console.log('页码变化:', page)
|
|
||||||
pagination.current = page
|
pagination.current = page
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPageSizeChange = (pageSize: number) => {
|
const onPageSizeChange = (pageSize: number) => {
|
||||||
console.log('每页大小变化:', pageSize)
|
|
||||||
pagination.pageSize = pageSize
|
pagination.pageSize = pageSize
|
||||||
pagination.current = 1
|
pagination.current = 1
|
||||||
loadData()
|
loadData()
|
||||||
|
@ -664,7 +597,7 @@ const openStatusModal = (record: TeamMemberResp) => {
|
||||||
const saveStatus = async () => {
|
const saveStatus = async () => {
|
||||||
try {
|
try {
|
||||||
await updateTeamMember(statusForm.id, {
|
await updateTeamMember(statusForm.id, {
|
||||||
status: statusForm.newStatus as 'ACTIVE' | 'SUSPENDED' | 'INACTIVE'
|
status: statusForm.newStatus as 'ACTIVE' | 'SUSPENDEN' | 'INACTIVE'
|
||||||
})
|
})
|
||||||
Message.success('状态更新成功')
|
Message.success('状态更新成功')
|
||||||
statusModalVisible.value = false
|
statusModalVisible.value = false
|
||||||
|
@ -783,7 +716,7 @@ const getRoleTypeText = (roleType: string) => {
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
const colorMap: Record<string, string> = {
|
const colorMap: Record<string, string> = {
|
||||||
ACTIVE: 'success',
|
ACTIVE: 'success',
|
||||||
SUSPENDED: 'warning',
|
SUSPENDEN: 'warning',
|
||||||
INACTIVE: 'danger'
|
INACTIVE: 'danger'
|
||||||
}
|
}
|
||||||
return colorMap[status] || 'danger'
|
return colorMap[status] || 'danger'
|
||||||
|
@ -792,17 +725,12 @@ const getStatusColor = (status: string) => {
|
||||||
const getStatusText = (status: string) => {
|
const getStatusText = (status: string) => {
|
||||||
const textMap: Record<string, string> = {
|
const textMap: Record<string, string> = {
|
||||||
ACTIVE: '可用',
|
ACTIVE: '可用',
|
||||||
SUSPENDED: '忙碌',
|
SUSPENDEN: '忙碌',
|
||||||
INACTIVE: '离线'
|
INACTIVE: '离线'
|
||||||
}
|
}
|
||||||
return textMap[status] || '未知'
|
return textMap[status] || '未知'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算属性:判断是否有激活的搜索条件
|
|
||||||
const hasActiveFilters = computed(() => {
|
|
||||||
return searchForm.name || searchForm.roleType || searchForm.status
|
|
||||||
})
|
|
||||||
|
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log('团队成员管理页面加载,项目ID:', projectId)
|
console.log('团队成员管理页面加载,项目ID:', projectId)
|
||||||
|
@ -1011,29 +939,6 @@ onMounted(() => {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索状态提示样式
|
|
||||||
.search-status {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
background: rgba(102, 126, 234, 0.1);
|
|
||||||
border-radius: 8px;
|
|
||||||
border-left: 4px solid #667eea;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #4e5969;
|
|
||||||
|
|
||||||
.arco-btn {
|
|
||||||
padding: 2px 8px;
|
|
||||||
height: auto;
|
|
||||||
line-height: 1.4;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(102, 126, 234, 0.2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.arco-form-item) {
|
:deep(.arco-form-item) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
@ -1064,7 +969,7 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.arco-btn) {
|
:deep(.arco-btn) {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
@ -1072,78 +977,87 @@ onMounted(() => {
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
opacity: 0.6;
|
|
||||||
cursor: not-allowed;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 搜索结果统计样式
|
// 表格区域样式
|
||||||
.search-results-info {
|
:deep(.gi-table) {
|
||||||
margin-bottom: 16px;
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.results-card {
|
.arco-table-container {
|
||||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%);
|
overflow-x: auto;
|
||||||
border-radius: 12px;
|
overflow-y: auto;
|
||||||
box-shadow: 0 2px 12px rgba(102, 126, 234, 0.1);
|
}
|
||||||
border: 1px solid rgba(102, 126, 234, 0.1);
|
|
||||||
|
.arco-table {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-table-body {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格头部样式
|
||||||
|
.arco-table-thead {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
|
||||||
:deep(.arco-card-body) {
|
.arco-table-th {
|
||||||
padding: 16px 20px;
|
background: transparent !important;
|
||||||
}
|
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
|
||||||
.results-summary {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #4e5969;
|
|
||||||
|
|
||||||
strong {
|
.arco-table-th-item-title {
|
||||||
color: #667eea;
|
color: white !important;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 表格行样式
|
||||||
|
.arco-table-tbody {
|
||||||
|
.arco-table-tr {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-table-td {
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
padding: 16px 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 斑马纹效果
|
||||||
|
.arco-table-tr:nth-child(even) {
|
||||||
|
background: #fafbfc;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 固定列样式
|
||||||
|
.arco-table-fixed-left,
|
||||||
|
.arco-table-fixed-right {
|
||||||
|
.arco-table-td {
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 空状态样式
|
|
||||||
.empty-state {
|
|
||||||
text-align: center;
|
|
||||||
padding: 60px 20px;
|
|
||||||
color: #c9cdd4;
|
|
||||||
|
|
||||||
.empty-text {
|
|
||||||
h3 {
|
|
||||||
margin: 0 0 12px 0;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #4e5969;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 8px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.6;
|
|
||||||
|
|
||||||
.arco-link {
|
|
||||||
color: #667eea;
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #4c5fd9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.remark-content {
|
.remark-content {
|
||||||
max-width: 180px;
|
max-width: 180px;
|
||||||
|
@ -1196,58 +1110,11 @@ onMounted(() => {
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: scale(1.05);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
|
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 状态标签样式
|
|
||||||
.status-tag {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
min-width: 60px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.status-ACTIVE {
|
|
||||||
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 4px 16px rgba(82, 196, 26, 0.4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.status-SUSPENDED {
|
|
||||||
background: linear-gradient(135deg, #faad14 0%, #ffc53d 100%);
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 2px 8px rgba(250, 173, 20, 0.3);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 4px 16px rgba(250, 173, 20, 0.4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.status-INACTIVE {
|
|
||||||
background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%);
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 4px 16px rgba(255, 77, 79, 0.4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.member-form {
|
.member-form {
|
||||||
.form-row {
|
.form-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -1440,80 +1307,4 @@ onMounted(() => {
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表格区域样式
|
|
||||||
:deep(.gi-table) {
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
|
||||||
margin-bottom: 24px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.arco-table-container {
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arco-table {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arco-table-body {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表格头部样式
|
|
||||||
.arco-table-thead {
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
|
|
||||||
.arco-table-th {
|
|
||||||
background: transparent !important;
|
|
||||||
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
|
|
||||||
|
|
||||||
.arco-table-th-item-title {
|
|
||||||
color: white !important;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表格行样式
|
|
||||||
.arco-table-tbody {
|
|
||||||
.arco-table-tr {
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.arco-table-td {
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
padding: 16px 12px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 斑马纹效果
|
|
||||||
.arco-table-tr:nth-child(even) {
|
|
||||||
background: #fafbfc;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 固定列样式
|
|
||||||
.arco-table-fixed-left,
|
|
||||||
.arco-table-fixed-right {
|
|
||||||
.arco-table-td {
|
|
||||||
background: white;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue