Commit 8fd3df19 by Sweet Zhang

对接接口,封装搜索组件

parent f97f6052
...@@ -290,3 +290,13 @@ export function policyNoCommissionPayRecord(data) { ...@@ -290,3 +290,13 @@ export function policyNoCommissionPayRecord(data) {
data: data data: data
}) })
} }
// 应收款导出
export function exportReceivedFortune(data) {
return request({
url: '/csf/api/CommissionExpected/export',
method: 'post',
data: data,
responseType: 'blob'
})
}
<!-- components/SearchForm.vue --> <!-- components/SearchForm.vue -->
<template> <template>
<el-form :model="formModel" label-width="100px" size="default" label-position="top"> <el-form :model="formModel" label-width="100px" size="default" label-position="top">
<el-row :gutter="20"> <el-row :gutter="20">
<template v-for="item in config" :key="item.prop"> <template v-for="item in config" :key="item.prop">
<!-- 所有字段都用相同的响应式布局 --> <!-- Input -->
<el-col :xs="24" :sm="12" :md="6" :lg="6" v-if="item.type === 'input'"> <el-col :xs="24" :sm="12" :md="6" :lg="6" v-if="item.type === 'input'">
<el-form-item :label="item.label" :prop="item.prop"> <el-form-item :label="item.label" :prop="item.prop" :rules="item.rules">
<el-input <el-input :model-value="formModel[item.prop]" @input="(val) => handleNumberInput(val, item)"
v-model="formModel[item.prop]" :placeholder="item.placeholder || `请输入${item.label}`" clearable style="width: 100%" />
:placeholder="item.placeholder || `请输入${item.label}`"
clearable
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- Remote Select (带 api 的 select) -->
<el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'select' && item.api"> <el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'select' && item.api">
<el-form-item :label="item.label" :prop="item.prop"> <el-form-item :label="item.label" :prop="item.prop" :rules="item.rules">
<el-select <el-select ref="selectRefs[item.prop]" v-model="formModel[item.prop]"
v-model="formModel[item.prop]" :placeholder="item.placeholder || `请选择${item.label}`" clearable filterable
:placeholder="item.placeholder || `请选择${item.label}`" :multiple="item.multiple" :loading="item.loading"
clearable :no-match-text="item.noMatchText || '无匹配数据'" @filter="handleSelectFilter(item)"
filterable @visible-change="handleVisibleChange(item)" @change="(val) => handleChange(item, val)"
:filter-method="getFilterMethod(item)" style="width: 100%">
:loading="item.loading" <el-option v-for="opt in item._allOptions" :key="opt[item.valueKey || 'value']"
@focus="handleFocus(item)" :label="opt[item.labelKey || 'label']" :value="opt[item.valueKey || 'value']" />
>
<el-option
v-for="opt in item.options"
:key="opt[item.valueKey || 'value']"
:label="opt[item.labelKey || 'label']"
:value="opt[item.valueKey || 'value']"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- Static Select / Dict Select -->
<el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'select'"> <el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'select'">
<el-form-item :label="item.label" :prop="item.prop"> <el-form-item :label="item.label" :prop="item.prop" :rules="item.rules">
<el-select <el-select v-model="formModel[item.prop]" :placeholder="item.placeholder || `请选择${item.label}`"
v-model="formModel[item.prop]" clearable :filterable="item.filterable !== false" :multiple="item.multiple"
:placeholder="item.placeholder || `请选择${item.label}`" :no-match-text="item.noMatchText || '无匹配数据'">
clearable <el-option v-for="opt in item.options" :key="opt[item.valueKey || 'value']"
:filterable="item.filterable !== false" :label="opt[item.labelKey || 'label']" :value="opt[item.valueKey || 'value']" />
:multiple="item.multiple"
>
<el-option
v-for="opt in item.options"
:key="opt[item.valueKey || 'value']"
:label="opt[item.labelKey || 'label']"
:value="opt[item.valueKey || 'value']"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- Checkbox Group -->
<el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'checkbox-group'"> <el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'checkbox-group'">
<el-form-item :label="item.label" :prop="item.prop"> <el-form-item :label="item.label" :prop="item.prop">
<el-checkbox-group v-model="formModel[item.prop]"> <el-checkbox-group v-model="formModel[item.prop]">
<el-checkbox <el-checkbox v-for="opt in item.options" :key="opt[item.valueKey || 'value']"
v-for="opt in item.options" :label="opt[item.valueKey || 'value']">
:key="opt[item.valueKey || 'value']"
:label="opt[item.valueKey || 'value']"
>
{{ opt[item.labelKey || 'label'] }} {{ opt[item.labelKey || 'label'] }}
</el-checkbox> </el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- Date -->
<el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'date'"> <el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'date'">
<el-form-item :label="item.label" :prop="item.prop"> <el-form-item :label="item.label" :prop="item.prop">
<el-date-picker <el-date-picker v-model="formModel[item.prop]" type="date"
v-model="formModel[item.prop]" :placeholder="item.placeholder || `请选择${item.label}`" format="YYYY-MM-DD"
type="date" value-format="YYYY-MM-DD" style="width: 100%" clearable />
:placeholder="item.placeholder || `请选择${item.label}`"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%"
clearable
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- Date Range -->
<el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'daterange'"> <el-col :xs="24" :sm="12" :md="6" :lg="6" v-else-if="item.type === 'daterange'">
<el-form-item :label="item.label" :prop="item.prop"> <el-form-item :label="item.label" :prop="item.prop">
<el-date-picker <el-date-picker v-model="formModel[item.prop]" type="daterange"
v-model="formModel[item.prop]"
type="daterange"
:range-separator="item.rangeSeparator || '至'" :range-separator="item.rangeSeparator || '至'"
:start-placeholder="item.startPlaceholder || '开始日期'" :start-placeholder="item.startPlaceholder || '开始日期'"
:end-placeholder="item.endPlaceholder || '结束日期'" :end-placeholder="item.endPlaceholder || '结束日期'" format="YYYY-MM-DD"
format="YYYY-MM-DD" value-format="YYYY-MM-DD" clearable />
value-format="YYYY-MM-DD"
clearable
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</template> </template>
</el-row> </el-row>
</el-form> </el-form>
</template> </template>
<script setup> <script setup>
import { reactive, onMounted } from 'vue' import { reactive, onMounted, onUnmounted } from 'vue'
import request from '@/utils/request' import request from '@/utils/request'
import { loadDicts, getDictOptions } from '@/utils/useDict'
const selectRefs = reactive({})
function debounce(func, wait) { // 新增 handleChange
let timeout function handleChange(item, value) {
return function (...args) { // 如果是多选,value 是数组;单选是值
clearTimeout(timeout) // 这里不需要处理值,因为 v-model 已绑定
timeout = setTimeout(() => func.apply(this, args), wait)
// 清空 filter 输入框
nextTick(() => {
const ref = selectRefs[item.prop]
if (ref && typeof ref.inputValue === 'string') {
ref.inputValue = ''
} }
})
} }
// ========================
// 工具函数
// ========================
const formModel = reactive({})
const props = defineProps({ const props = defineProps({
config: { type: Array, required: true } config: { type: Array, required: true }
}) })
const formModel = reactive({}) // 防抖定时器 Map
const debounceMap = new Map()
// 搜索缓存 Map: key = `${prop}:${keyword}`, value = option list
const searchCache = new Map()
// ========================
// 数字输入处理
// ========================
function handleNumberInput(value, item) {
const { inputType = 'text', decimalDigits = 2 } = item
if (inputType === 'text') {
formModel[item.prop] = value
return
}
let result = String(value)
if (inputType === 'integer') {
result = result.replace(/[^\d]/g, '')
} else if (inputType === 'decimal') {
result = result.replace(/[^\d.]/g, '').replace(/^\./, '')
const parts = result.split('.')
if (parts.length > 2) result = parts[0] + '.' + parts.slice(1).join('')
} else if (inputType === 'signed-decimal') {
result = result.replace(/[^\d.-]/g, '')
if (result.startsWith('--')) result = '-' + result.slice(2)
if (result.includes('-') && !result.startsWith('-')) result = result.replace(/-/g, '')
result = result.replace(/^\./, '')
const parts = result.split('.')
if (parts.length > 2) result = parts[0] + '.' + parts.slice(1).join('')
}
if ((inputType === 'decimal' || inputType === 'signed-decimal') && decimalDigits >= 0) {
const dotIndex = result.indexOf('.')
if (dotIndex !== -1) {
const integerPart = result.slice(0, dotIndex)
let decimalPart = result.slice(dotIndex + 1).slice(0, decimalDigits)
result = integerPart + (decimalPart ? '.' + decimalPart : '')
}
}
formModel[item.prop] = result
}
// ======================== // ========================
// 公共请求方法 // 远程选项请求
// ======================== // ========================
async function fetchOptions(item, keyword = '') { async function fetchOptions(item, keyword = '') {
const payload = { const payload = {
...@@ -130,7 +154,6 @@ async function fetchOptions(item, keyword = '') { ...@@ -130,7 +154,6 @@ async function fetchOptions(item, keyword = '') {
pageNo: item.pageNo || 1, pageNo: item.pageNo || 1,
pageSize: item.pageSize || 20 pageSize: item.pageSize || 20
} }
if (keyword) { if (keyword) {
const keyField = item.keywordField || 'keyword' const keyField = item.keywordField || 'keyword'
payload[keyField] = keyword.trim() payload[keyField] = keyword.trim()
...@@ -154,39 +177,80 @@ async function fetchOptions(item, keyword = '') { ...@@ -154,39 +177,80 @@ async function fetchOptions(item, keyword = '') {
} else if (res?.data?.data && Array.isArray(res.data.data)) { } else if (res?.data?.data && Array.isArray(res.data.data)) {
list = res.data.data list = res.data.data
} }
return list return list
} }
// ======================== // ========================
// 创建搜索函数(带防抖) // 远程搜索逻辑
// ======================== // ========================
function createSearchFn(item) { function handleSelectFilter(item, query) {
const search = async (keyword) => { // 清除旧防抖
if (debounceMap.has(item.prop)) {
clearTimeout(debounceMap.get(item.prop))
}
const timer = setTimeout(() => {
doRemoteSearch(item, query)
}, item.debounceWait ?? 300)
debounceMap.set(item.prop, timer)
}
async function doRemoteSearch(item, keyword) {
const cacheKey = `${item.prop}:${keyword}`
if (searchCache.has(cacheKey)) {
item._allOptions = searchCache.get(cacheKey)
return
}
item.loading = true item.loading = true
try { try {
if (!keyword) { let list = []
// 关键词清空 → 恢复默认选项(从缓存) if (!keyword.trim()) {
item.options = item._defaultOptions ? [...item._defaultOptions] : [] list = item._fullOptions || []
} else { } else {
// 有关键词 → 远程搜索 list = await fetchOptions(item, keyword)
const list = await fetchOptions(item, keyword)
item.options = list
} }
item._allOptions = list
searchCache.set(cacheKey, list)
} catch (e) { } catch (e) {
console.error(`${item.label} 搜索失败`, e) console.error(`${item.label} 搜索失败`, e)
item.options = [] item._allOptions = item._fullOptions || []
} finally { } finally {
item.loading = false item.loading = false
} }
}
// ========================
// 下拉展开时懒加载全量
// ========================
function handleVisibleChange(item, visible) {
if (visible && !item._hasLoadedFull) {
item.loading = true
fetchOptions(item, '')
.then(list => {
item._allOptions = list
item._fullOptions = list
item._hasLoadedFull = true
searchCache.set(`${item.prop}:`, list) // 缓存空关键词
})
.catch(e => {
console.error('加载默认选项失败', e)
item._allOptions = []
})
.finally(() => {
item.loading = false
})
} }
return debounce(search, item.debounceWait ?? 300)
} }
// ======================== // ========================
// 初始化 // 初始化
// ======================== // ========================
onMounted(async () => { onMounted(async () => {
const dictTypes = []
const dictItems = []
for (const item of props.config) { for (const item of props.config) {
// 初始化表单值 // 初始化表单值
if (['checkbox-group', 'daterange'].includes(item.type)) { if (['checkbox-group', 'daterange'].includes(item.type)) {
...@@ -194,49 +258,61 @@ onMounted(async () => { ...@@ -194,49 +258,61 @@ onMounted(async () => {
} else { } else {
formModel[item.prop] = item.defaultValue ?? '' formModel[item.prop] = item.defaultValue ?? ''
} }
// 处理带 api 的 select(hybrid 模式)
if (item.type === 'select' && item.api) { if (item.type === 'select' && item.api) {
item.options = [] item._allOptions = []
item._fullOptions = []
item.loading = true item.loading = true
try { try {
// 1. 加载默认数据(不传 keyword) const list = await fetchOptions(item, '') // 无关键词,加载默认列表
const defaultList = await fetchOptions(item, '') item._allOptions = list
item._defaultOptions = [...defaultList] // 缓存 item._fullOptions = list
item.options = defaultList item._hasLoadedFull = true
searchCache.set(`${item.prop}:`, list) // 缓存空关键词结果
} catch (e) { } catch (e) {
console.error(`加载 ${item.label} 默认选项失败`, e) console.error(`加载 ${item.label} 默认选项失败`, e)
item.options = [] item._allOptions = []
} finally { } finally {
item.loading = false item.loading = false
} }
} }
// 处理数字输入
if (item.type === 'input' && item.inputType && item.inputType !== 'text') {
handleNumberInput(formModel[item.prop], item)
} }
})
// ======================== // 收集字典项
// 提供给 el-select 的 filter-method if (item.type === 'select' && item.dictType) {
// ======================== dictTypes.push(item.dictType)
function getFilterMethod(item) { dictItems.push(item)
if (!item._searchFn) {
item._searchFn = createSearchFn(item)
} }
return (keyword) => {
item._searchFn(keyword)
return true // 必须返回 true,否则下拉框会关闭
} }
}
// 可选:聚焦时确保有数据(一般不需要,因为 onMounted 已加载) // 加载字典
function handleFocus(item) { if (dictTypes.length > 0) {
// 如果意外为空,可重试 await loadDicts(dictTypes)
if (item.type === 'select' && item.api && (!item.options || item.options.length === 0)) { for (const item of dictItems) {
// 可加 loading 重试逻辑(略) item.options = getDictOptions(item.dictType)
} }
} }
})
// ========================
// 清理
// ========================
onUnmounted(() => {
debounceMap.forEach(timer => clearTimeout(timer))
debounceMap.clear()
searchCache.clear()
})
// ========================
// 暴露方法 // 暴露方法
// ========================
defineExpose({ defineExpose({
async validate() {
// 如果你有 el-form ref,可加校验;否则直接返回
return formModel
},
getSearchParams() { getSearchParams() {
const params = {} const params = {}
for (const key in formModel) { for (const key in formModel) {
......
...@@ -52,24 +52,3 @@ export function useDictLists(typeLists) { ...@@ -52,24 +52,3 @@ export function useDictLists(typeLists) {
}) })
})() })()
} }
// /**
// * 获取字典数据
// */
// export function useDict(...args) {
// const res = ref({})
// return (() => {
// args.forEach((dictType, index) => {
// res.value[dictType] = []
// const dicts = useDictStore().getDict(dictType)
// if (dicts) {
// res.value[dictType] = dicts
// } else {
// getDicts(dictType).then(resp => {
// res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
// useDictStore().setDict(dictType, res.value[dictType])
// })
// }
// })
// return toRefs(res.value)
// })()
// }
...@@ -149,6 +149,7 @@ service.interceptors.response.use( ...@@ -149,6 +149,7 @@ service.interceptors.response.use(
// 通用下载方法 // 通用下载方法
export function download(url, params, filename, config) { export function download(url, params, filename, config) {
console.log(url, params, filename, config)
downloadLoadingInstance = ElLoading.service({ downloadLoadingInstance = ElLoading.service({
text: '正在下载数据,请稍候', text: '正在下载数据,请稍候',
background: 'rgba(0, 0, 0, 0.7)' background: 'rgba(0, 0, 0, 0.7)'
...@@ -160,7 +161,7 @@ export function download(url, params, filename, config) { ...@@ -160,7 +161,7 @@ export function download(url, params, filename, config) {
return tansParams(params) return tansParams(params)
} }
], ],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/json' },
responseType: 'blob', responseType: 'blob',
...config ...config
}) })
......
// utils/safeDownload.js
import { ElMessage } from 'element-plus'
/**
* 安全下载函数:适用于后端返回 Blob 文件流 或 JSON 错误 的场景
* @param {Blob} blobData - 接口返回的响应数据(必须是 responseType: 'blob')
* @param {string} defaultFilename - 默认文件名(如 'data.xlsx')
* @param {string} [mimeType] - MIME 类型(用于兜底创建 Blob)
*/
export async function safeDownload(blobData, defaultFilename, mimeType = 'application/octet-stream') {
if (!(blobData instanceof Blob)) {
ElMessage.error('无效的下载数据')
return
}
try {
// 👇 关键:先 peek 前 100 字节,判断是否是 JSON 错误
const firstChunk = await blobData.slice(0, 100).text()
const trimmed = firstChunk.trim()
// 如果看起来像 JSON(以 { 或 [ 开头),尝试解析
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
const fullText = await blobData.text()
let parsed
try {
parsed = JSON.parse(fullText)
} catch (e) {
// 解析失败,当作正常文件(比如内容就是纯文本)
parsed = null
}
// 如果解析成功,且包含错误字段(根据你后端约定)
if (parsed && (parsed.code !== undefined || parsed.msg || parsed.message)) {
const errorMsg = parsed.msg || parsed.message || '导出失败'
ElMessage.error(errorMsg)
return
}
}
// ✅ 是合法文件流,执行下载(使用你已验证的逻辑)
const url = window.URL.createObjectURL(blobData)
const link = document.createElement('a')
link.href = url
link.download = defaultFilename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
ElMessage.success('下载成功')
} catch (error) {
console.error('safeDownload error:', error)
ElMessage.error('下载过程中发生错误')
}
}
\ No newline at end of file
// dictUtils.js
import request from '@/utils/request'
// 全局缓存:key 为 dictType,value 为 { label: value } 映射对象
const dictCache = new Map()
// 批量加载字典(支持多个 type)
export async function loadDicts(typeList) {
// 过滤已缓存的类型,避免重复请求
const needLoadTypes = typeList.filter(type => !dictCache.has(type))
if (needLoadTypes.length === 0) return
try {
const res = await request({
url: '/user/api/sysDict/type/list', // 替换为你的实际接口地址
method: 'POST',
data: {
typeList: needLoadTypes
}
})
// 替换 loadDicts 中的缓存部分
if (res.code === 200 && Array.isArray(res.data)) {
for (const dict of res.data) {
const { dictType, dictItemList = [] } = dict
if (!dictType) continue
// 缓存完整列表(并可选排序 + 过滤)
const validItems = dictItemList
.filter(item => item.status === 1) // 只取启用的
.sort((a, b) => a.orderNum - b.orderNum) // 按序号排序
dictCache.set(dictType, validItems) // 👈 缓存完整 item 列表
}
}
} catch (error) {
console.error('字典加载失败:', error)
throw error
}
}
// 可选:提供清除缓存方法(用于权限切换等场景)
export function clearDictCache() {
dictCache.clear()
}
export function getDictOptions(dictType) {
const items = dictCache.get(dictType) || []
// console.log('getDictOptions',items)
return items.map(item => ({
value: item.itemValue,
label: item.itemLabel
// 如果需要,还可以加其他字段:disabled, key 等
}))
}
// 同时更新 getDictLabel
export function getDictLabel(dictType, value) {
const items = dictCache.get(dictType) || []
const item = items.find(i => i.itemValue === value)
return item ? item.itemLabel : value
}
\ No newline at end of file
...@@ -5,33 +5,7 @@ ...@@ -5,33 +5,7 @@
:page-size='pageSize' @size-change='handleSizeChange' @current-change='handleCurrentChange'> :page-size='pageSize' @size-change='handleSizeChange' @current-change='handleCurrentChange'>
<!-- 搜索区域 --> <!-- 搜索区域 -->
<template #searchForm> <template #searchForm>
<el-form :model="queryParams" label-width="80px"> <SearchForm ref="searchFormRef" :config="searchConfig" />
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="转介人" label-position="top">
<el-select v-model="queryParams.broker" placeholder="请选择转介人" clearable :remote-method="loadBrokers"
:loading="searchLoading" filterable remote reserve-keyword>
<el-option v-for="item in brokerOptions" :key="item.userSaleBizId" :label="item.realName + '(' + item.phone + ')'"
:value="item.realName"/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="出账日期" label-position="top">
<el-date-picker v-model="queryParams.accountDate" type="daterange" range-separator="-"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="出账状态" label-position="top">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option v-for="item in dictLists" :key="item.itemValue" :label="item.itemLabel"
:value="item.itemValue" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template> </template>
<!-- 列表区域 --> <!-- 列表区域 -->
<template #table> <template #table>
...@@ -47,7 +21,7 @@ ...@@ -47,7 +21,7 @@
</el-row> </el-row>
</div> </div>
<el-table :data="tableData" @selection-change="handleSelectionChange" v-loading="loading" ref="tableRef" <el-table :data="tableData" @selection-change="handleSelectionChange" v-loading="loading" ref="tableRef"
row-key="fortuneAccountBizId" :reserve-selection="true" border="true"> row-key="fortuneAccountBizId" :reserve-selection="true" :border="true">
<el-table-column type="selection" width="55" :reserve-selection="true" /> <el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column prop="broker" label="转介人" min-width="120" sortable /> <el-table-column prop="broker" label="转介人" min-width="120" sortable />
<el-table-column prop="team" label="所属团队" min-width="120" sortable /> <el-table-column prop="team" label="所属团队" min-width="120" sortable />
...@@ -68,6 +42,7 @@ ...@@ -68,6 +42,7 @@
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="fortuneAccountDate" label="出账日" min-width="150" show-overflow-tooltip />
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip /> <el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip />
</el-table> </el-table>
</template> </template>
...@@ -107,10 +82,44 @@ import { ...@@ -107,10 +82,44 @@ import {
getReferrerFortuneList, getReferrerFortuneList,
salaryStatistics, salaryStatistics,
} from '@/api/financial/commission' } from '@/api/financial/commission'
import { searchIntermediaries } from '@/api/search' // import { searchIntermediaries } from '@/api/search'
import { formatCurrency } from '@/utils/number' import { formatCurrency } from '@/utils/number'
import { debounce } from '@/utils/index'; import { debounce } from '@/utils/index';
import {ElMessageBox} from 'element-plus' import { ElMessageBox } from 'element-plus'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
const searchFormRef = ref(null)
const searchParams = ref({})
const searchConfig = ref([
{
type: 'select',
prop: 'brokerBizIdList',
label: '转介人',
api: '/insurance/base/api/userSaleExpand/page',
keywordField: 'realName',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入转介人名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'userSaleBizId',
labelKey: 'realName',
multiple: true,
transform: (res) => {
return res?.data.records || []
}
},
{
type: 'daterange',
prop: 'payoutDate',
label: '出账日(实)',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间'
},
{
type: 'select',
prop: 'status',
label: '出账状态',
dictType: 'csf_fortune_account_status'
}
])
// 添加表格引用 // 添加表格引用
const tableRef = ref() const tableRef = ref()
// 存储所有选中的行数据(用于跨页保持选择) // 存储所有选中的行数据(用于跨页保持选择)
...@@ -120,16 +129,7 @@ const statisticInfo = ref({ ...@@ -120,16 +129,7 @@ const statisticInfo = ref({
totalAmount: 0, totalAmount: 0,
brokerCount: 0 brokerCount: 0
}) })
// 查询参数
const queryParams = reactive({
broker: '',
accountDate: [],
accountDateStart: '',
accountDateEnd: '',
sortField: '',
sortOrder: 'desc',
status: '6'
})
// 表格数据 // 表格数据
const tableData = ref([]) const tableData = ref([])
const loading = ref(false) const loading = ref(false)
...@@ -221,16 +221,14 @@ const clearAllSelection = () => { ...@@ -221,16 +221,14 @@ const clearAllSelection = () => {
// 获取数据列表 // 获取数据列表
const getList = async () => { const getList = async (searchParams = {}) => {
loading.value = true loading.value = true
try { try {
if (queryParams.accountDate.length > 0) {
queryParams.accountDateStart = queryParams.accountDate[0]
queryParams.accountDateEnd = queryParams.accountDate[1]
}
const params = { const params = {
...queryParams, ...searchParams,
accountDate:undefined, accountDateStart: searchParams.payoutDate?.[0] || undefined,
accountDateEnd: searchParams.payoutDate?.[1] || undefined,
payoutDate:undefined,
pageNo: currentPage.value, pageNo: currentPage.value,
pageSize: pageSize.value pageSize: pageSize.value
} }
...@@ -241,7 +239,7 @@ const getList = async () => { ...@@ -241,7 +239,7 @@ const getList = async () => {
pageTotal.value = response.data.page.total pageTotal.value = response.data.page.total
loading.value = false loading.value = false
} }
if (response.data.statisticsVO && isSearch.value) { if (response.data.statisticsVO) {
statisticInfo.value = response.data.statisticsVO statisticInfo.value = response.data.statisticsVO
} }
// 数据加载完成后,设置当前页的选中状态 // 数据加载完成后,设置当前页的选中状态
...@@ -266,32 +264,19 @@ const getStatusType = status => { ...@@ -266,32 +264,19 @@ const getStatusType = status => {
// 查询 // 查询
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNo = 1 const params = searchFormRef.value.getSearchParams()
Object.values(queryParams).some(value => { console.log('父组件发起查询:', params)
if (Array.isArray(value) && value.length > 0) {
isSearch.value = true
}
if (value !== '' && value != null) {
isSearch.value = true
}
})
clearAllSelection() clearAllSelection()
getList() getList(params)
} }
// 重置查询 // 重置查询
const handleReset = () => { const handleReset = () => {
Object.assign(queryParams, { // 重置搜索表单
broker: '', searchFormRef.value.resetForm()
accountDateStart: '', searchParams.value = {}
accountDateEnd: '', console.log('表单已重置')
sortField: '', getList(searchParams.value)
sortOrder: 'desc'
})
isSearch.value = false
// 清空选择
clearAllSelection()
getList()
} }
// 选择行变化 // 选择行变化
...@@ -391,7 +376,7 @@ const fetchCompletePolicyFortune = async row => { ...@@ -391,7 +376,7 @@ const fetchCompletePolicyFortune = async row => {
const searchLoading = ref(false) const searchLoading = ref(false)
const brokerOptions = ref([]) const brokerOptions = ref([])
// 获取转介人列表 // 获取转介人列表
const loadBrokers = async (query='') => { const loadBrokers = async (query = '') => {
searchLoading.value = true searchLoading.value = true
const params = { const params = {
realName: query, realName: query,
...@@ -399,18 +384,18 @@ const loadBrokers = async (query='') => { ...@@ -399,18 +384,18 @@ const loadBrokers = async (query='') => {
pageSize: 1000, pageSize: 1000,
} }
try { try {
const res = await searchIntermediaries(params) // const res = await searchIntermediaries(params)
if (res.code === 200) { // if (res.code === 200) {
brokerOptions.value = res.data.records || [] // brokerOptions.value = res.data.records || []
searchLoading.value = false // searchLoading.value = false
} else { // } else {
brokerOptions.value = [] // brokerOptions.value = []
searchLoading.value = false // searchLoading.value = false
} // }
} catch (error) { } } catch (error) { }
} }
debounce(loadBrokers, 500,false) debounce(loadBrokers, 500, false)
const visibleDefaultButtons = ref(['reset', 'query']) const visibleDefaultButtons = ref(['reset', 'query'])
// 按钮配置 // 按钮配置
const operationBtnList = ref([ const operationBtnList = ref([
......
...@@ -64,20 +64,24 @@ ...@@ -64,20 +64,24 @@
</div> </div>
<!-- 应付款管理列表 --> <!-- 应付款管理列表 -->
<el-table :data="tableData" height="400" border highlight-current-row style="width: 100%" v-loading="loading"> <el-table :data="tableData" height="400" border highlight-current-row style="width: 100%" v-loading="loading">
<el-table-column prop="commissionBizType" label="应付款类型" width="120" fixed="left" sortable /> <el-table-column prop="fortuneBizType" label="应付款类型" width="120" fixed="left" sortable>
<el-table-column prop="payableNo" label="应付款编号" width="120" /> <template #default="{ row }">
<el-table-column prop="policyNo" label="保单号" width="120" /> {{ getFortuneBizTypeLabel(row.fortuneBizType) }}
</template>
</el-table-column>
<el-table-column prop="payableNo" label="应付款编号" width="120" sortable/>
<el-table-column prop="policyNo" label="保单号" width="120" sortable/>
<el-table-column prop="status" label="出账状态" width="120" sortable> <el-table-column prop="status" label="出账状态" width="120" sortable>
<template #default="{ row }"> <template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'warning'"> {{ getDictLabel('csf_expected_fortune_status', row.status) }}
{{ row.status === '1' ? '已入账' : '待入账' }}
</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="currency" label="出账币种" width="120" sortable />
<el-table-column prop="fortunePeriod" label="出账期数" width="120" sortable /> <el-table-column prop="fortunePeriod" label="出账期数" width="120" sortable />
<el-table-column prop="fortuneTotalPeriod" label="出账总期数" width="120" sortable /> <el-table-column prop="fortuneTotalPeriod" label="出账总期数" width="120" sortable />
<el-table-column prop="fortuneType" label="出账项目" width="120" sortable /> <el-table-column prop="fortuneName" label="出账项目" width="120" sortable />
<el-table-column prop="payoutDate" label="出账日(估)" width="120" sortable /> <el-table-column prop="payoutDate" label="出账日(估)" width="120" sortable />
<el-table-column prop="actualPayoutDate" label="出账日(实)" width="120" sortable />
<el-table-column prop="commissionRatio" label="出账比例(估)" width="140" sortable> <el-table-column prop="commissionRatio" label="出账比例(估)" width="140" sortable>
<template #default="{ row }"> <template #default="{ row }">
{{ (row.commissionRatio || 0) + '%' }} {{ (row.commissionRatio || 0) + '%' }}
...@@ -85,7 +89,7 @@ ...@@ -85,7 +89,7 @@
</el-table-column> </el-table-column>
<el-table-column prop="amount" label="出账金额(估)" width="140" sortable> <el-table-column prop="amount" label="出账金额(估)" width="140" sortable>
<template #default="{ row }"> <template #default="{ row }">
{{ formatCurrency(row.expectedAmount) }} {{ formatCurrency(row.amount) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="paidRatio" label="已出账比例" width="120" sortable> <el-table-column prop="paidRatio" label="已出账比例" width="120" sortable>
...@@ -153,6 +157,18 @@ import { ElMessage, ElMessageBox } from 'element-plus' ...@@ -153,6 +157,18 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { formatCurrency } from '@/utils/number' import { formatCurrency } from '@/utils/number'
import { expectedFortuneList, payRecordList } from '@/api/financial/commission' import { expectedFortuneList, payRecordList } from '@/api/financial/commission'
import SearchForm from '@/components/SearchForm/SearchForm.vue' import SearchForm from '@/components/SearchForm/SearchForm.vue'
import {getDictLabel} from '@/utils/useDict';
// 应收单类型
const fortuneBizTypeOptions = [
{ value: 'R', label: '关联保单应付单' },
{ value: 'U', label: '非关联保单应付单' }
]
// 应付单类型通过value转成label
const getFortuneBizTypeLabel = (value) => {
const item = fortuneBizTypeOptions.find(item => item.value === value)
return item?.label || ''
}
const searchFormRef = ref(null) const searchFormRef = ref(null)
const searchParams = ref({}) const searchParams = ref({})
const searchConfig = ref([ const searchConfig = ref([
...@@ -173,39 +189,22 @@ const searchConfig = ref([ ...@@ -173,39 +189,22 @@ const searchConfig = ref([
prop: 'statusList', prop: 'statusList',
label: '出账状态', label: '出账状态',
multiple: true, multiple: true,
options: [ dictType: 'csf_expected_fortune_status'
{ value: '1', label: '启用' },
{ value: '0', label: '禁用' }
]
}, },
{ {
type: 'input', type: 'input',
prop: 'fortunePeriod', prop: 'fortunePeriod',
label: '出账期数' label: '出账期数',
inputType: 'decimal',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
}, },
{ {
type: 'select', type: 'select',
prop: 'fortuneName', prop: 'fortuneName',
label: '出账项目', label: '出账项目',
options: [ dictType: 'csf_fortune_type'
{ value: '1', label: '启用' },
{ value: '0', label: '禁用' }
]
},
{
type: 'select',
prop: 'insurerBizId',
label: '对账公司',
api: '/insurance/base/api/insuranceReconciliationCompany/page',
keywordField: 'name',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入对账公司名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
transform: (res) => {
return res?.data.records || []
}
}, },
{ {
type: 'select', type: 'select',
...@@ -219,6 +218,7 @@ const searchConfig = ref([ ...@@ -219,6 +218,7 @@ const searchConfig = ref([
valueKey: 'insuranceCompanyBizId', valueKey: 'insuranceCompanyBizId',
labelKey: 'abbreviation', labelKey: 'abbreviation',
transform: (res) => { transform: (res) => {
console.log(res)
return res?.data.records || [] return res?.data.records || []
} }
}, { }, {
...@@ -227,7 +227,7 @@ const searchConfig = ref([ ...@@ -227,7 +227,7 @@ const searchConfig = ref([
label: '产品计划', label: '产品计划',
api: '/product/api/relProjectProductLaunch/parameter/page', api: '/product/api/relProjectProductLaunch/parameter/page',
keywordField: 'productName', keywordField: 'productName',
requestParams: { fieldBizId:'field_olk1qZe81qHHKXbw',fieldValueBizId:'field_value_uOfJH5ucA2YwJpbn',pageNo: 1, pageSize: 20 }, requestParams: { fieldBizId: 'field_olk1qZe81qHHKXbw', fieldValueBizId: 'field_value_uOfJH5ucA2YwJpbn', pageNo: 1, pageSize: 20 },
placeholder: '输入产品计划名称搜索', placeholder: '输入产品计划名称搜索',
debounceWait: 500, // 自定义防抖时间 debounceWait: 500, // 自定义防抖时间
valueKey: 'productLaunchBizId', valueKey: 'productLaunchBizId',
...@@ -353,14 +353,15 @@ const handleCurrentChange = (val) => { ...@@ -353,14 +353,15 @@ const handleCurrentChange = (val) => {
} }
// 加载表格数据 // 加载表格数据
const loadTableData = async (searchParams={}) => { const loadTableData = async (searchParams = {}) => {
console.log(searchFormRef.value)
loading.value = true loading.value = true
try { try {
const params = { const params = {
...searchParams, ...searchParams,
payoutDateStart: searchParams.payoutDate?.[0] || undefined, payoutDateStart: searchParams.payoutDate?.[0] || undefined,
payoutDateEnd: searchParams.payoutDate?.[1] || undefined, payoutDateEnd: searchParams.payoutDate?.[1] || undefined,
payoutDate:undefined, payoutDate: undefined,
pageNo: currentPage.value, pageNo: currentPage.value,
pageSize: pageSize.value pageSize: pageSize.value
} }
......
<template> <template>
<div> <div>
<CommonPage <CommonPage :operationBtnList="operationBtnList" :showSearchForm="true" :show-pagination="true"
:operationBtnList="operationBtnList" :total="pageTotal" :current-page="currentPage" :page-size="pageSize" @size-change="handleSizeChange"
:showSearchForm="true" @current-change="handleCurrentChange">
:show-pagination="true"
:total="pageTotal"
:current-page="currentPage"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<!-- 搜索区域 --> <!-- 搜索区域 -->
<template #searchForm> <template #searchForm>
<el-form :model="searchFormData" label-width="120px"> <SearchForm ref="searchFormRef" :config="searchConfig" />
<el-row :gutter="20">
<!-- 原有搜索项 -->
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="保单号" prop="policyNo" label-position="top">
<el-input v-model="searchFormData.policyNo" placeholder="请输入保单号" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="入账日(估)" prop="incomeDateRange" label-position="top">
<el-date-picker
v-model="searchFormData.incomeDateRange"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="入账状态" prop="statusList" label-position="top">
<el-select v-model="searchFormData.statusList" placeholder="请选择入账状态" clearable multiple>
<el-option label="待入账" value="0" />
<el-option label="完成入账" value="1" />
<el-option label="部分入账" value="2" />
<el-option label="已失效" value="3" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="入账期数" prop="commissionPeriod" label-position="top">
<el-input v-model="searchFormData.commissionPeriod" placeholder="请输入入账期数" clearable />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="入账项目" prop="commissionName" label-position="top">
<el-input v-model="searchFormData.commissionName" placeholder="请输入入账项目" clearable />
</el-form-item>
</el-col>
<!-- 新增:保险公司远程搜索下拉 -->
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="保险公司" prop="insurerBizId" label-position="top">
<el-select
v-model="searchFormData.insurerBizId"
placeholder="请选择保险公司"
clearable
:remote-method="(keyword) => remoteMethod('insurer', keyword)"
:loading="options.insurer.loading"
filterable
remote
reserve-keyword
>
<el-option
v-for="item in options.insurer.options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<!-- 新增:产品计划远程搜索下拉 -->
<!-- <el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="产品计划" prop="productLaunchBizId" label-position="top">
<el-select
v-model="searchFormData.productLaunchBizId"
placeholder="请选择产品计划"
clearable
:remote-method="(keyword) => remoteMethod('product', keyword)"
:loading="options.product.loading"
filterable
remote
reserve-keyword
>
<el-option
v-for="item in options.product.options"
:key="item.bizId"
:label="item.productName"
:value="item.bizId"
/>
</el-select>
</el-form-item>
</el-col> -->
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="应收单类型" prop="commissionBizType" label-position="top">
<el-select v-model="searchFormData.commissionBizType" placeholder="请选择应收单类型" clearable>
<el-option label="关联保单应收单" value="R" />
<el-option label="非关联保单应收单" value="U" />
</el-select>
</el-form-item>
</el-col>
<!-- 新增:出单团队远程搜索下拉 -->
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="对账公司" prop="reconciliationCompanyBizId" label-position="top">
<el-select
v-model="searchFormData.reconciliationCompany"
placeholder="请选择对账公司"
clearable
:remote-method="(keyword) => remoteMethod('reconciliationCompany', keyword)"
:loading="options.reconciliationCompany.loading"
filterable
remote
reserve-keyword
>
<el-option
v-for="item in options.reconciliationCompany.options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<!-- 新增:出单团队远程搜索下拉 -->
<!-- <el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="出单团队" prop="outTeamBizId" label-position="top">
<el-select
v-model="searchFormData.outTeamBizId"
placeholder="请选择出单团队"
clearable
:remote-method="(keyword) => remoteMethod('team', keyword)"
:loading="options.team.loading"
filterable
remote
reserve-keyword
>
<el-option
v-for="item in options.team.options"
:key="item.teamBizId"
:label="item.teamName"
:value="item.teamBizId"
/>
</el-select>
</el-form-item>
</el-col> -->
</el-row>
</el-form>
</template> </template>
<!-- 列表区域(原有代码不变) --> <!-- 列表区域 -->
<template #table> <template #table>
<div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0"> <div class="statistics-container" v-if="statisticsData.totalPolicyCount > 0">
<el-row :gutter="20"> <el-row :gutter="20">
...@@ -176,16 +29,19 @@ ...@@ -176,16 +29,19 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<el-table :data="tableData" height="400" border highlight-current-row style="width: 100%" v-loading="loading"> <el-table :data="tableData" height="400" border highlight-current-row style="width: 100%"
<el-table-column prop="commissionBizType" label="应收款类型" width="120" sortable /> v-loading="loading">
<el-table-column prop="commissionBizType" label="应收单类型" width="130" sortable>
<template #default="{ row }">
{{ getCommissionBizTypeLabel(row.commissionBizType) }}
</template>
</el-table-column>
<el-table-column prop="receivableNo" label="应收款编号" width="120" /> <el-table-column prop="receivableNo" label="应收款编号" width="120" />
<el-table-column prop="policyNo" label="保单号" width="120" fixed="left" sortable /> <el-table-column prop="policyNo" label="保单号" width="120" fixed="left" sortable />
<el-table-column prop="reconciliationCompany" label="对账公司" width="120" sortable /> <el-table-column prop="reconciliationCompany" label="对账公司" width="120" sortable />
<el-table-column prop="status" label="入账状态" width="120" sortable> <el-table-column prop="status" label="入账状态" width="120" sortable>
<template #default="{ row }"> <template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : row.status === '0' ? 'warning' : row.status === '2' ? 'info' : 'danger'"> {{ getDictLabel('csf_expected_commission_status', row.status) }}
{{ row.status === '1' ? '完成入账' : row.status === '0' ? '待入账' : row.status === '2' ? '部分入账' : '已失效' }}
</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="commissionPeriod" label="入账期数" width="120" sortable /> <el-table-column prop="commissionPeriod" label="入账期数" width="120" sortable />
...@@ -236,7 +92,9 @@ ...@@ -236,7 +92,9 @@
<template #default="{ row }"> <template #default="{ row }">
<el-popover placement="right" :width="200" trigger="click"> <el-popover placement="right" :width="200" trigger="click">
<template #reference> <template #reference>
<el-icon><MoreFilled /></el-icon> <el-icon>
<MoreFilled />
</el-icon>
</template> </template>
<el-menu @select="handleSelect($event, row)" popper-class="custom-menu"> <el-menu @select="handleSelect($event, row)" popper-class="custom-menu">
<el-menu-item :index="item.value" v-for="item in dropdownItems" :key="item.value"> <el-menu-item :index="item.value" v-for="item in dropdownItems" :key="item.value">
...@@ -251,22 +109,11 @@ ...@@ -251,22 +109,11 @@
</CommonPage> </CommonPage>
<!-- 原有弹窗(不变) --> <!-- 原有弹窗(不变) -->
<CommonDialog <CommonDialog dialogTitle="入账记录" dialogWidth="80%" :openDialog="entryRecordDialogTableVisible"
dialogTitle="入账记录" :showAction="false" :showClose="true" @close="entryRecordDialogTableVisible = false">
dialogWidth="80%"
:openDialog="entryRecordDialogTableVisible"
:showAction="false"
:showClose="true"
@close="entryRecordDialogTableVisible = false"
>
<el-table :data="entryRecordDialogTableData" border style="width: 100%"> <el-table :data="entryRecordDialogTableData" border style="width: 100%">
<el-table-column <el-table-column v-for="item in entryRecordDialogTableColumns" :key="item.property"
v-for="item in entryRecordDialogTableColumns" :prop="item.property" :label="item.label" :width="item.width" />
:key="item.property"
:prop="item.property"
:label="item.label"
:width="item.width"
/>
<el-table-column fixed="right" label="操作" min-width="120"> <el-table-column fixed="right" label="操作" min-width="120">
<template #default> <template #default>
<el-button link type="primary" size="small" @click="handleClick"> <el-button link type="primary" size="small" @click="handleClick">
...@@ -277,32 +124,16 @@ ...@@ -277,32 +124,16 @@
</el-table> </el-table>
</CommonDialog> </CommonDialog>
<CommonDialog <CommonDialog dialogTitle="操作记录" dialogWidth="80%" :openDialog="actionRecordsDialogVisible" :showAction="false"
dialogTitle="操作记录" :showClose="true" @close="actionRecordsDialogVisible = false">
dialogWidth="80%"
:openDialog="actionRecordsDialogVisible"
:showAction="false"
:showClose="true"
@close="actionRecordsDialogVisible = false"
>
<el-table :data="actionRecordsDialogTableData" border style="width: 100%"> <el-table :data="actionRecordsDialogTableData" border style="width: 100%">
<el-table-column <el-table-column v-for="item in actionRecordsDialogTableColumns" :key="item.property"
v-for="item in actionRecordsDialogTableColumns" :prop="item.property" :label="item.label" :width="item.width" />
:key="item.property"
:prop="item.property"
:label="item.label"
:width="item.width"
/>
</el-table> </el-table>
</CommonDialog> </CommonDialog>
<CommonDialog <CommonDialog dialogTitle="设置入账状态" dialogWidth="80%" :openDialog="setStatusDialogTableVisible"
dialogTitle="设置入账状态" @close="setStatusDialogTableVisible = false" @confirm="setStatusDialogTableVisible = false">
dialogWidth="80%"
:openDialog="setStatusDialogTableVisible"
@close="setStatusDialogTableVisible = false"
@confirm="setStatusDialogTableVisible = false"
>
<el-form :model="form"> <el-form :model="form">
<el-form-item label="入账状态" label-width="120"> <el-form-item label="入账状态" label-width="120">
<el-select v-model="form.status" placeholder="请选择入账状态"> <el-select v-model="form.status" placeholder="请选择入账状态">
...@@ -332,11 +163,121 @@ import CommonDialog from '@/components/commonDialog' ...@@ -332,11 +163,121 @@ import CommonDialog from '@/components/commonDialog'
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { MoreFilled } from '@element-plus/icons-vue' import { MoreFilled } from '@element-plus/icons-vue'
import { numberWithCommas, debounce } from '@/utils/index' import { receivedFortuneList, commissionEntryRecord, commissionEntryEditRecords, exportReceivedFortune } from '@/api/financial/commission'
import { receivedFortuneList, commissionEntryRecord, commissionEntryEditRecords } from '@/api/financial/commission' import { numberWithCommas } from '@/utils/index'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
import { getDictLabel } from '@/utils/useDict';
import { safeDownload } from '@/utils/safeDownload'
// 应收单类型
const commissionBizTypeOptions = [
{ value: 'R', label: '关联保单应收单' },
{ value: 'U', label: '非关联保单应收单' }
]
// 查询下拉列表数据 const searchFormRef = ref(null)
import { searchInsurers,searchReconciliationCompanies } from '@/api/search' const searchParams = ref({})
const searchConfig = ref([
{
type: 'input',
prop: 'policyNo',
label: '保单号'
},
{
type: 'daterange',
prop: 'entryDate',
label: '入账日(估)',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间'
},
{
type: 'select',
prop: 'statusList',
label: '入账状态',
multiple: true,
dictType: 'csf_expected_commission_status'
},
{
type: 'input',
prop: 'commissionPeriod',
label: '入账期数',
inputType: 'decimal',
rules: [
{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }
]
},
{
type: 'select',
prop: 'fortuneName',
label: '入账项目',
dictType: 'csf_commission_type'
},
{
type: 'select',
prop: 'reconciliationCompanyBizIdList',
label: '对账公司',
api: '/insurance/base/api/insuranceReconciliationCompany/page',
keywordField: 'name',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入对账公司名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'reconciliationCompanyBizId',
labelKey: 'name',
transform: (res) => {
console.log(res)
return res?.data.records || []
}
},
{
type: 'select',
prop: 'insurerCompanyBizIdList',
label: '保险公司',
api: '/insurance/base/api/insuranceCompany/page',
keywordField: 'queryContent',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入保险公司名称搜索',
debounceWait: 500, // 自定义防抖时间
multiple: true,
valueKey: 'insuranceCompanyBizId',
labelKey: 'abbreviation',
transform: (res) => {
return res?.data.records || []
}
}, {
type: 'select',
prop: 'productLaunchBizId',
label: '产品计划',
api: '/product/api/relProjectProductLaunch/parameter/page',
keywordField: 'productName',
requestParams: { fieldBizId: 'field_olk1qZe81qHHKXbw', fieldValueBizId: 'field_value_uOfJH5ucA2YwJpbn', pageNo: 1, pageSize: 20 },
placeholder: '输入产品计划名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'productLaunchBizId',
labelKey: 'productName',
transform: (res) => {
return res?.data.records || []
}
}, {
type: 'select',
prop: 'commissionBizType',
label: '应收单类型',
options: commissionBizTypeOptions,
}, {
type: 'select',
prop: 'teamBizId',
label: '出单团队',
api: '/csf/api/team/page',
keywordField: 'teamName',
requestParams: { pageNo: 1, pageSize: 20 },
placeholder: '输入出单团队名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'teamBizId',
labelKey: 'teamName',
transform: (res) => {
return res?.data.records || []
}
},
])
// 分页相关 // 分页相关
const currentPage = ref(1) const currentPage = ref(1)
...@@ -344,6 +285,13 @@ const pageSize = ref(10) ...@@ -344,6 +285,13 @@ const pageSize = ref(10)
const pageTotal = ref(0) const pageTotal = ref(0)
const loading = ref(false) const loading = ref(false)
// 应收单类型通过value转成label
const getCommissionBizTypeLabel = (value) => {
const item = commissionBizTypeOptions.find(item => item.value === value)
return item?.label || ''
}
// 表格操作菜单 // 表格操作菜单
const dropdownItems = [ const dropdownItems = [
{ label: '入账记录', value: 'entryRecord' }, { label: '入账记录', value: 'entryRecord' },
...@@ -361,13 +309,6 @@ const entryRecordDialogTableData = ref([]) ...@@ -361,13 +309,6 @@ const entryRecordDialogTableData = ref([])
const entryRecordDialogTableColumns = ref([]) const entryRecordDialogTableColumns = ref([])
const actionRecordsDialogTableColumns = ref([]) const actionRecordsDialogTableColumns = ref([])
// 新增:下拉框选项状态管理(loading + 选项列表)
const options = reactive({
insurer: { loading: false, options: [] }, // 保险公司
product: { loading: false, options: [] }, // 产品计划
reconciliationCompany: { loading: false, options: [] }, // 对账公司
})
// 设置入账状态表单 // 设置入账状态表单
const form = reactive({ const form = reactive({
status: '', status: '',
...@@ -376,20 +317,6 @@ const form = reactive({ ...@@ -376,20 +317,6 @@ const form = reactive({
const selectedRow = ref(null) const selectedRow = ref(null)
// 搜索表单数据(新增下拉框绑定字段)
const searchFormData = reactive({
policyNo: '',
incomeDateRange: [],
statusList: [],
commissionName: '',
commissionPeriod: '',
reconciliationCompany: '',
insurerBizId: '', // 保险公司ID
productLaunchBizId: '', // 产品计划ID
commissionBizType: '',
outTeamBizId: '' // 出单团队ID(替换原outTeam)
})
// 表格数据 // 表格数据
const tableData = ref([]) const tableData = ref([])
...@@ -402,80 +329,36 @@ const statisticsData = ref({ ...@@ -402,80 +329,36 @@ const statisticsData = ref({
totalPolicyCount: 0 totalPolicyCount: 0
}) })
// 新增:防抖处理远程搜索(延迟300ms,避免频繁请求)
const debouncedRemoteMethod = debounce(async (type, keyword) => {
try {
// 根据类型设置加载状态
options[type].loading = true
let res = []
// 不同类型调用不同接口
switch (type) {
case 'insurer':
res = await searchInsurers({ queryContent: keyword, pageSize: 20 }) // 保险公司搜索
options.insurer.options = res.data.records.map(item=>{
return {
label: item.abbreviation,
value: item.insuranceCompanyBizId
}
}) || []
break
case 'reconciliationCompany':
res = await searchReconciliationCompanies({ name: keyword, pageSize: 20 }) // 对账公司搜索
options.reconciliationCompany.options = res.data.records.map(item=>{
return {
label: item.name,
value: item.reconciliationCompanyBizId
}
}) || []
break
default:
break
}
} catch (error) {
console.error(`搜索${type}失败:`, error)
ElMessage.error(`加载${type === 'insurer' ? '保险公司' : type === 'product' ? '产品计划' : '出单团队'}失败`)
} finally {
// 关闭加载状态
options[type].loading = false
}
console.log(options)
}, 300)
// 新增:远程搜索方法(对外暴露)
const remoteMethod = (type, keyword) => {
// if (!keyword) {
// // 关键词为空时清空选项(可选)
// options[type].options = []
// return
// }
debouncedRemoteMethod(type, keyword)
}
// remoteMethod('reconciliationCompany', searchFormData.reconciliationCompany)
// remoteMethod('insurer', searchFormData.insurerBizId)
// 按钮事件处理 // 按钮事件处理
const handleAdd = () => ElMessage.info('点击新增按钮') const handleAdd = () => ElMessage.info('点击新增按钮')
const handleImport = () => ElMessage.info('点击导入按钮') const handleImport = () => ElMessage.info('点击导入按钮')
const handleExport = () => ElMessage.info('点击导出按钮') const handleExport = async () => {
// 获取搜索参数
const params = searchFormRef.value?.getSearchParams() || {}
const response = await exportReceivedFortune(params)
// 文件名设置为应收款导出_yyyy-MM-dd hh:mm:ss.xlsx,不需要-,用字符串
const fileName = `应收款导出_${new Date().toLocaleString().replace(/\//g, '').replace(/:/g, '').replace(/\s/g, '')}.xlsx`
await safeDownload(
response,
fileName,
'application/vnd.ms-excel;charset=utf-8'
)
}
const handleReset = () => { const handleReset = () => {
// 重置搜索表单 // 重置搜索表单
Object.keys(searchFormData).forEach(key => { searchFormRef.value.resetForm()
if (Array.isArray(searchFormData[key])) { searchParams.value = {}
searchFormData[key] = [] console.log('表单已重置')
} else {
searchFormData[key] = ''
}
})
// 重置下拉框选项
Object.keys(options).forEach(key => {
options[key].options = []
})
ElMessage.success('搜索条件已重置')
loadTableData() loadTableData()
} }
const handleQuery = () => loadTableData() const handleQuery = () => {
const params = searchFormRef.value.getSearchParams()
loadTableData(params)
}
// 按钮配置 // 按钮配置
const operationBtnList = ref([ const operationBtnList = ref([
...@@ -498,14 +381,14 @@ const handleCurrentChange = (val) => { ...@@ -498,14 +381,14 @@ const handleCurrentChange = (val) => {
} }
// 加载表格数据 // 加载表格数据
const loadTableData = async () => { const loadTableData = async (searchParams = {}) => {
loading.value = true loading.value = true
try { try {
const params = { const params = {
...searchFormData, ...searchParams,
commissionDateStart: searchFormData.incomeDateRange[0] || '', commissionDateStart: searchParams?.entryDate?.[0] || undefined,
commissionDateEnd: searchFormData.incomeDateRange[1] || '', commissionDateEnd: searchParams?.entryDate?.[1] || undefined,
incomeDateRange: undefined, entryDate: undefined,
pageNo: currentPage.value, pageNo: currentPage.value,
pageSize: pageSize.value pageSize: pageSize.value
} }
...@@ -646,11 +529,7 @@ const handleConfirmSetStatus = () => { ...@@ -646,11 +529,7 @@ const handleConfirmSetStatus = () => {
} }
// 初始化加载数据 // 初始化加载数据
onMounted(() => { loadTableData()
loadTableData()
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss"></style>
\ No newline at end of file
</style>
\ No newline at end of file
...@@ -52,6 +52,17 @@ export default defineConfig(({ mode, command }) => { ...@@ -52,6 +52,17 @@ export default defineConfig(({ mode, command }) => {
changeOrigin: true, changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '') rewrite: (p) => p.replace(/^\/dev-api/, '')
}, },
'/csf': {
target: 'http://139.224.145.34:9002',
changeOrigin: true,
secure: false,
// 如果后端需要 host 头
// configure: (proxy, options) => {
// proxy.on('proxyReq', (proxyReq, req, res) => {
// proxyReq.setHeader('host', '139.224.145.34:9002')
// })
// }
},
// springdoc proxy // springdoc proxy
'^/v3/api-docs/(.*)': { '^/v3/api-docs/(.*)': {
target: baseUrl, target: baseUrl,
......
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