Commit a9cca2fa by Sweet Zhang

通用表单样式修改,查询页面动作区域按钮封装调整

parent 051a8a7b
......@@ -17,11 +17,20 @@
margin: 0px;
padding: 0px;
}
body{
font-family: 'Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif' !important;
font-size: 14px !important;
letter-spacing: 0.3px;
color: #383838 !important;
}
/* element-plus表单的label统一样式 */
.el-form-item__label {
color: #383838 !important;
}
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
color: #383838;
padding: 0.2em 0;
}
......
......@@ -222,3 +222,37 @@ export function policyData(data) {
data: data
})
}
// 应付款管理列表
export function expectedFortuneList(data) {
return request({
url: '/csf/api/expectedFortune/list',
method: 'post',
data: data
})
}
// 应收款管理列表
export function receivedFortuneList(data) {
return request({
url: '/csf/api/CommissionExpected/queryCommissionExpectedByPage',
method: 'post',
data: data
})
}
// 统计应收数据总金额、总入账金额、待入账金额、已入账比例(已入账金额-总金额)、总保单数
export function commissionExpectedStatistics(data) {
return request({
url: '/csf/api/CommissionExpected/statistics',
method: 'post',
data: data
})
}
// 统计计算统计数据 预计发佣金额 HKD、已出账金额 HKD、待出账金额 HKD、总保单数、总保费 HKD
export function expectedFortuneStatistics(data) {
return request({
url: '/csf/api/expectedFortune/statistics',
method: 'post',
data: data
})
}
......@@ -28,174 +28,360 @@
<!-- 按钮区域 -->
<el-col :span="24" class="operationBtn">
<div class="operationLeft">
<template v-for="left in leftOperationBtns" :key="left.label">
<!-- 左侧按钮 -->
<template v-for="btn in leftButtons" :key="btn.key || btn.label">
<el-button
:type="left.type || 'primary'"
:icon="left.icon"
:size="left.size || 'default'"
@click="left.click"
:disabled="left.disabled"
:loading="left.loading"
:type="btn.type || 'primary'"
:icon="btn.icon"
:size="btn.size || 'default'"
@click="handleButtonClick(btn)"
:disabled="btn.disabled"
:loading="btn.loading"
:class="btn.customClass"
>
{{ left.label }}
{{ btn.label }}
</el-button>
</template>
<!-- 左侧插槽 -->
<slot name="leftButtons"></slot>
</div>
<div class="operationRight">
<template v-for="right in rightOperationBtns" :key="right.label">
<!-- 右侧按钮 -->
<template v-for="btn in rightButtons" :key="btn.key || btn.label">
<el-button
:type="right.type || 'primary'"
:icon="right.icon"
:size="right.size || 'default'"
@click="right.click"
:disabled="right.disabled"
:loading="right.loading"
:type="btn.type || 'primary'"
:icon="btn.icon"
:size="btn.size || 'default'"
@click="handleButtonClick(btn)"
:disabled="btn.disabled"
:loading="btn.loading"
:class="btn.customClass"
>
{{ right.label }}
{{ btn.label }}
</el-button>
</template>
<!-- 右侧插槽 -->
<slot name="rightButtons"></slot>
</div>
</el-col>
<!-- 表格插槽 -->
<slot name="table"></slot>
<el-col :span="24">
<slot name="table"></slot>
</el-col>
<!-- 分页区域 -->
<el-col :span="24" v-if="showPagination">
<div class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="pageSizes"
:layout="paginationLayout"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script setup>
<script setup lang="ts">
import { ref, computed, watch, nextTick, onMounted, defineExpose } from 'vue'
import { Bottom, Top } from '@element-plus/icons-vue'
const props = defineProps({
// 按钮类型定义
export interface OperationButton {
// 按钮唯一标识(用于匹配默认按钮配置)
key?: string
// 显示文本(如果提供了key,会自动使用默认文本)
label?: string
// 按钮类型
type?: 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'text'
// 图标名称
icon?: string
// 按钮尺寸
size?: 'large' | 'default' | 'small'
// 显示位置
direction?: 'left' | 'right'
// 是否禁用
disabled?: boolean
// 加载状态
loading?: boolean
// 是否显示(默认显示)
visible?: boolean
// 点击事件回调函数
click?: () => void | Promise<void>
// 权限标识
permission?: string
// 自定义样式类
customClass?: string
}
// 组件 Props
interface Props {
// 操作按钮列表
operationBtnList: {
type: Array,
default: () => []
},
operationBtnList?: OperationButton[]
// 是否显示查询表单
showSearchForm: {
type: Boolean,
default: true
},
// 分页相关
showPagination: {
type: Boolean,
default: false
},
total: {
type: Number,
default: 0
},
currentPage: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
pageSizes: {
type: Array,
default: () => [10, 20, 50, 100]
},
paginationLayout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
showSearchForm?: boolean
// 是否显示分页
showPagination?: boolean
// 总条数
total?: number
// 当前页码
currentPage?: number
// 每页显示条数
pageSize?: number
// 每页条数选项
pageSizes?: number[]
// 分页布局
paginationLayout?: string
// 默认显示的查询条件数量
defaultVisibleConditions: {
type: Number,
default: 6
}
defaultVisibleConditions?: number
// 是否显示默认按钮
showDefaultButtons?: boolean
// 需要显示的默认按钮
visibleDefaultButtons?: ('add' | 'import' | 'export' | 'reset' | 'query')[]
}
const props = withDefaults(defineProps<Props>(), {
operationBtnList: () => [],
showSearchForm: true,
showPagination: false,
total: 0,
currentPage: 1,
pageSize: 10,
pageSizes: () => [10, 20, 50, 100],
paginationLayout: 'total, sizes, prev, pager, next, jumper',
defaultVisibleConditions: 8,
showDefaultButtons: true,
visibleDefaultButtons: () => ['add', 'import', 'export', 'reset', 'query']
})
const emit = defineEmits(['size-change', 'current-change', 'btn-click', 'search-toggle'])
const emit = defineEmits<{
// 分页事件
'size-change': [size: number]
'current-change': [page: number]
// 按钮点击事件
'btn-click': [btn: OperationButton]
// 搜索展开/收起
'search-toggle': [expanded: boolean]
// 默认按钮事件(为方便使用保留)
'add': []
'import': []
'export': []
'reset': []
'query': []
}>()
// 响应式数据
const isExpanded = ref(false)
const hasMoreCondition = ref(false)
const searchFormRef = ref(null)
const searchFormRef = ref<HTMLElement | null>(null)
const currentPage = ref(props.currentPage)
const pageSize = ref(props.pageSize)
// 计算属性
const leftOperationBtns = computed(() => {
return props.operationBtnList.filter(btn => btn.direction === 'left' || !btn.direction)
// 默认按钮配置(作为基础配置)
const defaultButtonConfigs = {
add: {
key: 'add',
label: '新增',
icon: 'Plus',
type: 'primary' as const,
direction: 'left' as const,
size: 'default' as const
},
import: {
key: 'import',
label: '导入',
icon: 'Upload',
type: 'primary' as const,
direction: 'left' as const,
size: 'default' as const
},
export: {
key: 'export',
label: '导出',
icon: 'Download',
type: 'success' as const,
direction: 'right' as const,
size: 'default' as const
},
reset: {
key: 'reset',
label: '重置',
icon: 'Refresh',
type: 'info' as const,
direction: 'right' as const,
size: 'default' as const
},
query: {
key: 'query',
label: '查询',
icon: 'Search',
type: 'primary' as const,
direction: 'right' as const,
size: 'default' as const
}
}
// 计算最终按钮配置
const computedButtons = computed(() => {
const result: OperationButton[] = []
// 1. 添加默认按钮(如果开启)
if (props.showDefaultButtons) {
props.visibleDefaultButtons.forEach(key => {
if (defaultButtonConfigs[key]) {
const defaultBtn = { ...defaultButtonConfigs[key] }
// 查找是否有自定义配置覆盖
const customConfig = props.operationBtnList.find(btn => btn.key === key)
if (customConfig) {
// 合并配置:自定义配置覆盖默认配置
Object.assign(defaultBtn, customConfig)
}
result.push(defaultBtn)
}
})
}
// 2. 添加没有匹配到默认按钮的自定义按钮
props.operationBtnList.forEach(customBtn => {
// 如果这个按钮已经有默认配置了,就跳过(已经在上面处理了)
const isDefaultBtn = customBtn.key && props.visibleDefaultButtons.includes(customBtn.key as any)
const alreadyAdded = result.some(btn => btn.key === customBtn.key)
if (!isDefaultBtn && !alreadyAdded) {
result.push(customBtn)
}
})
return result
})
// 计算左侧按钮
const leftButtons = computed(() => {
return computedButtons.value.filter(btn =>
(btn.direction === 'left' || (!btn.direction && props.showDefaultButtons)) &&
(btn.visible !== false)
)
})
const rightOperationBtns = computed(() => {
return props.operationBtnList.filter(btn => btn.direction === 'right')
// 计算右侧按钮
const rightButtons = computed(() => {
return computedButtons.value.filter(btn =>
btn.direction === 'right' &&
(btn.visible !== false)
)
})
// 方法
const toggleExpand = () => {
isExpanded.value = !isExpanded.value
emit('search-toggle', isExpanded.value)
checkConditions()
}
const checkConditions = () => {
if (!searchFormRef.value) return
if (!searchFormRef.value || !props.showSearchForm) return
nextTick(() => {
// 查找表单中的所有表单项
const formItems = searchFormRef.value.querySelectorAll('.el-form-item')
const formButtons = searchFormRef.value.querySelectorAll('.el-button')
const totalItems = formItems.length + formButtons.length
hasMoreCondition.value = totalItems > props.defaultVisibleConditions
const formItems = Array.from(
searchFormRef.value!.querySelectorAll('.el-form-item, .el-button-group')
)
hasMoreCondition.value = formItems.length > props.defaultVisibleConditions
if (!isExpanded.value && hasMoreCondition.value) {
// 隐藏超出默认数量的条件
let visibleCount = 0
let hiddenCount = 0
// 处理表单项
formItems.forEach((item, index) => {
if (visibleCount < props.defaultVisibleConditions) {
item.style.display = ''
visibleCount++
if (index >= props.defaultVisibleConditions) {
(item as HTMLElement).style.display = 'none'
} else {
item.style.display = 'none'
hiddenCount++
}
})
// 处理按钮项
formButtons.forEach((button, index) => {
if (visibleCount < props.defaultVisibleConditions) {
button.style.display = ''
visibleCount++
} else {
button.style.display = 'none'
hiddenCount++
(item as HTMLElement).style.display = ''
}
})
} else {
// 显示所有条件
formItems.forEach(item => (item.style.display = ''))
formButtons.forEach(button => (button.style.display = ''))
formItems.forEach(item => {
(item as HTMLElement).style.display = ''
})
}
})
}
const handleBtnClick = btn => {
// 处理按钮点击
const handleButtonClick = async (btn: OperationButton) => {
// 触发通用按钮点击事件
emit('btn-click', btn)
// 触发默认按钮的特定事件(保持向后兼容)
if (btn.key) {
switch (btn.key) {
case 'add':
emit('add')
break
case 'import':
emit('import')
break
case 'export':
emit('export')
break
case 'reset':
emit('reset')
break
case 'query':
emit('query')
break
}
}
// 执行父组件传入的click函数
if (btn.click && typeof btn.click === 'function') {
btn.click()
try {
const result = btn.click()
// 如果是Promise,等待执行完成
if (result instanceof Promise) {
await result
}
} catch (error) {
console.error('按钮点击事件执行失败:', error)
}
}
}
// 分页事件处理
const handleSizeChange = (size: number) => {
pageSize.value = size
emit('size-change', size)
}
const handleCurrentChange = (page: number) => {
currentPage.value = page
emit('current-change', page)
}
// 更新按钮状态的方法
const updateButtonState = (key: string, updates: Partial<OperationButton>) => {
// 这个方法可以在父组件中调用
console.log(`更新按钮 ${key} 状态:`, updates)
}
// 暴露给父组件的方法
defineExpose({
checkConditions,
toggleExpand
toggleExpand,
updateButtonState,
refresh: () => {
checkConditions()
}
})
// 监听器
watch(isExpanded, () => {
checkConditions()
})
watch(isExpanded, checkConditions)
watch(
() => props.currentPage,
......@@ -211,15 +397,27 @@ watch(
}
)
watch(
() => props.operationBtnList,
() => {
nextTick(() => {
checkConditions()
})
},
{ deep: true }
)
// 生命周期
onMounted(() => {
if (props.showSearchForm) {
checkConditions()
setTimeout(() => {
checkConditions()
}, 100)
}
})
</script>
<style scoped>
<style lang="scss" scoped>
.commonPage-container {
width: 100%;
box-sizing: border-box;
......@@ -228,6 +426,7 @@ onMounted(() => {
.cardStyle {
margin-bottom: 10px;
border: none !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.moreBtn {
......@@ -235,25 +434,29 @@ onMounted(() => {
align-items: center;
justify-content: center;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid var(--el-border-color-lighter);
}
.operationBtn {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
margin-bottom: 16px;
flex-wrap: wrap;
gap: 12px;
}
.operationLeft,
.operationRight {
display: flex;
gap: 10px;
gap: 8px;
flex-wrap: wrap;
}
.pagination-container {
display: flex;
justify-content: center;
justify-content: flex-end;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid var(--el-border-color-lighter);
......@@ -262,27 +465,35 @@ onMounted(() => {
/* 搜索表单容器样式 */
.search-form-container {
width: 100%;
transition: all 0.3s ease;
&.expanded {
.el-form-item {
display: flex !important;
}
}
}
/* 响应式调整 */
@media (max-width: 768px) {
.operationBtn {
flex-direction: column;
gap: 10px;
align-items: stretch;
gap: 12px;
}
.operationLeft,
.operationRight {
width: 100%;
justify-content: center;
}
.search-form-container {
flex-direction: column;
.cardStyle {
margin-bottom: 8px;
}
.search-form-container :deep(.el-form-item) {
width: 100% !important;
.pagination-container {
justify-content: center;
}
}
</style>
</style>
\ No newline at end of file
<template>
<CommonPage
:operationBtnList="operationBtnList"
:visibleDefaultButtons="visibleDefaultButtons"
:showSearchForm="true"
:show-pagination="true"
>
<!-- 搜索区域 -->
<template #searchForm>
<el-form
:model="searchFormData"
ref="searchFormRef"
label-width="120px"
label-position="top"
class="search-form"
>
<el-row :gutter="20">
<el-col
:xs="24" :sm="12" :md="8" :lg="6" :xl="6"
v-for="item in searchFormItems"
:key="item.value"
>
<el-form-item
:label="item.label"
:prop="item.value"
:rules="item.rules"
>
<template v-if="item.type === 'input'">
<el-input
v-model="searchFormData[item.value]"
:placeholder="item.placeholder"
size="large"
clearable
/>
</template>
<template v-else-if="item.type === 'select'">
<el-select
v-model="searchFormData[item.value]"
:placeholder="item.placeholder"
size="large"
clearable
>
<el-option
v-for="option in item.options"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</template>
<template v-else-if="item.type === 'daterange'">
<el-date-picker
v-model="searchFormData[item.value]"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
size="large"
clearable
style="width: 100%"
/>
</template>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<!-- 列表区域 -->
<template #table>
<!-- 统计信息卡片 -->
<div class="statistics-cards" v-if="statisticsData.totalPolicyCount > 0">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="6" :lg="6">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">应收款总金额</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalAmount) }}</div>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="12" :md="6" :lg="6">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">已入账金额</div>
<div class="card-value">{{ formatCurrency(statisticsData.totalPaidAmount) }}</div>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="12" :md="6" :lg="6">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">待入账金额</div>
<div class="card-value">{{ formatCurrency(statisticsData.pendingAmount) }}</div>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="12" :md="6" :lg="6">
<el-card shadow="hover" class="statistics-card">
<div class="card-content">
<div class="card-label">总保单数</div>
<div class="card-value">{{ statisticsData.totalPolicyCount }}</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<!-- 应收款管理列表 -->
<el-table
:data="tableData"
height="350"
border
highlight-current-row
style="width: 100%"
v-loading="loading"
>
<el-table-column prop="commissionBizType" label="应收款类型" width="120" fixed="left" />
<el-table-column prop="receivableNo" label="应收款编号" width="120" />
<el-table-column prop="policyNo" label="保单号" width="120" />
<el-table-column prop="reconciliationCompany" label="对账公司" width="120" />
<el-table-column prop="status" label="入账状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'warning'">
{{ row.status === '1' ? '已入账' : '待入账' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="commissionPeriod" label="入账期数" width="100" />
<el-table-column prop="totalPeriod" label="入账总期数" width="100" />
<el-table-column prop="commissionType" label="入账项目" width="100" />
<el-table-column prop="commissionDate" label="入账日(估)" width="120" />
<el-table-column prop="commissionRatio" label="入账比例(估)" width="120">
<template #default="{ row }">
{{ (row.commissionRatio || 0) + '%' }}
</template>
</el-table-column>
<el-table-column prop="expectedAmount" label="入账金额(估)" width="120">
<template #default="{ row }">
{{ formatCurrency(row.expectedAmount) }}
</template>
</el-table-column>
<el-table-column prop="paidRatio" label="已入账比例" width="120">
<template #default="{ row }">
{{ (row.paidRatio || 0) + '%' }}
</template>
</el-table-column>
<el-table-column prop="paidAmount" label="已入账金额" width="120">
<template #default="{ row }">
{{ formatCurrency(row.paidAmount) }}
</template>
</el-table-column>
<el-table-column prop="pendingRatio" label="待入账比例" width="120">
<template #default="{ row }">
{{ (row.pendingRatio || 0) + '%' }}
</template>
</el-table-column>
<el-table-column prop="pendingAmount" label="待入账金额(估)" width="120">
<template #default="{ row }">
{{ formatCurrency(row.pendingAmount) }}
</template>
</el-table-column>
<el-table-column prop="defaultExchangeRate" label="结算汇率(估)" width="120" />
<el-table-column prop="insurerBizId" label="保险公司" width="120" />
<el-table-column prop="productLaunchBizId" label="产品计划" width="120" />
<el-table-column prop="premium" label="期交保费" width="120">
<template #default="{ row }">
{{ formatCurrency(row.premium) }}
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" width="150" />
<el-table-column fixed="right" label="操作" width="150">
<template #default="{ row }">
<el-button
link
type="primary"
size="small"
@click="handleViewDetail(row)"
>
查看
</el-button>
<el-button
link
type="danger"
size="small"
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</template>
</CommonPage>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, watch } from 'vue'
import CommonPage from '@/components/commonPage'
import type { OperationButton } from '@/components/commonPage'
import { ElMessage, ElMessageBox } from 'element-plus'
// 分页相关
const currentPage = ref(1)
const pageSize = ref(10)
const pageTotal = ref(0)
const loading = ref(false)
// 搜索表单数据
const searchFormData = reactive({
policyNo: '',
incomeDateRange: [] as string[],
incomeStatus: '',
incomeTerm: '',
incomeItem: '',
outTeam: '',
insurer: '',
productPlan: '',
paymentType: '',
intermediary: '',
signer: ''
})
// 搜索表单项配置
const searchFormItems = ref([
{
label: '保单号',
value: 'policyNo',
type: 'input',
placeholder: '请输入保单号'
},
{
label: '入账日(估)',
value: 'incomeDateRange',
type: 'daterange',
placeholder: '请选择入账日(估)'
},
{
label: '入账状态',
value: 'incomeStatus',
type: 'select',
placeholder: '请选择入账状态',
options: [
{ label: '全部', value: '' },
{ label: '已入账', value: '1' },
{ label: '待入账', value: '0' }
]
},
{
label: '入账期数',
value: 'incomeTerm',
type: 'input',
placeholder: '请输入入账期数'
},
{
label: '入账项目',
value: 'incomeItem',
type: 'select',
placeholder: '请选择入账项目',
options: [
{ label: '全部', value: '' },
{ label: '佣金', value: '1' },
{ label: '服务费', value: '2' }
]
},
{
label: '出单团队',
value: 'outTeam',
type: 'select',
placeholder: '请选择出单团队',
options: [
{ label: '全部', value: '' },
{ label: '团队A', value: '1' },
{ label: '团队B', value: '2' }
]
},
{
label: '保险公司',
value: 'insurer',
type: 'select',
placeholder: '请选择保险公司',
options: [
{ label: '全部', value: '' },
{ label: '保险公司A', value: '1' },
{ label: '保险公司B', value: '2' }
]
},
{
label: '产品计划',
value: 'productPlan',
type: 'select',
placeholder: '请选择产品计划',
options: [
{ label: '全部', value: '' },
{ label: '计划A', value: '1' },
{ label: '计划B', value: '2' }
]
},
{
label: '应收款类型',
value: 'paymentType',
type: 'select',
placeholder: '请选择应收款类型',
options: [
{ label: '全部', value: '' },
{ label: '关联保单应收单', value: '1' },
{ label: '非关联保单应收单', value: '2' }
]
},
{
label: '转介人',
value: 'intermediary',
type: 'select',
placeholder: '请选择转介人',
options: [
{ label: '全部', value: '' },
{ label: '转介人A', value: '1' },
{ label: '转介人B', value: '2' }
]
},
{
label: '签单员',
value: 'signer',
type: 'select',
placeholder: '请选择签单员',
options: [
{ label: '全部', value: '' },
{ label: '签单员A', value: '1' },
{ label: '签单员B', value: '2' }
]
}
])
// 表格数据
const tableData = ref<any[]>([
// 示例数据
{
id: 1,
commissionBizType: '佣金',
receivableNo: 'YSK001',
policyNo: 'POL001',
reconciliationCompany: '对账公司A',
status: '1',
commissionPeriod: 1,
totalPeriod: 12,
commissionType: '首期佣金',
commissionDate: '2024-01-15',
commissionRatio: 8.5,
expectedAmount: 8500,
paidRatio: 100,
paidAmount: 8500,
pendingRatio: 0,
pendingAmount: 0,
defaultExchangeRate: 1.0,
insurerBizId: '保险公司A',
productLaunchBizId: '计划A',
premium: 100000,
remark: '备注信息'
},
{
id: 2,
commissionBizType: '服务费',
receivableNo: 'YSK002',
policyNo: 'POL002',
reconciliationCompany: '对账公司B',
status: '0',
commissionPeriod: 2,
totalPeriod: 12,
commissionType: '服务费',
commissionDate: '2024-02-15',
commissionRatio: 5.0,
expectedAmount: 5000,
paidRatio: 50,
paidAmount: 2500,
pendingRatio: 50,
pendingAmount: 2500,
defaultExchangeRate: 1.0,
insurerBizId: '保险公司B',
productLaunchBizId: '计划B',
premium: 50000,
remark: ''
}
])
// 统计信息
const statisticsData = ref({
totalAmount: 13500,
totalPaidAmount: 11000,
pendingAmount: 2500,
paidRatio: 81.48, // (11000/13500*100)
totalPolicyCount: 2
})
// 货币格式化
const formatCurrency = (value: number) => {
if (value === undefined || value === null) return '¥0.00'
return '¥' + value.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,')
}
// 按钮事件处理
const handleAdd = () => {
ElMessage.info('点击新增按钮')
// 这里可以打开新增弹窗
// proxy.$refs.addForm.resetFields()
}
const handleImport = () => {
ElMessage.info('点击导入按钮')
// 这里可以打开导入弹窗
// proxy.$refs.importForm.resetFields()
}
const handleExport = () => {
ElMessage.info('点击导出按钮')
// 这里可以执行导出逻辑
// proxy.$refs.exportForm.resetFields()
}
const handleReset = () => {
// 重置搜索表单
Object.keys(searchFormData).forEach(key => {
if (Array.isArray(searchFormData[key])) {
searchFormData[key] = []
} else {
searchFormData[key] = ''
}
})
ElMessage.success('搜索条件已重置')
// 重新加载数据
loadTableData()
}
const handleQuery = async () => {
// 表单验证
// const valid = await proxy.$refs.searchFormRef.validate()
// if (!valid) return
ElMessage.info('执行查询操作')
loadTableData()
}
// 控制要显示的默认按钮
const visibleDefaultButtons = ref(['add', 'import', 'export']) // 只显示新增和查询两个默认按钮
// 按钮配置
const operationBtnList = ref<OperationButton[]>([
{
key: 'add',
direction: 'left',
click: handleAdd
},
{
key: 'import',
direction: 'left',
click: handleImport
},
{
key: 'export',
direction: 'right',
click: handleExport
},
{
key: 'reset',
direction: 'right',
click: handleReset
},
{
key: 'query',
direction: 'right',
click: handleQuery
}
])
// 表格操作
const handleViewDetail = (row: any) => {
ElMessage.info(`查看应收款详情:${row.receivableNo}`)
// 这里可以打开详情弹窗或跳转到详情页
}
const handleDelete = async (row: any) => {
try {
await ElMessageBox.confirm(
`确定要删除应收款 "${row.receivableNo}" 吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
// 执行删除操作
tableData.value = tableData.value.filter(item => item.id !== row.id)
ElMessage.success('删除成功')
// 重新计算统计信息
calculateStatistics()
} catch {
// 用户取消了操作
}
}
// 分页事件
const handleSizeChange = (val: number) => {
pageSize.value = val
loadTableData()
}
const handleCurrentChange = (val: number) => {
currentPage.value = val
loadTableData()
}
// 加载表格数据
const loadTableData = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 500))
// 这里应该调用真实的API接口
// const params = {
// ...searchFormData,
// currentPage: currentPage.value,
// pageSize: pageSize.value
// }
// const response = await api.getReceivablesList(params)
// tableData.value = response.data.list
// pageTotal.value = response.data.total
// 计算统计信息
calculateStatistics()
} catch (error) {
console.error('加载数据失败:', error)
ElMessage.error('加载数据失败')
} finally {
loading.value = false
}
}
// 计算统计信息
const calculateStatistics = () => {
const data = tableData.value
const totalAmount = data.reduce((sum, item) => sum + (item.expectedAmount || 0), 0)
const totalPaidAmount = data.reduce((sum, item) => sum + (item.paidAmount || 0), 0)
const pendingAmount = totalAmount - totalPaidAmount
const paidRatio = totalAmount > 0 ? (totalPaidAmount / totalAmount * 100) : 0
statisticsData.value = {
totalAmount,
totalPaidAmount,
pendingAmount,
paidRatio: Number(paidRatio.toFixed(2)),
totalPolicyCount: data.length
}
}
// 监听搜索条件变化(可选:自动查询)
watch(
() => searchFormData,
() => {
// 如果需要自动查询,可以在这里调用
// debounce(() => loadTableData(), 500)
},
{ deep: true }
)
// 初始化加载数据
onMounted(() => {
loadTableData()
calculateStatistics()
})
</script>
<style scoped lang="scss">
.search-form {
padding: 10px 0;
:deep(.el-form-item) {
margin-bottom: 20px;
.el-form-item__label {
font-weight: 500;
}
}
}
.pagination-wrapper {
display: flex;
justify-content: flex-end;
margin-top: 20px;
padding: 15px 0;
border-top: 1px solid var(--el-border-color-lighter);
}
.statistics-cards {
margin-top: 20px;
}
.statistics-card {
height: 120px;
margin-bottom: 20px;
.card-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
.card-label {
font-size: 14px;
color: var(--el-text-color-secondary);
margin-bottom: 8px;
}
.card-value {
font-size: 24px;
font-weight: 600;
color: var(--el-color-primary);
}
}
}
// 响应式调整
@media (max-width: 768px) {
.search-form {
:deep(.el-col) {
margin-bottom: 10px;
}
}
.statistics-cards {
.el-col {
margin-bottom: 15px;
}
}
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment