未知(继上次中断)
This commit is contained in:
@@ -1,14 +1,5 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">{{ TEXT.modules.feeContracts.title }}</h1>
|
||||
</div>
|
||||
<el-button type="primary" :disabled="!canWrite" @click="goNew" class="header-action-btn">
|
||||
<el-icon class="el-icon--left"><Plus /></el-icon>
|
||||
{{ TEXT.modules.feeContracts.newTitle }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="overview">
|
||||
<el-row :gutter="20">
|
||||
@@ -55,34 +46,39 @@
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-card class="filter-card" shadow="never">
|
||||
<el-form :inline="true" :model="filters" class="filter-form">
|
||||
<el-form-item label="" class="filter-item">
|
||||
<el-select v-model="filters.centerId" clearable :placeholder="TEXT.common.fields.site" class="filter-select">
|
||||
<template #prefix>
|
||||
<el-icon><Location /></el-icon>
|
||||
</template>
|
||||
<el-option
|
||||
v-for="site in sites"
|
||||
:key="site.id"
|
||||
:label="site.name || TEXT.common.fallback"
|
||||
:value="site.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="" class="filter-item">
|
||||
<el-input v-model="filters.q" :placeholder="TEXT.common.placeholders.keyword" clearable class="filter-input">
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item class="filter-actions">
|
||||
<el-button type="primary" @click="load">{{ TEXT.common.actions.search }}</el-button>
|
||||
<el-button @click="resetFilters">{{ TEXT.common.actions.reset }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="main-content-card" v-if="!errorMessage && !loading">
|
||||
<div class="filter-container">
|
||||
<el-form :inline="true" :model="filters" class="filter-form">
|
||||
<el-form-item label="" class="filter-item">
|
||||
<el-select v-model="filters.centerId" clearable :placeholder="TEXT.common.fields.site" class="filter-select">
|
||||
<template #prefix>
|
||||
<el-icon><Location /></el-icon>
|
||||
</template>
|
||||
<el-option
|
||||
v-for="site in sites"
|
||||
:key="site.id"
|
||||
:label="site.name || TEXT.common.fallback"
|
||||
:value="site.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="" class="filter-item">
|
||||
<el-input v-model="filters.q" :placeholder="TEXT.common.placeholders.keyword" clearable class="filter-input">
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item class="filter-actions">
|
||||
<el-button @click="resetFilters">{{ TEXT.common.actions.reset }}</el-button>
|
||||
</el-form-item>
|
||||
<div class="filter-spacer"></div>
|
||||
<el-button type="primary" :disabled="!canWrite" @click="goNew" class="header-action-btn">
|
||||
<el-icon class="el-icon--left"><Plus /></el-icon>
|
||||
{{ TEXT.modules.feeContracts.newTitle }}
|
||||
</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<StateError v-if="errorMessage" :description="errorMessage">
|
||||
<template #action>
|
||||
@@ -92,7 +88,6 @@
|
||||
|
||||
<StateLoading v-else-if="loading" :rows="6" />
|
||||
|
||||
<el-card v-else class="table-card" shadow="never">
|
||||
<el-table
|
||||
:data="sortedContracts"
|
||||
style="width: 100%"
|
||||
@@ -100,26 +95,26 @@
|
||||
:row-class-name="contractRowClass"
|
||||
:header-cell-style="{ background: '#f8f9fb' }"
|
||||
>
|
||||
<el-table-column prop="center_name" :label="TEXT.common.fields.site" min-width="180">
|
||||
<el-table-column prop="center_name" :label="TEXT.common.fields.site" min-width="160">
|
||||
<template #default="scope">
|
||||
<div class="site-cell">
|
||||
<span class="site-name">{{ scope.row.center_name || TEXT.common.fallback }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.modules.feeContracts.contractAmount" width="160" align="right">
|
||||
<el-table-column :label="TEXT.modules.feeContracts.contractAmount" width="140" align="right">
|
||||
<template #default="scope">
|
||||
<span class="amount-text">{{ formatAmountWan(scope.row.contract_amount) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.modules.feeContracts.caseProgress" min-width="150" align="center">
|
||||
<el-table-column :label="TEXT.modules.feeContracts.caseProgress" min-width="140" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag effect="plain" type="info">
|
||||
{{ displayCases(scope.row.contract_cases, scope.row.actual_cases) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.modules.feeContracts.paidSummary" min-width="180">
|
||||
<el-table-column :label="TEXT.modules.feeContracts.paidSummary" min-width="200">
|
||||
<template #default="scope">
|
||||
<div class="summary-line">
|
||||
<span>{{ TEXT.modules.feeContracts.paidTotal }}</span>
|
||||
@@ -132,7 +127,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.modules.feeContracts.verifySummary" min-width="180">
|
||||
<el-table-column :label="TEXT.modules.feeContracts.verifySummary" min-width="200">
|
||||
<template #default="scope">
|
||||
<div class="summary-line">
|
||||
<span>{{ TEXT.modules.feeContracts.verifiedTotal }}</span>
|
||||
@@ -145,7 +140,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.modules.feeContracts.recentDates" min-width="200">
|
||||
<el-table-column :label="TEXT.modules.feeContracts.recentDates" min-width="180">
|
||||
<template #default="scope">
|
||||
<div class="date-line">
|
||||
<span class="date-label">{{ TEXT.modules.feeContracts.lastPaid }}</span>
|
||||
@@ -157,12 +152,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.common.fields.remark" min-width="180" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ scope.row.remark || TEXT.common.fallback }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.common.labels.actions" width="120">
|
||||
<el-table-column :label="TEXT.common.labels.actions" width="100">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
@@ -182,7 +172,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { computed, onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { Coin, Wallet, CircleCheck, CircleClose, Plus, Location, Search } from "@element-plus/icons-vue";
|
||||
@@ -214,14 +204,26 @@ const siteActiveMap = computed(() => {
|
||||
return map;
|
||||
});
|
||||
const isInactiveSite = (siteId?: string) => !!siteId && siteActiveMap.value[siteId] === false;
|
||||
const filteredContracts = computed(() => {
|
||||
const keyword = filters.q.trim().toLowerCase();
|
||||
return contracts.value.filter((item) => {
|
||||
if (filters.centerId && item?.center_id !== filters.centerId) return false;
|
||||
if (keyword) {
|
||||
const fields = [item?.center_name, item?.contract_no, item?.contract_name];
|
||||
const matched = fields.some((field) => String(field || "").toLowerCase().includes(keyword));
|
||||
if (!matched) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
const sortedContracts = computed(() =>
|
||||
[...contracts.value].sort((a, b) => Number(isInactiveSite(a?.center_id)) - Number(isInactiveSite(b?.center_id)))
|
||||
[...filteredContracts.value].sort((a, b) => Number(isInactiveSite(a?.center_id)) - Number(isInactiveSite(b?.center_id)))
|
||||
);
|
||||
const contractRowClass = ({ row }: { row: any }) =>
|
||||
`${row?.id ? "clickable-row" : ""}${isInactiveSite(row?.center_id) ? " row-inactive" : ""}`.trim();
|
||||
|
||||
const filters = reactive({
|
||||
centerId: "",
|
||||
centerId: study.currentSite?.id || "",
|
||||
q: "",
|
||||
});
|
||||
|
||||
@@ -231,7 +233,7 @@ const toNumber = (value: any) => {
|
||||
};
|
||||
|
||||
const overview = computed(() => {
|
||||
const totals = contracts.value.reduce(
|
||||
const totals = filteredContracts.value.reduce(
|
||||
(acc, item) => {
|
||||
acc.totalContractAmount += toNumber(item.contract_amount);
|
||||
acc.totalPaidAmount += toNumber(item.paid_total);
|
||||
@@ -248,7 +250,7 @@ const overview = computed(() => {
|
||||
);
|
||||
return {
|
||||
...totals,
|
||||
centerCount: contracts.value.length,
|
||||
centerCount: filteredContracts.value.length,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -269,11 +271,7 @@ const load = async () => {
|
||||
loading.value = true;
|
||||
errorMessage.value = "";
|
||||
try {
|
||||
const { data } = await listContractFees({
|
||||
projectId,
|
||||
centerId: filters.centerId || undefined,
|
||||
q: filters.q || undefined,
|
||||
});
|
||||
const { data } = await listContractFees({ projectId });
|
||||
contracts.value = data?.data || [];
|
||||
} catch (e: any) {
|
||||
errorMessage.value = e?.response?.data?.message || TEXT.common.messages.loadFailed;
|
||||
@@ -285,7 +283,6 @@ const load = async () => {
|
||||
const resetFilters = () => {
|
||||
filters.centerId = "";
|
||||
filters.q = "";
|
||||
load();
|
||||
};
|
||||
|
||||
const goNew = () => router.push("/fees/contracts/new");
|
||||
@@ -327,6 +324,19 @@ const formatAmountWan = (value: any) => {
|
||||
return (numberValue / 10000).toFixed(2);
|
||||
};
|
||||
|
||||
watch(() => filters.centerId, (val: string) => {
|
||||
if (val) {
|
||||
const matched = sites.value.find(s => s.id === val);
|
||||
if (matched) study.setCurrentSite(matched);
|
||||
} else {
|
||||
study.setCurrentSite(null);
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => study.currentSite, (newSite: any) => {
|
||||
filters.centerId = newSite?.id || "";
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await loadSites();
|
||||
load();
|
||||
@@ -337,58 +347,21 @@ onMounted(async () => {
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
.main-content-card :deep(.el-card__body) {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: var(--ctms-text-main);
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
margin: 6px 0 0;
|
||||
font-size: 14px;
|
||||
color: var(--ctms-text-secondary);
|
||||
}
|
||||
|
||||
.header-action-btn {
|
||||
height: 36px;
|
||||
padding: 0 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.overview {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.filter-card :deep(.el-card__body) {
|
||||
padding: 0;
|
||||
.filter-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--ctms-border-color);
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
@@ -398,14 +371,17 @@ onMounted(async () => {
|
||||
|
||||
.filter-select,
|
||||
.filter-input {
|
||||
width: 240px;
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
margin-left: auto !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.filter-spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--ctms-border-color);
|
||||
@@ -424,22 +400,32 @@ onMounted(async () => {
|
||||
|
||||
.summary-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
margin-bottom: 4px;
|
||||
color: var(--ctms-text-regular);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.summary-line:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.summary-line span:first-child {
|
||||
color: var(--ctms-text-secondary);
|
||||
}
|
||||
|
||||
.amount-highlight {
|
||||
font-weight: 600;
|
||||
font-family: var(--el-font-family);
|
||||
}
|
||||
|
||||
.summary-line .amount-highlight,
|
||||
.summary-line .amount-muted {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.amount-highlight.success { color: var(--el-color-success); }
|
||||
.amount-highlight.warning { color: var(--el-color-warning); }
|
||||
.amount-highlight.info { color: var(--el-color-info); }
|
||||
@@ -447,9 +433,11 @@ onMounted(async () => {
|
||||
|
||||
.date-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
margin-bottom: 2px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.date-label {
|
||||
|
||||
Reference in New Issue
Block a user