feat 搭建架构骨架
This commit is contained in:
214
src/views/project/detail.vue
Normal file
214
src/views/project/detail.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ConfirmDialog } from '@/components'
|
||||
import type { FileInfo } from '@/types/api'
|
||||
// 页面容器:数据只经 store,不直接 import api
|
||||
import { useProjectStore } from '@/stores/modules/project'
|
||||
import ProjectInfoCard from './cpns/ProjectInfoCard.vue'
|
||||
import ProjectTeam from './cpns/ProjectTeam.vue'
|
||||
import ProjectMilestones from './cpns/ProjectMilestones.vue'
|
||||
import ProjectFiles from './cpns/ProjectFiles.vue'
|
||||
import ProjectFinance from './cpns/ProjectFinance.vue'
|
||||
import ProjectAfterSales from './cpns/ProjectAfterSales.vue'
|
||||
|
||||
type DetailTab = 'progress' | 'files' | 'finance' | 'afterSales'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const projectStore = useProjectStore()
|
||||
const { current } = storeToRefs(projectStore)
|
||||
|
||||
const projectId = computed(() => String(route.params.id))
|
||||
const activeTab = ref<DetailTab>('progress')
|
||||
const cancelVisible = ref(false)
|
||||
|
||||
const tabs: Array<{ label: string; value: DetailTab }> = [
|
||||
{ label: '项目进度', value: 'progress' },
|
||||
{ label: '项目文件', value: 'files' },
|
||||
{ label: '合同财务', value: 'finance' },
|
||||
{ label: '售后信息', value: 'afterSales' },
|
||||
]
|
||||
|
||||
function load(): void {
|
||||
void projectStore.fetchDetail(projectId.value)
|
||||
}
|
||||
|
||||
function onUpload(file: File): void {
|
||||
void projectStore.uploadFile(file).then(() => {
|
||||
ElMessage.success('文件已上传')
|
||||
})
|
||||
}
|
||||
|
||||
function onDeleteFile(index: number): void {
|
||||
void projectStore.deleteFile(index).then(() => {
|
||||
ElMessage.success('文件已删除')
|
||||
})
|
||||
}
|
||||
|
||||
function onPreviewFile(): void {
|
||||
// TODO(api): 文件预览接口
|
||||
ElMessage.info('文件预览待接口接入')
|
||||
}
|
||||
|
||||
function onDownloadFile(): void {
|
||||
// TODO(api): 文件下载接口
|
||||
ElMessage.info('文件下载待接口接入')
|
||||
}
|
||||
|
||||
function onPay(): void {
|
||||
// TODO: 跳财务中心 `/finance/bills`
|
||||
ElMessage.info('请前往财务中心完成支付')
|
||||
}
|
||||
|
||||
function onFinancePreview(file: FileInfo): void {
|
||||
// TODO(api): 发票/合同文件预览接口
|
||||
ElMessage.info(`「${file.name}」预览待接口接入`)
|
||||
}
|
||||
|
||||
function onFinanceDownload(file: FileInfo): void {
|
||||
// TODO(api): 发票/合同文件下载接口
|
||||
ElMessage.info(`「${file.name}」下载待接口接入`)
|
||||
}
|
||||
|
||||
function onApplyAfterSales(): void {
|
||||
void router.push(`/project/${projectId.value}/after-sales`)
|
||||
}
|
||||
|
||||
function onConfirmCancel(): void {
|
||||
void projectStore.cancelAfterSales()
|
||||
}
|
||||
|
||||
watch(projectId, load)
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="project-detail">
|
||||
<nav class="project-detail__breadcrumb text-secondary">
|
||||
<RouterLink to="/project" class="link">
|
||||
项目中心
|
||||
</RouterLink>
|
||||
<span> > </span>
|
||||
<span>项目详情</span>
|
||||
</nav>
|
||||
|
||||
<template v-if="current">
|
||||
<ProjectInfoCard :project="current" />
|
||||
<ProjectTeam :team="current.team" />
|
||||
|
||||
<!-- 本地 Tab 切换 -->
|
||||
<div class="project-detail__tabs">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
class="project-detail__tab"
|
||||
:class="{ 'is-active': activeTab === tab.value }"
|
||||
@click="activeTab = tab.value"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ProjectMilestones
|
||||
v-if="activeTab === 'progress'"
|
||||
:progress="current.progress"
|
||||
:milestones="current.milestones"
|
||||
:start-time="current.startTime"
|
||||
:end-time="current.endTime"
|
||||
/>
|
||||
|
||||
<ProjectFiles
|
||||
v-if="activeTab === 'files'"
|
||||
:files="current.files"
|
||||
@upload="onUpload"
|
||||
@delete="onDeleteFile"
|
||||
@preview="onPreviewFile"
|
||||
@download="onDownloadFile"
|
||||
/>
|
||||
|
||||
<ProjectFinance
|
||||
v-if="activeTab === 'finance'"
|
||||
:payment-plans="current.paymentPlans"
|
||||
:invoice-records="current.invoiceRecords"
|
||||
:contract-file="current.contractFile"
|
||||
@pay="onPay"
|
||||
@preview="onFinancePreview"
|
||||
@download="onFinanceDownload"
|
||||
/>
|
||||
|
||||
<ProjectAfterSales
|
||||
v-if="activeTab === 'afterSales'"
|
||||
:after-sales="current.afterSales"
|
||||
:project-id="current.id"
|
||||
@apply="onApplyAfterSales"
|
||||
@cancel="cancelVisible = true"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<p v-else class="project-detail__empty text-secondary">
|
||||
项目不存在或已取消
|
||||
</p>
|
||||
|
||||
<ConfirmDialog
|
||||
v-model:visible="cancelVisible"
|
||||
title="取消售后"
|
||||
message="确定取消当前售后工单吗?"
|
||||
confirm-text="确定取消"
|
||||
danger
|
||||
@confirm="onConfirmCancel"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.project-detail {
|
||||
.flex(column, flex-start);
|
||||
gap: @spacing-base;
|
||||
padding-top: @spacing-lg;
|
||||
padding-bottom: @spacing-xxl;
|
||||
|
||||
&__breadcrumb {
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
.flex(row, flex-start);
|
||||
gap: @spacing-xs;
|
||||
padding: @spacing-xs;
|
||||
border-radius: @radius-lg;
|
||||
background: @white;
|
||||
box-shadow: @shadow-base;
|
||||
}
|
||||
|
||||
&__tab {
|
||||
height: 34px;
|
||||
padding: 0 @spacing-base;
|
||||
border: none;
|
||||
border-radius: @radius-base;
|
||||
background: transparent;
|
||||
color: @text-color;
|
||||
font-size: @font-size-base;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background: @primary-light;
|
||||
color: @primary-color;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&__empty {
|
||||
padding: @spacing-xxl 0;
|
||||
text-align: center;
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user