|
|
@@ -48,6 +48,14 @@
|
|
|
</li>
|
|
|
</ul>
|
|
|
</div>
|
|
|
+ <div class="card" @click="showFinancePopup = true">
|
|
|
+ <h4>课题组经费统计</h4>
|
|
|
+ <div class="balance-display">
|
|
|
+ <span class="amount">{{ financeStats.calculatedBalance.toFixed(2) }}</span>
|
|
|
+ <p class="label">可用余额(¥)</p>
|
|
|
+ <van-icon name="arrow" class="arrow-icon" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<div class="card" @click="showOperatingGuidelines">
|
|
|
<h4>平台入室操作指引</h4>
|
|
|
</div>
|
|
|
@@ -70,6 +78,89 @@
|
|
|
</van-button>
|
|
|
</footer>
|
|
|
|
|
|
+ <van-popup position="bottom" v-model:show="showFinancePopup" round>
|
|
|
+ <div class="finance-detail">
|
|
|
+ <div class="detail-header">
|
|
|
+ <h3>经费统计详情</h3>
|
|
|
+ <van-icon name="cross" @click="showFinancePopup = false" />
|
|
|
+ </div>
|
|
|
+ <div class="detail-content">
|
|
|
+ <div class="detail-item" @click="showListPopup = true">
|
|
|
+ <span class="label">经费卡数量</span>
|
|
|
+ <div class="value-with-arrow">
|
|
|
+ <span class="value">{{ financeStats.accountCount }}</span>
|
|
|
+ <van-icon name="arrow" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">授信额度</span>
|
|
|
+ <span class="value">¥{{ financeStats.totalCredit.toFixed(2) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">可用余额</span>
|
|
|
+ <span class="value">¥{{ financeStats.totalBalance.toFixed(2) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">锁定金额</span>
|
|
|
+ <span class="value">¥{{ financeStats.totalLocked.toFixed(2) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item highlight">
|
|
|
+ <span class="label">最终可用额度</span>
|
|
|
+ <span class="value">¥{{ financeStats.calculatedBalance.toFixed(2) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="divider"></div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">已出账单</span>
|
|
|
+ <span class="value">¥{{ financeStats.totalIssued.toFixed(2) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">未出账单</span>
|
|
|
+ <span class="value">¥{{ financeStats.totalUnpaid.toFixed(2) }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="detail-footer">
|
|
|
+ <p class="formula-tip">* 计算公式:授信额度 + 可用余额 - 锁定金额</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
+ <van-popup position="bottom" v-model:show="showListPopup" round closeable>
|
|
|
+ <div class="finance-list">
|
|
|
+ <div class="list-header">
|
|
|
+ <h3>经费卡列表</h3>
|
|
|
+ </div>
|
|
|
+ <div class="list-content">
|
|
|
+ <div v-for="item in financeList" :key="item.id" class="finance-card-item">
|
|
|
+ <div class="card-title">
|
|
|
+ <span class="account-no">{{ item.finAccount }}</span>
|
|
|
+ <span class="org-name">{{ item.finOrgName }}-{{ item.projName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="card-details">
|
|
|
+ <div class="grid">
|
|
|
+ <div class="grid-item">
|
|
|
+ <p class="label">可用余额</p>
|
|
|
+ <p class="value">¥{{ (item.finAvailBalance || 0).toFixed(2) }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="grid-item">
|
|
|
+ <p class="label">授信额度</p>
|
|
|
+ <p class="value">¥{{ (item.finCreditLimit || 0).toFixed(2) }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="grid-item">
|
|
|
+ <p class="label">锁定金额</p>
|
|
|
+ <p class="value">¥{{ (item.finLockAmount || 0).toFixed(2) }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="grid-item">
|
|
|
+ <p class="label">已出账单</p>
|
|
|
+ <p class="value">¥{{ (item.finIssuedBill || 0).toFixed(2) }}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <van-empty v-if="financeList.length === 0" description="暂无经费卡信息" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </van-popup>
|
|
|
+
|
|
|
<van-popup position="bottom" v-model:show="showPopup" :style="{ padding: '20px' }">
|
|
|
<div class="operation-guide">
|
|
|
<div class="guide-header">
|
|
|
@@ -121,6 +212,7 @@ import { useUserInfo } from '/@/stores/userInfo'
|
|
|
import { Local, Session } from '/@/utils/storage'
|
|
|
import { useBillApi } from '/@/api/instr/finance/bill'
|
|
|
import { useLoginApi } from '/@/api/login'
|
|
|
+import { useFinaceApi } from '/@/api/finace'
|
|
|
|
|
|
import { scanCodeWxUrl, InstSwitchType } from '/@/constants/pageConstants'
|
|
|
|
|
|
@@ -128,6 +220,7 @@ import wx from 'weixin-js-sdk'
|
|
|
|
|
|
const billApi = useBillApi()
|
|
|
const loginApi = useLoginApi()
|
|
|
+const financeApi = useFinaceApi()
|
|
|
|
|
|
const router = useRouter()
|
|
|
const storesUseUserInfo = useUserInfo()
|
|
|
@@ -144,6 +237,21 @@ const billInfo = ref<{
|
|
|
totalFee: 0,
|
|
|
})
|
|
|
|
|
|
+const financeStats = ref({
|
|
|
+ accountCount: 0,
|
|
|
+ totalBalance: 0,
|
|
|
+ totalCredit: 0,
|
|
|
+ totalLocked: 0,
|
|
|
+ totalIssued: 0,
|
|
|
+ totalUnpaid: 0,
|
|
|
+ calculatedBalance: 0, // 授信 + 可用 - 锁定
|
|
|
+})
|
|
|
+
|
|
|
+const financeList = ref<any[]>([])
|
|
|
+
|
|
|
+const showFinancePopup = ref<boolean>(false)
|
|
|
+const showListPopup = ref<boolean>(false)
|
|
|
+
|
|
|
const handleStepList = ref([
|
|
|
{
|
|
|
title: '第一步:入室培训和考试',
|
|
|
@@ -239,8 +347,37 @@ const getBillInfo = async () => {
|
|
|
billInfo.value = res?.data
|
|
|
}
|
|
|
|
|
|
+// 获取经费卡统计信息
|
|
|
+const getFinanceStats = async () => {
|
|
|
+ if (!userInfos.value?.projectGroup?.id) return
|
|
|
+ const [err, res]: ToResponse = await to(
|
|
|
+ financeApi.getFinanceAccountList({
|
|
|
+ projId: userInfos.value.projectGroup.id,
|
|
|
+ })
|
|
|
+ )
|
|
|
+ if (err) return
|
|
|
+ const list = res?.data?.list || []
|
|
|
+ financeList.value = list
|
|
|
+ const totalBalance = list.reduce((sum: number, item: any) => sum + (item.finAvailBalance || 0), 0)
|
|
|
+ const totalCredit = list.reduce((sum: number, item: any) => sum + (item.finCreditLimit || 0), 0)
|
|
|
+ const totalLocked = list.reduce((sum: number, item: any) => sum + (item.finLockAmount || 0), 0)
|
|
|
+ const totalIssued = list.reduce((sum: number, item: any) => sum + (item.finIssuedBill || 0), 0)
|
|
|
+ const totalUnpaid = list.reduce((sum: number, item: any) => sum + (item.finUnpaidBill || 0), 0)
|
|
|
+
|
|
|
+ financeStats.value = {
|
|
|
+ accountCount: list.length,
|
|
|
+ totalBalance,
|
|
|
+ totalCredit,
|
|
|
+ totalLocked,
|
|
|
+ totalIssued,
|
|
|
+ totalUnpaid,
|
|
|
+ calculatedBalance: totalCredit + totalBalance - totalLocked,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
getBillInfo()
|
|
|
+ getFinanceStats()
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
@@ -342,6 +479,35 @@ onMounted(() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ .balance-display {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ padding: 15px 0;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ .amount {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #1c9bfd;
|
|
|
+ }
|
|
|
+
|
|
|
+ .label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ margin-top: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .arrow-icon {
|
|
|
+ position: absolute;
|
|
|
+ right: 0;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ color: #ccc;
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.nav {
|
|
|
display: flex;
|
|
|
margin: 10px 0;
|
|
|
@@ -349,21 +515,25 @@ onMounted(() => {
|
|
|
justify-content: space-between;
|
|
|
|
|
|
li {
|
|
|
- flex: 0 0 25%;
|
|
|
+ flex: 0 0 33.33%;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
|
|
|
span {
|
|
|
- font-size: 20px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
}
|
|
|
|
|
|
p {
|
|
|
margin-top: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #666;
|
|
|
}
|
|
|
|
|
|
- &:nth-child(n + 5) {
|
|
|
+ &:nth-child(n + 4) {
|
|
|
margin-top: 10px;
|
|
|
}
|
|
|
}
|
|
|
@@ -381,6 +551,176 @@ onMounted(() => {
|
|
|
padding: 20px !important;
|
|
|
}
|
|
|
|
|
|
+.finance-detail {
|
|
|
+ .detail-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ padding-bottom: 15px;
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
+
|
|
|
+ h3 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .van-icon {
|
|
|
+ font-size: 18px;
|
|
|
+ color: #999;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .detail-content {
|
|
|
+ .detail-item {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ font-size: 14px;
|
|
|
+
|
|
|
+ .label {
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+
|
|
|
+ .value {
|
|
|
+ color: #333;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .value-with-arrow {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ color: #1c9bfd;
|
|
|
+ font-weight: 500;
|
|
|
+
|
|
|
+ .van-icon {
|
|
|
+ margin-left: 4px;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.highlight {
|
|
|
+ margin-top: 5px;
|
|
|
+ padding-top: 15px;
|
|
|
+ border-top: 1px solid #f5f5f5;
|
|
|
+
|
|
|
+ .label {
|
|
|
+ color: #333;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+
|
|
|
+ .value {
|
|
|
+ color: #1c9bfd;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .divider {
|
|
|
+ height: 1px;
|
|
|
+ background-color: #eee;
|
|
|
+ margin: 15px 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .detail-footer {
|
|
|
+ margin-top: 20px;
|
|
|
+ padding: 15px;
|
|
|
+ background: #f8f9fa;
|
|
|
+ border-radius: 8px;
|
|
|
+
|
|
|
+ .formula-tip {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #999;
|
|
|
+ line-height: 1.4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.finance-list {
|
|
|
+ max-height: 80vh;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .list-header {
|
|
|
+ padding: 20px;
|
|
|
+ border-bottom: 1px solid #f5f5f5;
|
|
|
+
|
|
|
+ h3 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .list-content {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 10px 15px 30px;
|
|
|
+
|
|
|
+ .finance-card-item {
|
|
|
+ background: #f8f9fa;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 15px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ border: 1px solid #eee;
|
|
|
+
|
|
|
+ .card-title {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding-bottom: 10px;
|
|
|
+ border-bottom: 1px dashed #ddd;
|
|
|
+
|
|
|
+ .account-no {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .org-name {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #1c9bfd;
|
|
|
+ background: rgba(28, 155, 253, 0.1);
|
|
|
+ padding: 2px 8px;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .card-details {
|
|
|
+ .grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ gap: 12px;
|
|
|
+
|
|
|
+ .grid-item {
|
|
|
+ .label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #999;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .value {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.operation-guide {
|
|
|
max-height: 70vh;
|
|
|
overflow-y: auto;
|