feat 搭建架构骨架
This commit is contained in:
275
src/views/project/cpns/ProjectFinance.vue
Normal file
275
src/views/project/cpns/ProjectFinance.vue
Normal file
@@ -0,0 +1,275 @@
|
||||
<script setup lang="ts">
|
||||
import { StatusTag } from '@/components'
|
||||
import type { FileInfo } from '@/types/api'
|
||||
import type { ProjectBill } from '@/types/business/project'
|
||||
import { formatFileSize, formatMoney } from '@/utils/format'
|
||||
|
||||
interface Props {
|
||||
paymentPlans: ProjectBill[]
|
||||
invoiceRecords: FileInfo[]
|
||||
contractFile?: FileInfo
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pay: [billId: string]
|
||||
preview: [file: FileInfo]
|
||||
download: [file: FileInfo]
|
||||
}>()
|
||||
|
||||
const BILL_VARIANTS: Record<ProjectBill['status'], 'primary' | 'success' | 'warning' | 'danger' | 'info'> = {
|
||||
'已支付': 'success',
|
||||
'待支付': 'warning',
|
||||
'未到期': 'info',
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="project-finance">
|
||||
<!-- 付款计划 -->
|
||||
<h2 class="project-finance__title">
|
||||
付款计划
|
||||
</h2>
|
||||
<div class="project-finance__plans">
|
||||
<article
|
||||
v-for="bill in paymentPlans"
|
||||
:key="bill.id"
|
||||
class="project-finance__plan"
|
||||
>
|
||||
<div class="project-finance__plan-head">
|
||||
<span class="project-finance__plan-name">{{ bill.planName }}</span>
|
||||
<StatusTag :label="bill.status" :variant="BILL_VARIANTS[bill.status]" />
|
||||
</div>
|
||||
<div class="project-finance__plan-meta">
|
||||
<div class="project-finance__field">
|
||||
<span class="project-finance__label text-secondary">关联项目</span>
|
||||
<span class="project-finance__value">{{ bill.projectName }}</span>
|
||||
</div>
|
||||
<div class="project-finance__field">
|
||||
<span class="project-finance__label text-secondary">账单号</span>
|
||||
<span class="project-finance__value">{{ bill.billNo }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-finance__amount">
|
||||
¥{{ formatMoney(bill.amount) }}
|
||||
</div>
|
||||
<div class="project-finance__plan-foot">
|
||||
<span v-if="bill.status === '已支付' && bill.tradeNo" class="project-finance__trade text-secondary">
|
||||
交易流水号:{{ bill.tradeNo }}
|
||||
</span>
|
||||
<button
|
||||
v-if="bill.status === '待支付'"
|
||||
class="project-finance__pay"
|
||||
@click="emit('pay', bill.id)"
|
||||
>
|
||||
立即支付
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
<p v-if="paymentPlans.length === 0" class="project-finance__empty text-secondary">
|
||||
暂无付款计划
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 发票记录 -->
|
||||
<h2 class="project-finance__title">
|
||||
发票记录
|
||||
</h2>
|
||||
<ul class="project-finance__files">
|
||||
<li
|
||||
v-for="(file, index) in invoiceRecords"
|
||||
:key="`invoice-${index}`"
|
||||
class="project-finance__file"
|
||||
>
|
||||
<div class="project-finance__file-info">
|
||||
<span class="project-finance__file-name" :title="file.name">{{ file.name }}</span>
|
||||
<span class="project-finance__file-meta text-secondary">
|
||||
{{ formatFileSize(file.size) }} · {{ file.uploadTime }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="project-finance__file-actions">
|
||||
<button class="project-finance__link" @click="emit('preview', file)">
|
||||
预览
|
||||
</button>
|
||||
<button class="project-finance__link" @click="emit('download', file)">
|
||||
下载
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li v-if="invoiceRecords.length === 0" class="project-finance__empty text-secondary">
|
||||
暂无发票记录
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 合同文件 -->
|
||||
<h2 class="project-finance__title">
|
||||
合同文件
|
||||
</h2>
|
||||
<div v-if="contractFile" class="project-finance__contract">
|
||||
<div class="project-finance__file-info">
|
||||
<span class="project-finance__file-name" :title="contractFile.name">{{ contractFile.name }}</span>
|
||||
<span class="project-finance__file-meta text-secondary">
|
||||
{{ formatFileSize(contractFile.size) }} · {{ contractFile.uploadTime }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="project-finance__file-actions">
|
||||
<button class="project-finance__link" @click="emit('preview', contractFile)">
|
||||
预览
|
||||
</button>
|
||||
<button class="project-finance__link" @click="emit('download', contractFile)">
|
||||
下载
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="project-finance__empty text-secondary">
|
||||
暂无合同文件
|
||||
</p>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.project-finance {
|
||||
.flex(column, flex-start);
|
||||
gap: @spacing-base;
|
||||
padding: @spacing-lg @spacing-xl;
|
||||
border-radius: @radius-lg;
|
||||
background: @white;
|
||||
box-shadow: @shadow-base;
|
||||
|
||||
&__title {
|
||||
font-size: @font-size-lg;
|
||||
font-weight: 600;
|
||||
color: @heading-color;
|
||||
}
|
||||
|
||||
&__plans {
|
||||
.flex(row, flex-start);
|
||||
flex-wrap: wrap;
|
||||
gap: @spacing-base;
|
||||
}
|
||||
|
||||
&__plan {
|
||||
.flex(column, flex-start);
|
||||
gap: @spacing-sm;
|
||||
width: 320px;
|
||||
padding: @spacing-base;
|
||||
border: 1px solid @border-color;
|
||||
border-radius: @radius-base;
|
||||
background: @bg-color-light;
|
||||
}
|
||||
|
||||
&__plan-head {
|
||||
.flex(row, space-between, center);
|
||||
gap: @spacing-sm;
|
||||
}
|
||||
|
||||
&__plan-name {
|
||||
font-size: @font-size-base;
|
||||
font-weight: 600;
|
||||
color: @heading-color;
|
||||
}
|
||||
|
||||
&__plan-meta {
|
||||
.flex(column, flex-start);
|
||||
gap: @spacing-xs;
|
||||
}
|
||||
|
||||
&__field {
|
||||
.flex(row, flex-start);
|
||||
gap: @spacing-sm;
|
||||
font-size: @font-size-sm;
|
||||
}
|
||||
|
||||
&__label {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__value {
|
||||
color: @text-color;
|
||||
.ellipsis();
|
||||
}
|
||||
|
||||
&__amount {
|
||||
font-size: @font-size-xl;
|
||||
font-weight: 600;
|
||||
color: @heading-color;
|
||||
}
|
||||
|
||||
&__plan-foot {
|
||||
.flex(row, space-between, center);
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
&__trade {
|
||||
font-size: @font-size-sm;
|
||||
.ellipsis();
|
||||
}
|
||||
|
||||
&__pay {
|
||||
height: 28px;
|
||||
padding: 0 @spacing-base;
|
||||
border: none;
|
||||
border-radius: @radius-sm;
|
||||
background: @primary-color;
|
||||
color: @white;
|
||||
font-size: @font-size-sm;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: @primary-hover;
|
||||
}
|
||||
}
|
||||
|
||||
&__files {
|
||||
.flex(column, flex-start);
|
||||
gap: @spacing-sm;
|
||||
}
|
||||
|
||||
&__file,
|
||||
&__contract {
|
||||
.flex(row, space-between);
|
||||
gap: @spacing-base;
|
||||
padding: @spacing-sm @spacing-base;
|
||||
border: 1px solid @border-color;
|
||||
border-radius: @radius-base;
|
||||
background: @bg-color-light;
|
||||
}
|
||||
|
||||
&__file-info {
|
||||
.flex(column, flex-start);
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__file-name {
|
||||
font-size: @font-size-base;
|
||||
color: @heading-color;
|
||||
.ellipsis();
|
||||
}
|
||||
|
||||
&__file-meta {
|
||||
font-size: @font-size-sm;
|
||||
}
|
||||
|
||||
&__file-actions {
|
||||
.flex(row, center);
|
||||
gap: @spacing-sm;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__link {
|
||||
font-size: @font-size-sm;
|
||||
color: @primary-color;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
|
||||
&__empty {
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user