Compare commits
2 Commits
fd1eca95a6
...
6af8fd7b09
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6af8fd7b09 | ||
|
|
123d259f92 |
@@ -34,6 +34,8 @@ defineProps<Props>()
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__slogan {
|
&__slogan {
|
||||||
|
// 设计稿 #20:slogan 水平居中(当前唯一使用方为财务中心页)
|
||||||
|
text-align: center;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
margin-top: 9px;
|
margin-top: 9px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|||||||
@@ -275,3 +275,14 @@
|
|||||||
@finance-entry-text: #4b5563;
|
@finance-entry-text: #4b5563;
|
||||||
@finance-axis-text: #9ca3af;
|
@finance-axis-text: #9ca3af;
|
||||||
@finance-ink: #1f2937;
|
@finance-ink: #1f2937;
|
||||||
|
// 财务中心首页快捷入口图标(设计稿 #20,蓝湖 fallback 无切图,色值取自封面采样)
|
||||||
|
@finance-entry-bill-bg: #e9f1fe;
|
||||||
|
@finance-entry-bill-icon: #1f67ef;
|
||||||
|
@finance-entry-pending-bg: #fff4e5;
|
||||||
|
@finance-entry-invoice-bg: #f1ecfe;
|
||||||
|
@finance-entry-invoice-icon: #7c5ce0;
|
||||||
|
@finance-entry-export-bg: #e6f6ec;
|
||||||
|
@finance-entry-badge-bg: #f53f3f;
|
||||||
|
@finance-chart-switch-active: #024fff;
|
||||||
|
@finance-switch-active-bg: #e8f0ff;
|
||||||
|
@finance-switch-idle-bg: #f2f4f7;
|
||||||
|
|||||||
@@ -118,9 +118,9 @@ const SIGN_VARIANTS: Record<SignStatus, 'primary' | 'success' | 'warning' | 'dan
|
|||||||
|
|
||||||
&__section {
|
&__section {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 43px;
|
left: 54px;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 0;
|
height: 70px;
|
||||||
|
|
||||||
&--1 { top: 36px; }
|
&--1 { top: 36px; }
|
||||||
&--2 { top: 649px; }
|
&--2 { top: 649px; }
|
||||||
@@ -130,11 +130,13 @@ const SIGN_VARIANTS: Record<SignStatus, 'primary' | 'success' | 'warning' | 'dan
|
|||||||
&__section-title {
|
&__section-title {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 60px;
|
left: 60px;
|
||||||
|
top: 23px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 41px;
|
line-height: 41px;
|
||||||
color: @contract-cell-text;
|
color: @contract-cell-text;
|
||||||
|
white-space: nowrap; // 父级 h3 内子元素全部脱流,标题会逐字换行,强制不换行
|
||||||
}
|
}
|
||||||
|
|
||||||
&__step {
|
&__step {
|
||||||
@@ -164,8 +166,8 @@ const SIGN_VARIANTS: Record<SignStatus, 'primary' | 'success' | 'warning' | 'dan
|
|||||||
|
|
||||||
&__field--5:has(&__label) { top: 749px; }
|
&__field--5:has(&__label) { top: 749px; }
|
||||||
|
|
||||||
&__field--l { left: 64px; }
|
&__field--l { left: 54px; }
|
||||||
&__field--r { left: 940px; }
|
&__field--r { left: 930px; }
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ElTable, ElTableColumn } from 'element-plus'
|
||||||
import { StatusTag } from '@/components'
|
import { StatusTag } from '@/components'
|
||||||
import type { Contract, SignStatus } from '@/types/business/contract'
|
import type { Contract, SignStatus } from '@/types/business/contract'
|
||||||
import { formatMoney } from '@/utils/format'
|
import { formatMoney } from '@/utils/format'
|
||||||
@@ -23,125 +24,96 @@ const SIGN_VARIANTS: Record<SignStatus, 'primary' | 'success' | 'warning' | 'dan
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="contract-table">
|
<div class="contract-table">
|
||||||
<table class="contract-table__grid">
|
<el-table
|
||||||
<thead>
|
:data="contracts"
|
||||||
<tr>
|
border
|
||||||
<th>合同编号</th>
|
style="width: 100%"
|
||||||
<th>合同名称</th>
|
>
|
||||||
<th>合同类型</th>
|
<el-table-column prop="contractNo" label="合同编号" show-overflow-tooltip />
|
||||||
<th>合同金额</th>
|
<el-table-column prop="contractName" label="合同名称" show-overflow-tooltip />
|
||||||
<th>关联项目</th>
|
<el-table-column prop="contractType" label="合同类型" show-overflow-tooltip />
|
||||||
<th>项目编号</th>
|
<el-table-column label="合同金额" show-overflow-tooltip>
|
||||||
<th>签署状态</th>
|
<template #default="{ row }">
|
||||||
<th>生效时间</th>
|
¥{{ formatMoney((row as Contract).contractAmount) }}
|
||||||
<th>到期时间</th>
|
</template>
|
||||||
<th>操作</th>
|
</el-table-column>
|
||||||
</tr>
|
<el-table-column prop="relatedProjectName" label="关联项目" show-overflow-tooltip />
|
||||||
</thead>
|
<el-table-column prop="projectNo" label="项目编号" show-overflow-tooltip />
|
||||||
<tbody>
|
<el-table-column label="签署状态" width="140">
|
||||||
<tr v-for="item in contracts" :key="item.id">
|
<template #default="{ row }">
|
||||||
<td>{{ item.contractNo }}</td>
|
<StatusTag :label="(row as Contract).signStatus" :variant="SIGN_VARIANTS[(row as Contract).signStatus]" />
|
||||||
<td class="contract-table__name" :title="item.contractName">
|
</template>
|
||||||
{{ item.contractName }}
|
</el-table-column>
|
||||||
</td>
|
<el-table-column prop="effectiveDate" label="生效时间" show-overflow-tooltip />
|
||||||
<td>{{ item.contractType }}</td>
|
<el-table-column prop="expiryDate" label="到期时间" show-overflow-tooltip />
|
||||||
<td>¥{{ formatMoney(item.contractAmount) }}</td>
|
<el-table-column label="操作" width="180">
|
||||||
<td class="contract-table__name" :title="item.relatedProjectName">
|
<template #default="{ row }">
|
||||||
{{ item.relatedProjectName }}
|
<div class="contract-table__actions">
|
||||||
</td>
|
|
||||||
<td>{{ item.projectNo }}</td>
|
|
||||||
<td>
|
|
||||||
<StatusTag :label="item.signStatus" :variant="SIGN_VARIANTS[item.signStatus]" />
|
|
||||||
</td>
|
|
||||||
<td>{{ item.effectiveDate }}</td>
|
|
||||||
<td>{{ item.expiryDate }}</td>
|
|
||||||
<td class="contract-table__actions">
|
|
||||||
<button
|
<button
|
||||||
v-if="item.signStatus === '待签署'"
|
v-if="(row as Contract).signStatus === '待签署'"
|
||||||
|
type="button"
|
||||||
class="contract-table__btn contract-table__btn--primary"
|
class="contract-table__btn contract-table__btn--primary"
|
||||||
@click="emit('sign', item.id)"
|
@click="emit('sign', (row as Contract).id)"
|
||||||
>
|
>
|
||||||
去签署
|
去签署
|
||||||
</button>
|
</button>
|
||||||
<button class="contract-table__btn" @click="emit('view', item.id)">
|
<button
|
||||||
|
type="button"
|
||||||
|
class="contract-table__btn"
|
||||||
|
@click="emit('view', (row as Contract).id)"
|
||||||
|
>
|
||||||
详情
|
详情
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</div>
|
||||||
</tr>
|
</template>
|
||||||
<tr v-if="contracts.length === 0">
|
</el-table-column>
|
||||||
<td colspan="10" class="contract-table__empty text-secondary">
|
<template #empty>
|
||||||
暂无符合条件的合同
|
<span class="contract-table__empty">暂无符合条件的合同</span>
|
||||||
</td>
|
</template>
|
||||||
</tr>
|
</el-table>
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.contract-table {
|
.contract-table {
|
||||||
overflow-x: auto;
|
:deep(.el-table) {
|
||||||
.scrollbar();
|
|
||||||
|
|
||||||
&__grid {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid @contract-line;
|
|
||||||
border-collapse: collapse;
|
|
||||||
table-layout: fixed;
|
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
--el-table-border-color: @contract-line;
|
||||||
|
--el-table-header-bg-color: @contract-table-head-bg;
|
||||||
|
--el-table-header-text-color: @contract-head-text;
|
||||||
|
--el-table-text-color: @contract-cell-text;
|
||||||
|
--el-table-row-hover-bg-color: @bg-color-light;
|
||||||
|
background: @white;
|
||||||
|
|
||||||
th,
|
.el-table__cell {
|
||||||
td {
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
border-bottom: 1px solid @contract-line;
|
|
||||||
vertical-align: middle;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
|
||||||
|
|
||||||
td.contract-table__name {
|
|
||||||
white-space: normal;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
height: 64px;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
background: @contract-table-head-bg;
|
|
||||||
color: @contract-head-text;
|
|
||||||
font-weight: 400;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
height: 96px;
|
|
||||||
color: @contract-cell-text;
|
|
||||||
line-height: 29px;
|
line-height: 29px;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr:last-child td {
|
th.el-table__cell {
|
||||||
border-bottom: none;
|
height: 64px;
|
||||||
|
font-weight: 400;
|
||||||
|
background: @contract-table-head-bg;
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
color: @contract-head-text;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr:hover td {
|
td.el-table__cell {
|
||||||
|
height: 96px;
|
||||||
|
color: @contract-cell-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__row:hover > td.el-table__cell {
|
||||||
background: @bg-color-light;
|
background: @bg-color-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
th:nth-child(1) { width: 11%; }
|
|
||||||
th:nth-child(2) { width: 9%; }
|
|
||||||
th:nth-child(3) { width: 10%; }
|
|
||||||
th:nth-child(4) { width: 9%; }
|
|
||||||
th:nth-child(5) { width: 10%; }
|
|
||||||
th:nth-child(6) { width: 11%; }
|
|
||||||
th:nth-child(7) { width: 7%; }
|
|
||||||
th:nth-child(8) { width: 11%; }
|
|
||||||
th:nth-child(9) { width: 11%; }
|
|
||||||
th:nth-child(10) { width: 11%; }
|
|
||||||
}
|
|
||||||
|
|
||||||
&__name {
|
|
||||||
color: @contract-cell-text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__actions {
|
&__actions {
|
||||||
@@ -152,6 +124,9 @@ const SIGN_VARIANTS: Record<SignStatus, 'primary' | 'success' | 'warning' | 'dan
|
|||||||
&__btn {
|
&__btn {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: @contract-accent;
|
color: @contract-accent;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -161,7 +136,6 @@ const SIGN_VARIANTS: Record<SignStatus, 'primary' | 'success' | 'warning' | 'dan
|
|||||||
&--primary {
|
&--primary {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
padding: 0 @spacing-sm;
|
padding: 0 @spacing-sm;
|
||||||
border: none;
|
|
||||||
border-radius: @radius-sm;
|
border-radius: @radius-sm;
|
||||||
background: @contract-accent;
|
background: @contract-accent;
|
||||||
color: @white;
|
color: @white;
|
||||||
@@ -173,6 +147,12 @@ const SIGN_VARIANTS: Record<SignStatus, 'primary' | 'success' | 'warning' | 'dan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__empty {
|
||||||
|
display: block;
|
||||||
|
padding: @spacing-xl 0;
|
||||||
|
color: @text-color-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.status-tag) {
|
:deep(.status-tag) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
@@ -180,10 +160,5 @@ const SIGN_VARIANTS: Record<SignStatus, 'primary' | 'success' | 'warning' | 'dan
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__empty {
|
|
||||||
padding: @spacing-xl 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, watch } from 'vue'
|
import { computed, onMounted, watch } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { AppButton } from '@/components'
|
|
||||||
import type { FileInfo } from '@/types/api'
|
import type { FileInfo } from '@/types/api'
|
||||||
import { useContractStore } from '@/stores/modules/contract'
|
import { useContractStore } from '@/stores/modules/contract'
|
||||||
import ContractInfoCard from './cpns/ContractInfoCard.vue'
|
import ContractInfoCard from './cpns/ContractInfoCard.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
|
||||||
const contractStore = useContractStore()
|
const contractStore = useContractStore()
|
||||||
const { current } = storeToRefs(contractStore)
|
const { current } = storeToRefs(contractStore)
|
||||||
|
|
||||||
@@ -19,10 +17,6 @@ function load(): void {
|
|||||||
void contractStore.fetchDetail(contractId.value)
|
void contractStore.fetchDetail(contractId.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBack(): void {
|
|
||||||
void router.push('/contract')
|
|
||||||
}
|
|
||||||
|
|
||||||
function onViewProject(): void {
|
function onViewProject(): void {
|
||||||
// TODO: 关联项目 id 由接口返回后跳转项目详情
|
// TODO: 关联项目 id 由接口返回后跳转项目详情
|
||||||
ElMessage.info('项目详情跳转待接口接入')
|
ElMessage.info('项目详情跳转待接口接入')
|
||||||
@@ -65,12 +59,6 @@ onMounted(load)
|
|||||||
<p v-else class="contract-detail__empty text-secondary">
|
<p v-else class="contract-detail__empty text-secondary">
|
||||||
合同不存在或已下线
|
合同不存在或已下线
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="contract-detail__footer">
|
|
||||||
<AppButton @click="onBack">
|
|
||||||
返回
|
|
||||||
</AppButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -91,28 +79,6 @@ onMounted(load)
|
|||||||
color: @contract-muted;
|
color: @contract-muted;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__footer {
|
|
||||||
.flex(row, flex-end);
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 30px;
|
|
||||||
|
|
||||||
:deep(.app-button) {
|
|
||||||
height: 40px;
|
|
||||||
padding: 0 32px;
|
|
||||||
border: none;
|
|
||||||
border-radius: @radius-base;
|
|
||||||
background: @primary-strong;
|
|
||||||
color: @white;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.app-button:hover) {
|
|
||||||
border-color: transparent;
|
|
||||||
background: @primary-color;
|
|
||||||
color: @white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__empty {
|
&__empty {
|
||||||
padding: @spacing-xxl 0;
|
padding: @spacing-xxl 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { computed, onMounted, watch } from 'vue'
|
import { computed, onMounted, watch } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { ElButton } from 'element-plus'
|
||||||
import { useFinanceStore } from '@/stores/modules/finance'
|
import { useFinanceStore } from '@/stores/modules/finance'
|
||||||
import type { Bill, BillStatus } from '@/types/business/finance'
|
import type { Bill, BillStatus } from '@/types/business/finance'
|
||||||
import { formatMoney } from '@/utils/format'
|
import { formatMoney } from '@/utils/format'
|
||||||
@@ -122,20 +123,20 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<el-button
|
||||||
v-if="card.status === '待支付'"
|
v-if="card.status === '待支付'"
|
||||||
class="bill-detail__cta"
|
class="bill-detail__cta"
|
||||||
@click="onPay(currentBill.id)"
|
@click="onPay(currentBill.id)"
|
||||||
>
|
>
|
||||||
立即支付
|
立即支付
|
||||||
</button>
|
</el-button>
|
||||||
<button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
class="bill-detail__cta"
|
class="bill-detail__cta"
|
||||||
@click="router.push('/finance/bills')"
|
@click="router.push('/finance/bills')"
|
||||||
>
|
>
|
||||||
返回
|
返回
|
||||||
</button>
|
</el-button>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -350,12 +351,12 @@ onMounted(() => {
|
|||||||
width: 90px;
|
width: 90px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border: none;
|
border: none;
|
||||||
|
padding: 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: @primary-strong;
|
background: @primary-strong;
|
||||||
color: @white;
|
color: @white;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding-bottom: 6px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { computed, onMounted, ref } from 'vue'
|
|||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { PaginationBar } from '@/components'
|
import { PaginationBar } from '@/components'
|
||||||
|
import { ElRadioGroup, ElRadioButton, ElInput, ElButton } from 'element-plus'
|
||||||
import { useFinanceStore } from '@/stores/modules/finance'
|
import { useFinanceStore } from '@/stores/modules/finance'
|
||||||
import type { BillStatus } from '@/types/business/finance'
|
import type { BillStatus } from '@/types/business/finance'
|
||||||
import BillSummaryBar from './cpns/BillSummaryBar.vue'
|
import BillSummaryBar from './cpns/BillSummaryBar.vue'
|
||||||
@@ -35,6 +36,10 @@ function onTab(value: BillStatus | 'all'): void {
|
|||||||
void financeStore.search()
|
void financeStore.search()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onTabChange(val: string | number | boolean | undefined): void {
|
||||||
|
onTab(val as BillStatus | 'all')
|
||||||
|
}
|
||||||
|
|
||||||
function onSearch(): void {
|
function onSearch(): void {
|
||||||
financeStore.setQuery({ ...query.value, keyword: keyword.value })
|
financeStore.setQuery({ ...query.value, keyword: keyword.value })
|
||||||
void financeStore.search()
|
void financeStore.search()
|
||||||
@@ -87,31 +92,34 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bills__toolbar">
|
<div class="bills__toolbar">
|
||||||
<div class="bills__tabs">
|
<el-radio-group
|
||||||
<button
|
class="bills__tabs"
|
||||||
|
:model-value="query.status"
|
||||||
|
@change="onTabChange"
|
||||||
|
>
|
||||||
|
<el-radio-button
|
||||||
v-for="tab in STATUS_TABS"
|
v-for="tab in STATUS_TABS"
|
||||||
:key="tab.value"
|
:key="tab.value"
|
||||||
class="bills__tab"
|
:value="tab.value"
|
||||||
:class="{ 'is-active': query.status === tab.value }"
|
|
||||||
@click="onTab(tab.value)"
|
|
||||||
>
|
>
|
||||||
{{ tab.label }}
|
{{ tab.label }}
|
||||||
</button>
|
</el-radio-button>
|
||||||
</div>
|
</el-radio-group>
|
||||||
|
|
||||||
<input
|
<el-input
|
||||||
v-model="keyword"
|
v-model="keyword"
|
||||||
class="bills__search"
|
class="bills__search"
|
||||||
placeholder="关键字搜索"
|
placeholder="关键字搜索"
|
||||||
|
clearable
|
||||||
@keyup.enter="onSearch"
|
@keyup.enter="onSearch"
|
||||||
>
|
/>
|
||||||
|
|
||||||
<button class="bills__btn bills__btn--reset" @click="onReset">
|
<el-button class="bills__btn bills__btn--reset" @click="onReset">
|
||||||
重置
|
重置
|
||||||
</button>
|
</el-button>
|
||||||
<button class="bills__btn bills__btn--primary" @click="onSearch">
|
<el-button class="bills__btn bills__btn--primary" @click="onSearch">
|
||||||
搜索
|
搜索
|
||||||
</button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bills__board">
|
<div class="bills__board">
|
||||||
@@ -220,44 +228,32 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__tabs {
|
&__tabs {
|
||||||
.flex(row, flex-start, center);
|
flex-shrink: 0;
|
||||||
gap: 0;
|
|
||||||
width: 279px;
|
:deep(.el-radio-button__inner) {
|
||||||
height: 34px;
|
height: 34px;
|
||||||
|
padding: 0 11.3px;
|
||||||
border: 1px solid @border-color;
|
border: 1px solid @border-color;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
|
||||||
flex-shrink: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0 11.3px 0 9.2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__tab {
|
|
||||||
flex: 1 1 0;
|
|
||||||
height: 32px;
|
|
||||||
border: none;
|
|
||||||
border-right: 1px solid @border-color;
|
|
||||||
border-radius: 0;
|
|
||||||
background: @white;
|
background: @white;
|
||||||
color: @finance-entry-text;
|
color: @finance-entry-text;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
transition: all 0.2s;
|
line-height: 32px;
|
||||||
|
box-shadow: none;
|
||||||
&:last-child {
|
|
||||||
border-right: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
:deep(.el-radio-button + .el-radio-button) {
|
||||||
color: @primary-strong;
|
margin-left: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active {
|
:deep(.el-radio-button.is-active .el-radio-button__inner) {
|
||||||
border-right-color: @primary-strong;
|
border-color: @primary-strong;
|
||||||
background: @primary-strong;
|
background: @primary-strong;
|
||||||
color: @white;
|
color: @white;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
box-shadow: -1px 0 0 0 @primary-strong;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,26 +261,31 @@ onMounted(() => {
|
|||||||
width: 1300px;
|
width: 1300px;
|
||||||
height: 37px;
|
height: 37px;
|
||||||
margin-left: 33px;
|
margin-left: 33px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
width: 1300px;
|
||||||
|
height: 37px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background: @finance-field-bg;
|
background: @finance-field-bg;
|
||||||
|
box-shadow: none;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
color: @finance-entry-text;
|
color: @finance-entry-text;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
outline: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: @contract-placeholder;
|
color: @contract-placeholder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__btn {
|
&__btn {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
background: transparent;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -293,9 +294,11 @@ onMounted(() => {
|
|||||||
width: 62.2px;
|
width: 62.2px;
|
||||||
margin-left: 15.4px;
|
margin-left: 15.4px;
|
||||||
color: @finance-entry-text;
|
color: @finance-entry-text;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: @primary-strong;
|
color: @primary-strong;
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage, ElButton } from 'element-plus'
|
||||||
import { useFinanceStore } from '@/stores/modules/finance'
|
import { useFinanceStore } from '@/stores/modules/finance'
|
||||||
import type { PayMethod } from '@/types/business/finance'
|
import type { PayMethod } from '@/types/business/finance'
|
||||||
import { createMerchantOrderNo, PAY_METHODS } from '@/utils/payMeta'
|
import { createMerchantOrderNo, PAY_METHODS } from '@/utils/payMeta'
|
||||||
@@ -172,13 +172,13 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<el-button
|
||||||
class="checkout__cta"
|
class="checkout__cta"
|
||||||
:disabled="!canPay"
|
:disabled="!canPay"
|
||||||
@click="onConfirm"
|
@click="onConfirm"
|
||||||
>
|
>
|
||||||
{{ submitting ? '处理中…' : '确认支付' }}
|
{{ submitting ? '处理中…' : '确认支付' }}
|
||||||
</button>
|
</el-button>
|
||||||
|
|
||||||
<p class="checkout__summary-tip">
|
<p class="checkout__summary-tip">
|
||||||
支付完成后订单状态将自动更新
|
支付完成后订单状态将自动更新
|
||||||
@@ -191,9 +191,9 @@ onBeforeUnmount(() => {
|
|||||||
<p class="checkout__empty-text">
|
<p class="checkout__empty-text">
|
||||||
未找到待支付账单,请返回账单明细重新选择
|
未找到待支付账单,请返回账单明细重新选择
|
||||||
</p>
|
</p>
|
||||||
<button class="checkout__empty-btn" @click="goBack">
|
<el-button class="checkout__empty-btn" @click="goBack">
|
||||||
返回账单明细
|
返回账单明细
|
||||||
</button>
|
</el-button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<WechatQrDialog
|
<WechatQrDialog
|
||||||
@@ -283,6 +283,8 @@ onBeforeUnmount(() => {
|
|||||||
&__cta {
|
&__cta {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
border-radius: @radius-round;
|
border-radius: @radius-round;
|
||||||
background: @checkout-cta;
|
background: @checkout-cta;
|
||||||
color: @white;
|
color: @white;
|
||||||
@@ -329,6 +331,12 @@ onBeforeUnmount(() => {
|
|||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.checkout__empty-btn.el-button) {
|
||||||
|
border: 1px solid @checkout-cta;
|
||||||
|
background: @checkout-card;
|
||||||
|
color: @checkout-cta;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 992px) {
|
@media (max-width: 992px) {
|
||||||
&__layout {
|
&__layout {
|
||||||
grid-template-columns: minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { ElCheckbox, ElButton } from 'element-plus'
|
||||||
import type { Bill } from '@/types/business/finance'
|
import type { Bill } from '@/types/business/finance'
|
||||||
import { formatMoney } from '@/utils/format'
|
import { formatMoney } from '@/utils/format'
|
||||||
|
|
||||||
@@ -30,12 +31,11 @@ function onBatchPay(): void {
|
|||||||
<div class="bill-summary">
|
<div class="bill-summary">
|
||||||
<div class="bill-summary__inner">
|
<div class="bill-summary__inner">
|
||||||
<div class="bill-summary__left">
|
<div class="bill-summary__left">
|
||||||
<input
|
<el-checkbox
|
||||||
class="bill-summary__check"
|
class="bill-summary__check"
|
||||||
type="checkbox"
|
:model-value="canPay"
|
||||||
:checked="canPay"
|
|
||||||
@change="onBatchPay"
|
@change="onBatchPay"
|
||||||
>
|
/>
|
||||||
<span class="bill-summary__text">已选择</span>
|
<span class="bill-summary__text">已选择</span>
|
||||||
<span class="bill-summary__count">{{ selectedBills.length }}</span>
|
<span class="bill-summary__count">{{ selectedBills.length }}</span>
|
||||||
<span class="bill-summary__text">笔订单</span>
|
<span class="bill-summary__text">笔订单</span>
|
||||||
@@ -44,9 +44,9 @@ function onBatchPay(): void {
|
|||||||
<div class="bill-summary__right">
|
<div class="bill-summary__right">
|
||||||
<span class="bill-summary__text">合计:</span>
|
<span class="bill-summary__text">合计:</span>
|
||||||
<span class="bill-summary__amount">¥ {{ formatMoney(selectedAmount, 0) }}</span>
|
<span class="bill-summary__amount">¥ {{ formatMoney(selectedAmount, 0) }}</span>
|
||||||
<button class="bill-summary__cta" :disabled="!canPay" @click="onBatchPay">
|
<el-button class="bill-summary__cta" :disabled="!canPay" @click="onBatchPay">
|
||||||
立即支付
|
立即支付
|
||||||
</button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,11 +77,15 @@ function onBatchPay(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__check {
|
&__check {
|
||||||
width: 15px;
|
|
||||||
height: 15px;
|
height: 15px;
|
||||||
margin: 0 12.25px 0 0;
|
margin: 0 12.25px 0 0;
|
||||||
accent-color: @primary-strong;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
:deep(.el-checkbox__inner) {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border-color: @border-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
@@ -118,6 +122,7 @@ function onBatchPay(): void {
|
|||||||
width: 84px;
|
width: 84px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border: none;
|
border: none;
|
||||||
|
padding: 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: @finance-summary-cta;
|
background: @finance-summary-cta;
|
||||||
color: @white;
|
color: @white;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
|
import { ElTable, ElTableColumn } from 'element-plus'
|
||||||
|
import type { TableInstance } from 'element-plus'
|
||||||
import type { Bill } from '@/types/business/finance'
|
import type { Bill } from '@/types/business/finance'
|
||||||
import { formatMoney } from '@/utils/format'
|
import { formatMoney } from '@/utils/format'
|
||||||
|
|
||||||
@@ -16,223 +18,158 @@ const emit = defineEmits<{
|
|||||||
'update:selection': [ids: string[]]
|
'update:selection': [ids: string[]]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const selection = ref<Set<string>>(new Set(props.selectedIds))
|
const tableRef = ref<TableInstance>()
|
||||||
|
|
||||||
|
function onSelectionChange(rows: Bill[]): void {
|
||||||
|
emit('update:selection', rows.map((row) => row.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外部清空选中(如重置查询)时同步清空 el-table 选择态
|
||||||
watch(
|
watch(
|
||||||
() => props.selectedIds,
|
() => props.selectedIds,
|
||||||
(ids) => {
|
(ids) => {
|
||||||
selection.value = new Set(ids)
|
if (ids.length === 0 && tableRef.value) tableRef.value.clearSelection()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const allSelected = computed(
|
|
||||||
() => props.bills.length > 0 && props.bills.every((bill) => selection.value.has(bill.id)),
|
|
||||||
)
|
|
||||||
|
|
||||||
const someSelected = computed(
|
|
||||||
() => props.bills.some((bill) => selection.value.has(bill.id)) && !allSelected.value,
|
|
||||||
)
|
|
||||||
|
|
||||||
function toggle(id: string): void {
|
|
||||||
if (selection.value.has(id)) selection.value.delete(id)
|
|
||||||
else selection.value.add(id)
|
|
||||||
emit('update:selection', [...selection.value])
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleAll(): void {
|
|
||||||
if (allSelected.value) {
|
|
||||||
for (const bill of props.bills) selection.value.delete(bill.id)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (const bill of props.bills) selection.value.add(bill.id)
|
|
||||||
}
|
|
||||||
emit('update:selection', [...selection.value])
|
|
||||||
}
|
|
||||||
|
|
||||||
const COLUMNS = [
|
|
||||||
{ key: 'index', label: '序号', className: 'bill-table__col--index' },
|
|
||||||
{ key: 'planName', label: '账单名称', className: 'bill-table__col--name' },
|
|
||||||
{ key: 'billNo', label: '账单号', className: 'bill-table__col--no' },
|
|
||||||
{ key: 'tradeNo', label: '交易流水号', className: 'bill-table__col--trade' },
|
|
||||||
{ key: 'projectName', label: '项目名称', className: 'bill-table__col--project' },
|
|
||||||
{ key: 'amount', label: '订单金额', className: 'bill-table__col--amount' },
|
|
||||||
{ key: 'status', label: '状态', className: 'bill-table__col--status' },
|
|
||||||
{ key: 'ops', label: '操作', className: 'bill-table__col--ops' },
|
|
||||||
] as const
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<table class="bill-table">
|
<el-table
|
||||||
<colgroup>
|
ref="tableRef"
|
||||||
<col class="bill-table__col--check">
|
class="bill-table"
|
||||||
<col
|
:data="bills"
|
||||||
v-for="column in COLUMNS"
|
row-key="id"
|
||||||
:key="column.key"
|
@selection-change="onSelectionChange"
|
||||||
:class="column.className"
|
|
||||||
>
|
>
|
||||||
</colgroup>
|
<el-table-column
|
||||||
<thead>
|
type="selection"
|
||||||
<tr>
|
width="51"
|
||||||
<th class="bill-table__cell bill-table__cell--check">
|
/>
|
||||||
<input
|
<el-table-column
|
||||||
type="checkbox"
|
type="index"
|
||||||
:checked="allSelected"
|
label="序号"
|
||||||
:indeterminate="someSelected"
|
width="235"
|
||||||
@change="toggleAll"
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="planName"
|
||||||
|
label="账单名称"
|
||||||
|
width="310"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="billNo"
|
||||||
|
label="账单号"
|
||||||
|
width="148"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="tradeNo"
|
||||||
|
label="交易流水号"
|
||||||
|
width="394"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="projectName"
|
||||||
|
label="项目名称"
|
||||||
|
width="339"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="订单金额"
|
||||||
|
width="97"
|
||||||
>
|
>
|
||||||
</th>
|
<template #default="{ row }">
|
||||||
<th
|
{{ formatMoney((row as Bill).amount, 0) }}
|
||||||
v-for="column in COLUMNS"
|
</template>
|
||||||
:key="column.key"
|
</el-table-column>
|
||||||
class="bill-table__cell"
|
<el-table-column
|
||||||
|
label="状态"
|
||||||
|
width="95"
|
||||||
>
|
>
|
||||||
{{ column.label }}
|
<template #default="{ row }">
|
||||||
</th>
|
{{ (row as Bill).status }}
|
||||||
</tr>
|
</template>
|
||||||
</thead>
|
</el-table-column>
|
||||||
<tbody>
|
<el-table-column
|
||||||
<tr v-for="(bill, index) in bills" :key="bill.id">
|
label="操作"
|
||||||
<td class="bill-table__cell bill-table__cell--check">
|
width="96"
|
||||||
<input
|
fixed="right"
|
||||||
type="checkbox"
|
|
||||||
:checked="selection.has(bill.id)"
|
|
||||||
@change="toggle(bill.id)"
|
|
||||||
>
|
>
|
||||||
</td>
|
<template #default="{ row }">
|
||||||
<td class="bill-table__cell">
|
|
||||||
{{ index + 1 }}
|
|
||||||
</td>
|
|
||||||
<td class="bill-table__cell">
|
|
||||||
{{ bill.planName }}
|
|
||||||
</td>
|
|
||||||
<td class="bill-table__cell">
|
|
||||||
{{ bill.billNo }}
|
|
||||||
</td>
|
|
||||||
<td class="bill-table__cell">
|
|
||||||
{{ bill.tradeNo ?? '—' }}
|
|
||||||
</td>
|
|
||||||
<td class="bill-table__cell">
|
|
||||||
{{ bill.projectName }}
|
|
||||||
</td>
|
|
||||||
<td class="bill-table__cell">
|
|
||||||
{{ formatMoney(bill.amount, 0) }}
|
|
||||||
</td>
|
|
||||||
<td class="bill-table__cell">
|
|
||||||
{{ bill.status }}
|
|
||||||
</td>
|
|
||||||
<td class="bill-table__cell bill-table__cell--ops">
|
|
||||||
<span class="bill-table__ops">
|
<span class="bill-table__ops">
|
||||||
<button
|
<el-button
|
||||||
v-if="bill.status === '待支付'"
|
v-if="(row as Bill).status === '待支付'"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
class="bill-table__link"
|
class="bill-table__link"
|
||||||
@click="emit('pay', bill.id)"
|
@click="emit('pay', (row as Bill).id)"
|
||||||
>
|
>
|
||||||
去支付
|
去支付
|
||||||
</button>
|
</el-button>
|
||||||
<button class="bill-table__link" @click="emit('view', bill.id)">
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
class="bill-table__link"
|
||||||
|
@click="emit('view', (row as Bill).id)"
|
||||||
|
>
|
||||||
详情
|
详情
|
||||||
</button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</template>
|
||||||
</tr>
|
</el-table-column>
|
||||||
<tr v-if="bills.length === 0">
|
|
||||||
<td colspan="9" class="bill-table__empty">
|
<template #empty>
|
||||||
暂无账单数据
|
<span class="bill-table__empty">暂无账单数据</span>
|
||||||
</td>
|
</template>
|
||||||
</tr>
|
</el-table>
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.bill-table {
|
.bill-table {
|
||||||
width: 1765px;
|
width: 1765px;
|
||||||
margin: 6.5px 0 0 14.5px;
|
margin: 6.5px 0 0 14.5px;
|
||||||
border-collapse: collapse;
|
|
||||||
table-layout: fixed;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: @finance-entry-text;
|
color: @finance-entry-text;
|
||||||
|
|
||||||
&__col--check { width: 51px; }
|
:deep(.el-table__header-wrapper th.el-table__cell) {
|
||||||
&__col--index { width: 235.4px; }
|
|
||||||
&__col--name { width: 310.2px; }
|
|
||||||
&__col--no { width: 148px; }
|
|
||||||
&__col--trade { width: 394.1px; }
|
|
||||||
&__col--project { width: 339.4px; }
|
|
||||||
&__col--amount { width: 97px; }
|
|
||||||
&__col--status { width: 95px; }
|
|
||||||
&__col--ops { width: 95.5px; }
|
|
||||||
|
|
||||||
&__cell {
|
|
||||||
height: 43.77px;
|
|
||||||
padding: 0;
|
|
||||||
border-bottom: 1px solid @project-divider;
|
|
||||||
text-align: left;
|
|
||||||
vertical-align: middle;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&:not(&--check):not(&--ops) {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--check {
|
|
||||||
padding-left: 43.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--ops {
|
|
||||||
padding-right: 22px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@col-pad-1: 43.5px;
|
|
||||||
@col-pad-2: 149.9px;
|
|
||||||
@col-pad-3: 8px;
|
|
||||||
@col-pad-4: 17px;
|
|
||||||
@col-pad-5: 5px;
|
|
||||||
@col-pad-6: 14.6px;
|
|
||||||
@col-pad-7: 4.6px;
|
|
||||||
@col-pad-8: 6px;
|
|
||||||
|
|
||||||
thead &__cell,
|
|
||||||
tbody &__cell {
|
|
||||||
&:nth-child(1) { padding-left: @col-pad-1; }
|
|
||||||
&:nth-child(2) { padding-left: @col-pad-2; }
|
|
||||||
&:nth-child(3) { padding-left: @col-pad-3; }
|
|
||||||
&:nth-child(4) { padding-left: @col-pad-4; }
|
|
||||||
&:nth-child(5) { padding-left: @col-pad-5; }
|
|
||||||
&:nth-child(6) { padding-left: @col-pad-6; }
|
|
||||||
&:nth-child(7) { padding-left: @col-pad-7; }
|
|
||||||
&:nth-child(8) { padding-left: @col-pad-8; }
|
|
||||||
&:nth-child(9) { padding-left: 0; padding-right: 10px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
thead &__cell {
|
|
||||||
height: 41.77px;
|
height: 41.77px;
|
||||||
|
padding: 0;
|
||||||
background: @finance-table-head-bg;
|
background: @finance-table-head-bg;
|
||||||
border-right: 1px solid @project-divider;
|
border-right: 1px solid @project-divider;
|
||||||
|
border-bottom: 1px solid @project-divider;
|
||||||
color: @finance-entry-text;
|
color: @finance-entry-text;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child {
|
:deep(.el-table__header-wrapper th.el-table__cell:first-child) {
|
||||||
border-left: 1px solid @project-divider;
|
border-left: 1px solid @project-divider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__body-wrapper td.el-table__cell) {
|
||||||
|
height: 43.77px;
|
||||||
|
padding: 0;
|
||||||
|
border-bottom: 1px solid @project-divider;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr:hover {
|
:deep(.el-table__body tr:hover > td.el-table__cell) {
|
||||||
background: @bg-color-light;
|
background: @bg-color-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__cell--check input {
|
:deep(.el-table .cell) {
|
||||||
width: 14px;
|
padding-left: 10px;
|
||||||
height: 14px;
|
padding-right: 10px;
|
||||||
margin: 0;
|
line-height: normal;
|
||||||
accent-color: @primary-strong;
|
white-space: nowrap;
|
||||||
vertical-align: middle;
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首列(多选)左缩进对齐设计稿
|
||||||
|
:deep(.el-table-column--selection .cell) {
|
||||||
|
padding-left: 43.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__ops {
|
&__ops {
|
||||||
@@ -240,31 +177,18 @@ const COLUMNS = [
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
gap: 18px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
color: @primary-strong;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
transition: color 0.2s;
|
padding: 0;
|
||||||
|
|
||||||
+ .bill-table__link {
|
|
||||||
margin-left: 18.36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: @primary-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__empty {
|
&__empty {
|
||||||
height: 43.77px;
|
|
||||||
color: @project-muted;
|
color: @project-muted;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import { ElButton } from 'element-plus'
|
||||||
|
|
||||||
interface DistributionItem {
|
interface DistributionItem {
|
||||||
name: string
|
name: string
|
||||||
@@ -26,13 +27,32 @@ const gradient = computed(() => {
|
|||||||
return `conic-gradient(${stops.join(', ')})`
|
return `conic-gradient(${stops.join(', ')})`
|
||||||
})
|
})
|
||||||
|
|
||||||
function percentOf(value: number): number {
|
const AXIS = [250, 200, 150, 100, 50]
|
||||||
if (total.value <= 0) return 0
|
const AXIS_MAX = AXIS[0]
|
||||||
return Math.round((value / total.value) * 100)
|
const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
||||||
|
|
||||||
|
interface PeriodOption {
|
||||||
|
label: string
|
||||||
|
value: 'month' | 'half'
|
||||||
}
|
}
|
||||||
|
|
||||||
const AXIS = [250, 200, 150, 100, 50]
|
// 设计稿 #20 右上角切换:月度账单(激活)/ 上半
|
||||||
const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
const PERIODS: PeriodOption[] = [
|
||||||
|
{ label: '月度账单', value: 'month' },
|
||||||
|
{ label: '上半', value: 'half' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const period = ref<'month' | 'half'>('month')
|
||||||
|
|
||||||
|
function onPeriodChange(value: 'month' | 'half'): void {
|
||||||
|
period.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 柱高按 Y 轴量程绝对值换算(设计稿 #20:Y 轴 250 为满量程),非占比
|
||||||
|
function heightPercentOf(value: number): number {
|
||||||
|
if (value <= 0) return 0
|
||||||
|
return Math.min((value / AXIS_MAX) * 100, 100)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -41,6 +61,18 @@ const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
|||||||
业务类型分布
|
业务类型分布
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
<div class="finance-chart__switch">
|
||||||
|
<el-button
|
||||||
|
v-for="item in PERIODS"
|
||||||
|
:key="item.value"
|
||||||
|
class="finance-chart__switch-btn"
|
||||||
|
:class="{ 'is-active': period === item.value }"
|
||||||
|
@click="onPeriodChange(item.value)"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="finance-chart__body">
|
<div class="finance-chart__body">
|
||||||
<ul class="finance-chart__axis" aria-hidden="true">
|
<ul class="finance-chart__axis" aria-hidden="true">
|
||||||
<li v-for="v in AXIS" :key="v" class="finance-chart__axis-item">
|
<li v-for="v in AXIS" :key="v" class="finance-chart__axis-item">
|
||||||
@@ -55,10 +87,10 @@ const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
|||||||
|
|
||||||
<div class="finance-chart__bars">
|
<div class="finance-chart__bars">
|
||||||
<span
|
<span
|
||||||
v-for="item in distribution"
|
v-for="(m, i) in MONTHS"
|
||||||
:key="item.name"
|
:key="m"
|
||||||
class="finance-chart__bar"
|
class="finance-chart__bar"
|
||||||
:style="{ height: `${Math.max(percentOf(item.value), 6)}%` }"
|
:style="{ height: `${heightPercentOf(distribution[i]?.value ?? 0)}%` }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -78,6 +110,10 @@ const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
|||||||
:key="item.name"
|
:key="item.name"
|
||||||
class="finance-chart__legend-item"
|
class="finance-chart__legend-item"
|
||||||
>
|
>
|
||||||
|
<i
|
||||||
|
class="finance-chart__dot"
|
||||||
|
:style="{ background: item.color }"
|
||||||
|
/>
|
||||||
<span class="finance-chart__name">{{ item.name }}</span>
|
<span class="finance-chart__name">{{ item.name }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -116,6 +152,42 @@ const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__switch {
|
||||||
|
.flex(row, flex-end, center);
|
||||||
|
gap: 8px;
|
||||||
|
position: absolute;
|
||||||
|
top: 25px;
|
||||||
|
right: 23px;
|
||||||
|
|
||||||
|
:deep(.el-button + .el-button) {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__switch-btn {
|
||||||
|
height: 24px;
|
||||||
|
margin-left: 0;
|
||||||
|
padding: 0 12px;
|
||||||
|
border: none;
|
||||||
|
border-radius: @radius-round;
|
||||||
|
background: @finance-switch-idle-bg;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px;
|
||||||
|
color: @finance-muted-text;
|
||||||
|
transition: color 0.2s, background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: @finance-switch-idle-bg;
|
||||||
|
color: @finance-chart-switch-active;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background: @finance-switch-active-bg;
|
||||||
|
color: @finance-chart-switch-active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__body {
|
&__body {
|
||||||
.flex(row, flex-start, flex-start);
|
.flex(row, flex-start, flex-start);
|
||||||
gap: 0;
|
gap: 0;
|
||||||
@@ -126,7 +198,8 @@ const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
|||||||
.flex(column, space-between, flex-start);
|
.flex(column, space-between, flex-start);
|
||||||
width: 34px;
|
width: 34px;
|
||||||
height: 234px;
|
height: 234px;
|
||||||
margin: 0;
|
// 设计稿 #20:Y 轴刻度文字左缘 x=103(卡左 70 + padding 23 + 10)
|
||||||
|
margin: 0 0 0 10px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -164,7 +237,7 @@ const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
|||||||
.flex(row, space-around, flex-end);
|
.flex(row, space-around, flex-end);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
padding: 0 40px;
|
// 无横向 padding:柱子与下方月份标签同为 space-around,中心才能对齐(设计稿 #20 柱月对齐)
|
||||||
}
|
}
|
||||||
|
|
||||||
&__bar {
|
&__bar {
|
||||||
@@ -200,7 +273,8 @@ const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
|||||||
.flex(row, flex-start, center);
|
.flex(row, flex-start, center);
|
||||||
gap: 27px;
|
gap: 27px;
|
||||||
margin: 41px 0 0;
|
margin: 41px 0 0;
|
||||||
padding: 0 0 0 662px;
|
// 设计稿 #20:图例文字左缘 x=809(卡左 70 + padding 23 + 704 + 圆点 6 + gap 6)
|
||||||
|
padding: 0 0 0 704px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +286,13 @@ const MONTHS = ['1月', '2月', '3月', '4月', '5月']
|
|||||||
color: @finance-muted-text;
|
color: @finance-muted-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__dot {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ElButton } from 'element-plus'
|
||||||
import type { InvoiceTitle } from '@/types/business/finance'
|
import type { InvoiceTitle } from '@/types/business/finance'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -20,18 +21,30 @@ const emit = defineEmits<{
|
|||||||
<span v-if="invoiceTitle.isDefault" class="invoice-title-card__badge">默认抬头</span>
|
<span v-if="invoiceTitle.isDefault" class="invoice-title-card__badge">默认抬头</span>
|
||||||
<span v-else class="invoice-title-card__badge-placeholder" />
|
<span v-else class="invoice-title-card__badge-placeholder" />
|
||||||
<div class="invoice-title-card__actions">
|
<div class="invoice-title-card__actions">
|
||||||
<button
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
class="invoice-title-card__btn"
|
class="invoice-title-card__btn"
|
||||||
@click="emit('setDefault', invoiceTitle.id)"
|
@click="emit('setDefault', invoiceTitle.id)"
|
||||||
>
|
>
|
||||||
{{ invoiceTitle.isDefault ? '取消默认' : '设为默认' }}
|
{{ invoiceTitle.isDefault ? '取消默认' : '设为默认' }}
|
||||||
</button>
|
</el-button>
|
||||||
<button class="invoice-title-card__btn" @click="emit('delete', invoiceTitle.id)">
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
class="invoice-title-card__btn"
|
||||||
|
@click="emit('delete', invoiceTitle.id)"
|
||||||
|
>
|
||||||
删除
|
删除
|
||||||
</button>
|
</el-button>
|
||||||
<button class="invoice-title-card__btn" @click="emit('edit', invoiceTitle)">
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
class="invoice-title-card__btn"
|
||||||
|
@click="emit('edit', invoiceTitle)"
|
||||||
|
>
|
||||||
编辑
|
编辑
|
||||||
</button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -104,6 +117,7 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: @primary-strong;
|
color: @primary-strong;
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, watch } from 'vue'
|
import { reactive, watch } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage, ElInput, ElRadio, ElButton } from 'element-plus'
|
||||||
import type { InvoiceTitle, InvoiceTitlePayload } from '@/types/business/finance'
|
import type { InvoiceTitle, InvoiceTitlePayload } from '@/types/business/finance'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -50,41 +50,39 @@ function onCancel(): void {
|
|||||||
|
|
||||||
<div class="title-dialog__field">
|
<div class="title-dialog__field">
|
||||||
<span class="title-dialog__label">抬头类型</span>
|
<span class="title-dialog__label">抬头类型</span>
|
||||||
<label class="title-dialog__radio">
|
<el-radio v-model="form.type" value="企业单位">
|
||||||
<input v-model="form.type" type="radio" value="企业单位">
|
|
||||||
企业单位
|
企业单位
|
||||||
</label>
|
</el-radio>
|
||||||
<label class="title-dialog__radio">
|
<el-radio v-model="form.type" value="非企业单位">
|
||||||
<input v-model="form.type" type="radio" value="非企业单位">
|
|
||||||
非企业单位
|
非企业单位
|
||||||
</label>
|
</el-radio>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="title-dialog__field">
|
<div class="title-dialog__field">
|
||||||
<span class="title-dialog__label">发票抬头</span>
|
<span class="title-dialog__label">发票抬头</span>
|
||||||
<input
|
<el-input
|
||||||
v-model="form.title"
|
v-model="form.title"
|
||||||
class="title-dialog__input"
|
class="title-dialog__input"
|
||||||
placeholder="请输入发票抬头"
|
placeholder="请输入发票抬头"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="title-dialog__field">
|
<div class="title-dialog__field">
|
||||||
<span class="title-dialog__label">税号</span>
|
<span class="title-dialog__label">税号</span>
|
||||||
<input
|
<el-input
|
||||||
v-model="form.taxNo"
|
v-model="form.taxNo"
|
||||||
class="title-dialog__input"
|
class="title-dialog__input"
|
||||||
placeholder="请输入税号"
|
placeholder="请输入税号"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="title-dialog__footer">
|
<div class="title-dialog__footer">
|
||||||
<button class="title-dialog__btn" @click="onCancel">
|
<el-button class="title-dialog__btn" @click="onCancel">
|
||||||
取消
|
取消
|
||||||
</button>
|
</el-button>
|
||||||
<button class="title-dialog__btn title-dialog__btn--confirm" @click="onSave">
|
<el-button class="title-dialog__btn title-dialog__btn--confirm" @click="onSave">
|
||||||
保存
|
保存
|
||||||
</button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -119,6 +117,11 @@ function onCancel(): void {
|
|||||||
.flex(row, flex-start, center);
|
.flex(row, flex-start, center);
|
||||||
gap: @spacing-sm;
|
gap: @spacing-sm;
|
||||||
margin-bottom: @spacing-base;
|
margin-bottom: @spacing-base;
|
||||||
|
|
||||||
|
:deep(.el-radio__label) {
|
||||||
|
color: @text-color;
|
||||||
|
font-size: @font-size-base;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
@@ -128,31 +131,19 @@ function onCancel(): void {
|
|||||||
color: @text-color;
|
color: @text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__radio {
|
|
||||||
.flex(row, flex-start, center);
|
|
||||||
gap: @spacing-xs;
|
|
||||||
font-size: @font-size-base;
|
|
||||||
color: @text-color;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
input {
|
|
||||||
accent-color: @primary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__input {
|
&__input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 34px;
|
|
||||||
padding: 0 @spacing-sm;
|
|
||||||
border: 1px solid @border-color;
|
|
||||||
border-radius: @radius-base;
|
|
||||||
background: @bg-color-light;
|
|
||||||
font-size: @font-size-base;
|
|
||||||
outline: none;
|
|
||||||
transition: border-color 0.2s;
|
|
||||||
|
|
||||||
&:focus {
|
:deep(.el-input__wrapper) {
|
||||||
border-color: @primary-color;
|
height: 34px;
|
||||||
|
background: @bg-color-light;
|
||||||
|
box-shadow: none;
|
||||||
|
border-radius: @radius-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: @text-color;
|
||||||
|
font-size: @font-size-base;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
|
import { ElRadio, ElButton } from 'element-plus'
|
||||||
import type { PayMethod } from '@/types/business/finance'
|
import type { PayMethod } from '@/types/business/finance'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -47,13 +48,13 @@ function onCancel(): void {
|
|||||||
class="pay-method-dialog__option"
|
class="pay-method-dialog__option"
|
||||||
:class="{ 'is-active': selected === 'wechat' }"
|
:class="{ 'is-active': selected === 'wechat' }"
|
||||||
>
|
>
|
||||||
<input
|
<el-radio
|
||||||
v-model="selected"
|
v-model="selected"
|
||||||
type="radio"
|
|
||||||
value="wechat"
|
value="wechat"
|
||||||
class="pay-method-dialog__radio"
|
class="pay-method-dialog__radio"
|
||||||
>
|
>
|
||||||
<span class="pay-method-dialog__name">微信支付</span>
|
微信支付
|
||||||
|
</el-radio>
|
||||||
<span class="pay-method-dialog__tip text-secondary">推荐使用</span>
|
<span class="pay-method-dialog__tip text-secondary">推荐使用</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -61,23 +62,23 @@ function onCancel(): void {
|
|||||||
class="pay-method-dialog__option"
|
class="pay-method-dialog__option"
|
||||||
:class="{ 'is-active': selected === 'alipay' }"
|
:class="{ 'is-active': selected === 'alipay' }"
|
||||||
>
|
>
|
||||||
<input
|
<el-radio
|
||||||
v-model="selected"
|
v-model="selected"
|
||||||
type="radio"
|
|
||||||
value="alipay"
|
value="alipay"
|
||||||
class="pay-method-dialog__radio"
|
class="pay-method-dialog__radio"
|
||||||
>
|
>
|
||||||
<span class="pay-method-dialog__name">支付宝支付</span>
|
支付宝支付
|
||||||
|
</el-radio>
|
||||||
<span class="pay-method-dialog__tip text-secondary">其他用户选择</span>
|
<span class="pay-method-dialog__tip text-secondary">其他用户选择</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="pay-method-dialog__footer">
|
<div class="pay-method-dialog__footer">
|
||||||
<button class="pay-method-dialog__btn" @click="onCancel">
|
<el-button class="pay-method-dialog__btn" @click="onCancel">
|
||||||
取消
|
取消
|
||||||
</button>
|
</el-button>
|
||||||
<button class="pay-method-dialog__btn pay-method-dialog__btn--confirm" @click="onConfirm">
|
<el-button class="pay-method-dialog__btn pay-method-dialog__btn--confirm" @click="onConfirm">
|
||||||
确认
|
确认
|
||||||
</button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,14 +130,12 @@ function onCancel(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__radio {
|
&__radio {
|
||||||
accent-color: @primary-color;
|
.el-radio__label {
|
||||||
}
|
|
||||||
|
|
||||||
&__name {
|
|
||||||
font-size: @font-size-base;
|
|
||||||
color: @heading-color;
|
color: @heading-color;
|
||||||
|
font-size: @font-size-base;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__tip {
|
&__tip {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
@@ -158,7 +157,6 @@ function onCancel(): void {
|
|||||||
color: @text-color;
|
color: @text-color;
|
||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: @primary-color;
|
border-color: @primary-color;
|
||||||
color: @primary-color;
|
color: @primary-color;
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ const financeStore = useFinanceStore()
|
|||||||
const { stats } = storeToRefs(financeStore)
|
const { stats } = storeToRefs(financeStore)
|
||||||
|
|
||||||
const ENTRIES = [
|
const ENTRIES = [
|
||||||
{ label: '账单明细', to: '/finance/bills' },
|
{ key: 'bill', label: '账单明细', to: '/finance/bills' },
|
||||||
{ label: '待支付', to: '/finance/bills' },
|
{ key: 'pending', label: '待支付', to: '/finance/bills' },
|
||||||
{ label: '发票申请', to: '/finance/invoices' },
|
{ key: 'invoice', label: '发票申请', to: '/finance/invoices' },
|
||||||
{ label: '报表导出', to: '' },
|
{ key: 'export', label: '报表导出', to: '' },
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
function onPeriodChange(period: string): void {
|
function onPeriodChange(period: string): void {
|
||||||
@@ -49,20 +49,108 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="finance__entries">
|
<div class="finance__entries">
|
||||||
<template v-for="entry in ENTRIES" :key="entry.label">
|
<template v-for="entry in ENTRIES" :key="entry.key">
|
||||||
<RouterLink
|
<RouterLink
|
||||||
v-if="entry.to"
|
v-if="entry.to"
|
||||||
:to="entry.to"
|
:to="entry.to"
|
||||||
class="finance__entry"
|
class="finance__entry"
|
||||||
>
|
>
|
||||||
{{ entry.label }}
|
<span
|
||||||
|
class="finance__entry-icon"
|
||||||
|
:class="`finance__entry-icon--${entry.key}`"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
v-if="entry.key === 'bill'"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="2.5"
|
||||||
|
width="12"
|
||||||
|
height="15"
|
||||||
|
rx="1.5"
|
||||||
|
/>
|
||||||
|
<path d="M7 7h6M7 10h6M7 13h4" />
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
v-else-if="entry.key === 'pending'"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="2.5"
|
||||||
|
y="4.5"
|
||||||
|
width="15"
|
||||||
|
height="11"
|
||||||
|
rx="1.5"
|
||||||
|
/>
|
||||||
|
<path d="M2.5 8.5h15" />
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
v-else-if="entry.key === 'invoice'"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="4"
|
||||||
|
y="2.5"
|
||||||
|
width="12"
|
||||||
|
height="15"
|
||||||
|
rx="1.5"
|
||||||
|
/>
|
||||||
|
<path d="M7.5 7h5M7.5 10.5h5M7.5 14h2.5" />
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
v-else
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M10 13V4M6.5 7.5 10 4l3.5 3.5M4 15.5h12" />
|
||||||
|
</svg>
|
||||||
|
<i
|
||||||
|
v-if="entry.key === 'pending' && (stats?.pendingCount ?? 0) > 0"
|
||||||
|
class="finance__entry-badge"
|
||||||
|
>
|
||||||
|
{{ stats?.pendingCount }}
|
||||||
|
</i>
|
||||||
|
</span>
|
||||||
|
<span class="finance__entry-label">{{ entry.label }}</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<button
|
<button
|
||||||
v-else
|
v-else
|
||||||
class="finance__entry"
|
class="finance__entry"
|
||||||
@click="onExport"
|
@click="onExport"
|
||||||
>
|
>
|
||||||
{{ entry.label }}
|
<span class="finance__entry-icon finance__entry-icon--export">
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M10 13V4M6.5 7.5 10 4l3.5 3.5M4 15.5h12" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="finance__entry-label">{{ entry.label }}</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -87,7 +175,8 @@ onMounted(() => {
|
|||||||
.flex(row, space-between, center);
|
.flex(row, space-between, center);
|
||||||
flex: 0 0 672px;
|
flex: 0 0 672px;
|
||||||
height: 135px;
|
height: 135px;
|
||||||
padding: 0 34px;
|
// 设计稿 #20:首入口中心 x=1272、尾中心 x=1756,两侧对称 padding 66px 命中
|
||||||
|
padding: 0 66px;
|
||||||
border: 1px solid @finance-card-line;
|
border: 1px solid @finance-card-line;
|
||||||
border-radius: @radius-base;
|
border-radius: @radius-base;
|
||||||
background: @white;
|
background: @white;
|
||||||
@@ -95,8 +184,10 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__entry {
|
&__entry {
|
||||||
.flex(row, center, center);
|
.flex(column, center, center);
|
||||||
height: 100%;
|
gap: 8px;
|
||||||
|
// 等宽保证 space-between 下四入口中心等距 161.5px(设计稿 #20)
|
||||||
|
width: 56px;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: @finance-entry-text;
|
color: @finance-entry-text;
|
||||||
@@ -110,5 +201,56 @@ onMounted(() => {
|
|||||||
color: @primary-strong;
|
color: @primary-strong;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__entry-icon {
|
||||||
|
position: relative;
|
||||||
|
.flex(row, center, center);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--bill {
|
||||||
|
background: @finance-entry-bill-bg;
|
||||||
|
color: @finance-entry-bill-icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--pending {
|
||||||
|
background: @finance-entry-pending-bg;
|
||||||
|
color: @finance-pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--invoice {
|
||||||
|
background: @finance-entry-invoice-bg;
|
||||||
|
color: @finance-entry-invoice-icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--export {
|
||||||
|
background: @finance-entry-export-bg;
|
||||||
|
color: @finance-paid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__entry-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -7px;
|
||||||
|
// 设计稿 #20:badge 叠在图标右上内侧(右缘距图标右约 7px)
|
||||||
|
right: 7px;
|
||||||
|
min-width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: @finance-entry-badge-bg;
|
||||||
|
color: @white;
|
||||||
|
font-size: 9px;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: 16px;
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage, ElButton, ElPagination } from 'element-plus'
|
||||||
import { ConfirmDialog } from '@/components'
|
import { ConfirmDialog } from '@/components'
|
||||||
import { useFinanceStore } from '@/stores/modules/finance'
|
import { useFinanceStore } from '@/stores/modules/finance'
|
||||||
import type { InvoiceTitle, InvoiceTitlePayload } from '@/types/business/finance'
|
import type { InvoiceTitle, InvoiceTitlePayload } from '@/types/business/finance'
|
||||||
@@ -24,7 +24,6 @@ const pageCount = computed(() => Math.max(1, Math.ceil(titles.value.length / PAG
|
|||||||
const pagedTitles = computed(() =>
|
const pagedTitles = computed(() =>
|
||||||
titles.value.slice((page.value - 1) * PAGE_SIZE, page.value * PAGE_SIZE),
|
titles.value.slice((page.value - 1) * PAGE_SIZE, page.value * PAGE_SIZE),
|
||||||
)
|
)
|
||||||
const pages = computed(() => Array.from({ length: Math.max(5, pageCount.value) }, (_, i) => i + 1))
|
|
||||||
|
|
||||||
function go(next: number): void {
|
function go(next: number): void {
|
||||||
if (next < 1 || next > pageCount.value) return
|
if (next < 1 || next > pageCount.value) return
|
||||||
@@ -89,9 +88,9 @@ onMounted(() => {
|
|||||||
<h2 class="invoice-titles__title">
|
<h2 class="invoice-titles__title">
|
||||||
发票管理
|
发票管理
|
||||||
</h2>
|
</h2>
|
||||||
<button class="invoice-titles__create" @click="onCreate">
|
<el-button class="invoice-titles__create" @click="onCreate">
|
||||||
+ 新增抬头
|
+ 新增抬头
|
||||||
</button>
|
</el-button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="invoice-titles__list">
|
<div class="invoice-titles__list">
|
||||||
@@ -108,23 +107,14 @@ onMounted(() => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="invoice-titles__pager" aria-label="分页">
|
<el-pagination
|
||||||
<button class="invoice-titles__page" :disabled="page <= 1" @click="go(page - 1)">
|
class="invoice-titles__pager"
|
||||||
‹
|
layout="prev, pager, next"
|
||||||
</button>
|
:page-size="PAGE_SIZE"
|
||||||
<button
|
:total="titles.length"
|
||||||
v-for="item in pages"
|
:current-page="page"
|
||||||
:key="item"
|
@current-change="go"
|
||||||
class="invoice-titles__page"
|
/>
|
||||||
:class="{ 'is-active': item === page }"
|
|
||||||
@click="go(item)"
|
|
||||||
>
|
|
||||||
{{ item }}
|
|
||||||
</button>
|
|
||||||
<button class="invoice-titles__page" :disabled="page >= pageCount" @click="go(page + 1)">
|
|
||||||
›
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<InvoiceTitleDialog
|
<InvoiceTitleDialog
|
||||||
@@ -197,6 +187,7 @@ onMounted(() => {
|
|||||||
height: 34px;
|
height: 34px;
|
||||||
margin-right: -5px;
|
margin-right: -5px;
|
||||||
border: none;
|
border: none;
|
||||||
|
padding: 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: @primary-strong;
|
background: @primary-strong;
|
||||||
color: @white;
|
color: @white;
|
||||||
@@ -223,13 +214,12 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__pager {
|
&__pager {
|
||||||
.flex(row, flex-end, center);
|
|
||||||
gap: 3.7px;
|
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding-right: 2.4px;
|
padding-right: 2.4px;
|
||||||
}
|
justify-content: flex-end;
|
||||||
|
|
||||||
&__page {
|
:deep(.el-pagination button),
|
||||||
|
:deep(.el-pagination .el-pager li) {
|
||||||
min-width: 21.45px;
|
min-width: 21.45px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
padding: 0 3px;
|
padding: 0 3px;
|
||||||
@@ -240,16 +230,18 @@ onMounted(() => {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
transition: color 0.2s;
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover:not(:disabled) {
|
:deep(.el-pagination .el-pager li.is-active) {
|
||||||
|
color: @primary-strong;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-pagination button:hover:not(:disabled)) {
|
||||||
color: @primary-strong;
|
color: @primary-strong;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active {
|
:deep(.el-pagination button:disabled) {
|
||||||
color: @primary-strong;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { RouterLink } from 'vue-router'
|
import { RouterLink } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage, ElButton, ElTable, ElTableColumn } from 'element-plus'
|
||||||
import { FilterPanel, PaginationBar, StatusTag } from '@/components'
|
import { FilterPanel, PaginationBar, StatusTag } from '@/components'
|
||||||
import { useFinanceStore } from '@/stores/modules/finance'
|
import { useFinanceStore } from '@/stores/modules/finance'
|
||||||
import type { Invoice, InvoiceStatus } from '@/types/business/finance'
|
import type { Invoice, InvoiceStatus } from '@/types/business/finance'
|
||||||
@@ -69,13 +69,8 @@ function onSendEmail(): void {
|
|||||||
ElMessage.success('发票已发送至绑定邮箱(演示)')
|
ElMessage.success('发票已发送至绑定邮箱(演示)')
|
||||||
}
|
}
|
||||||
|
|
||||||
function onToggleSelect(id: string): void {
|
function onSelectionChange(rows: Invoice[]): void {
|
||||||
if (selectedIds.value.includes(id)) {
|
selectedIds.value = rows.map((row) => row.id)
|
||||||
selectedIds.value = selectedIds.value.filter((item) => item !== id)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
selectedIds.value = [...selectedIds.value, id]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -114,7 +109,7 @@ onMounted(() => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="invoices__tabs">
|
<div class="invoices__tabs">
|
||||||
<button
|
<el-button
|
||||||
v-for="tab in STATUS_TABS"
|
v-for="tab in STATUS_TABS"
|
||||||
:key="tab.value"
|
:key="tab.value"
|
||||||
class="invoices__tab"
|
class="invoices__tab"
|
||||||
@@ -122,7 +117,7 @@ onMounted(() => {
|
|||||||
@click="onTab(tab.value)"
|
@click="onTab(tab.value)"
|
||||||
>
|
>
|
||||||
{{ tab.label }}
|
{{ tab.label }}
|
||||||
</button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FilterPanel
|
<FilterPanel
|
||||||
@@ -133,67 +128,121 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="invoices__table card">
|
<div class="invoices__table card">
|
||||||
<table class="invoices__table-el">
|
<el-table
|
||||||
<thead>
|
class="invoices__table-el"
|
||||||
<tr>
|
:data="invoices"
|
||||||
<th class="invoices__check">
|
row-key="id"
|
||||||
多选
|
@selection-change="onSelectionChange"
|
||||||
</th>
|
|
||||||
<th>序号</th>
|
|
||||||
<th>账单名称</th>
|
|
||||||
<th>账单号</th>
|
|
||||||
<th>交易流水号</th>
|
|
||||||
<th>项目名称</th>
|
|
||||||
<th>订单金额</th>
|
|
||||||
<th>状态</th>
|
|
||||||
<th>申请时间</th>
|
|
||||||
<th>操作</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="(invoice, index) in invoices" :key="invoice.id">
|
|
||||||
<td class="invoices__check">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="selectedIds.includes(invoice.id)"
|
|
||||||
@change="onToggleSelect(invoice.id)"
|
|
||||||
>
|
>
|
||||||
</td>
|
<el-table-column
|
||||||
<td>{{ index + 1 }}</td>
|
type="selection"
|
||||||
<td>{{ invoice.planName }}</td>
|
width="48"
|
||||||
<td>{{ invoice.billNo }}</td>
|
/>
|
||||||
<td>{{ invoice.tradeNo ?? '—' }}</td>
|
<el-table-column
|
||||||
<td>{{ invoice.projectName }}</td>
|
type="index"
|
||||||
<td class="invoices__amount">¥{{ formatMoney(invoice.amount) }}</td>
|
label="序号"
|
||||||
<td>
|
width="60"
|
||||||
<StatusTag :label="invoice.status" :variant="STATUS_VARIANTS[invoice.status]" />
|
/>
|
||||||
</td>
|
<el-table-column
|
||||||
<td>{{ invoice.applyTime }}</td>
|
prop="planName"
|
||||||
<td class="invoices__ops">
|
label="账单名称"
|
||||||
<template v-if="invoice.status === '已开票'">
|
min-width="180"
|
||||||
<button class="invoices__link" @click="onPreview">
|
show-overflow-tooltip
|
||||||
预览
|
/>
|
||||||
</button>
|
<el-table-column
|
||||||
<button class="invoices__link" @click="onDownload">
|
prop="billNo"
|
||||||
下载
|
label="账单号"
|
||||||
</button>
|
width="150"
|
||||||
<button class="invoices__link" @click="onSendEmail">
|
show-overflow-tooltip
|
||||||
发送邮箱
|
/>
|
||||||
</button>
|
<el-table-column
|
||||||
|
prop="tradeNo"
|
||||||
|
label="交易流水号"
|
||||||
|
min-width="180"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="projectName"
|
||||||
|
label="项目名称"
|
||||||
|
min-width="160"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="订单金额"
|
||||||
|
width="110"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span class="invoices__amount">¥{{ formatMoney((row as Invoice).amount) }}</span>
|
||||||
</template>
|
</template>
|
||||||
<span v-else-if="invoice.status === '开票中'" class="invoices__dash">—</span>
|
</el-table-column>
|
||||||
<button v-else class="invoices__link" @click="onApply(invoice)">
|
<el-table-column
|
||||||
|
label="状态"
|
||||||
|
width="90"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<StatusTag
|
||||||
|
:label="(row as Invoice).status"
|
||||||
|
:variant="STATUS_VARIANTS[(row as Invoice).status]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="applyTime"
|
||||||
|
label="申请时间"
|
||||||
|
width="140"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="操作"
|
||||||
|
width="180"
|
||||||
|
fixed="right"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span class="invoices__ops">
|
||||||
|
<template v-if="(row as Invoice).status === '已开票'">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
class="invoices__link"
|
||||||
|
@click="onPreview"
|
||||||
|
>
|
||||||
|
预览
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
class="invoices__link"
|
||||||
|
@click="onDownload"
|
||||||
|
>
|
||||||
|
下载
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
class="invoices__link"
|
||||||
|
@click="onSendEmail"
|
||||||
|
>
|
||||||
|
发送邮箱
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<span v-else-if="(row as Invoice).status === '开票中'" class="invoices__dash">—</span>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
class="invoices__link"
|
||||||
|
@click="onApply(row as Invoice)"
|
||||||
|
>
|
||||||
申请开票
|
申请开票
|
||||||
</button>
|
</el-button>
|
||||||
</td>
|
</span>
|
||||||
</tr>
|
</template>
|
||||||
<tr v-if="invoices.length === 0">
|
</el-table-column>
|
||||||
<td colspan="10" class="invoices__empty text-secondary">
|
|
||||||
暂无发票记录
|
<template #empty>
|
||||||
</td>
|
<span class="invoices__empty text-secondary">暂无发票记录</span>
|
||||||
</tr>
|
</template>
|
||||||
</tbody>
|
</el-table>
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PaginationBar
|
<PaginationBar
|
||||||
@@ -271,22 +320,27 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__tabs {
|
&__tabs {
|
||||||
.flex(row, flex-start);
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
gap: @spacing-sm;
|
gap: @spacing-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__tab {
|
&__tab {
|
||||||
height: 34px;
|
height: 34px;
|
||||||
|
margin: 0;
|
||||||
padding: 0 @spacing-base;
|
padding: 0 @spacing-base;
|
||||||
border: 1px solid @border-color;
|
border: 1px solid @border-color;
|
||||||
border-radius: @radius-base;
|
border-radius: @radius-base;
|
||||||
background: @white;
|
background: @white;
|
||||||
color: @text-color;
|
color: @text-color;
|
||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
|
font-weight: 400;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: @primary-color;
|
border-color: @primary-color;
|
||||||
|
background: @white;
|
||||||
color: @primary-color;
|
color: @primary-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,6 +348,12 @@ onMounted(() => {
|
|||||||
border-color: @primary-color;
|
border-color: @primary-color;
|
||||||
background: @primary-color;
|
background: @primary-color;
|
||||||
color: @white;
|
color: @white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: @primary-color;
|
||||||
|
background: @primary-color;
|
||||||
|
color: @white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,32 +367,35 @@ onMounted(() => {
|
|||||||
|
|
||||||
&__table-el {
|
&__table-el {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
|
||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
color: @text-color;
|
color: @text-color;
|
||||||
|
|
||||||
th,
|
:deep(th.el-table__cell) {
|
||||||
td {
|
background: @bg-color-light;
|
||||||
padding: @spacing-sm;
|
|
||||||
text-align: left;
|
|
||||||
white-space: nowrap;
|
|
||||||
border-bottom: 1px solid @border-color;
|
border-bottom: 1px solid @border-color;
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
background: @bg-color-light;
|
|
||||||
color: @text-color-secondary;
|
color: @text-color-secondary;
|
||||||
|
font-size: @font-size-base;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr:hover {
|
:deep(td.el-table__cell) {
|
||||||
|
padding: @spacing-sm 0;
|
||||||
|
border-bottom: 1px solid @border-color;
|
||||||
|
font-size: @font-size-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__body tr:hover > td.el-table__cell) {
|
||||||
background: @bg-color-light;
|
background: @bg-color-light;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&__check {
|
:deep(.el-table .cell) {
|
||||||
width: 48px;
|
padding-left: @spacing-sm;
|
||||||
text-align: center;
|
padding-right: @spacing-sm;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__amount {
|
&__amount {
|
||||||
@@ -341,17 +404,23 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__ops {
|
&__ops {
|
||||||
.flex(row, flex-start);
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
gap: @spacing-sm;
|
gap: @spacing-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
color: @primary-color;
|
color: @primary-color;
|
||||||
font-size: @font-size-base;
|
font-size: @font-size-base;
|
||||||
transition: color 0.2s;
|
transition: color 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: @primary-hover;
|
color: @primary-hover;
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { computed, onMounted } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { ElButton } from 'element-plus'
|
||||||
import { useFinanceStore } from '@/stores/modules/finance'
|
import { useFinanceStore } from '@/stores/modules/finance'
|
||||||
import { formatMoney } from '@/utils/format'
|
import { formatMoney } from '@/utils/format'
|
||||||
|
|
||||||
@@ -110,13 +111,12 @@ onMounted(() => {
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="pay-result__footer">
|
<div class="pay-result__footer">
|
||||||
<button
|
<el-button
|
||||||
class="pay-result__back"
|
class="pay-result__back"
|
||||||
type="button"
|
|
||||||
@click="router.push('/finance/bills')"
|
@click="router.push('/finance/bills')"
|
||||||
>
|
>
|
||||||
返回
|
返回
|
||||||
</button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -311,6 +311,7 @@ onMounted(() => {
|
|||||||
width: 84px;
|
width: 84px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border: none;
|
border: none;
|
||||||
|
padding: 0;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: @primary-strong;
|
background: @primary-strong;
|
||||||
color: @white;
|
color: @white;
|
||||||
|
|||||||
Reference in New Issue
Block a user