Commit 0480ece5 by zhangxingmin

Merge remote-tracking branch 'origin/dev' into prod

# Conflicts:
#	nginx.conf
parents b6642184 f11f221a
......@@ -25,6 +25,7 @@
"axios": "1.9.0",
"clipboard": "2.0.11",
"dayjs": "^1.11.18",
"decimal.js": "^10.6.0",
"echarts": "5.6.0",
"element-plus": "^2.13.5",
"file-saver": "^2.0.5",
......@@ -36,6 +37,7 @@
"lodash": "^4.18.1",
"nprogress": "0.2.0",
"p-limit": "^7.3.0",
"pdfjs-dist": "^5.7.284",
"pinia": "3.0.2",
"spark-md5": "^3.0.2",
"splitpanes": "^4.0.4",
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -360,7 +360,7 @@ export function updateCommissionRecord(data) {
})
}
// 新增出账检核记录
// 出账检核---新增出账检核记录
export function addCheckRecordaddBatch(data) {
return request({
url: '/csf/api/fortune/addBatch',
......@@ -490,7 +490,6 @@ export function billBatchSave(data) {
})
}
// 拆分出账查询-计算目标金额
export function billCalculateToAmount(data) {
return request({
......@@ -499,11 +498,68 @@ export function billCalculateToAmount(data) {
data: data
})
}
// 应收款管理--明细列表
export function newQueryCommissionExpectedByPage(data) {
return request({
url: 'csf/api/CommissionExpected/queryCommissionExpectedByPage/new',
method: 'post',
data: data
})
}
// 应收款管理修改应收记录状态
export function CommissionExpectedChangeStatus(data) {
return request({
url: 'csf/api/CommissionExpected/change_status',
url: 'csf/api/CommissionExpected/edit/status',
method: 'put',
data: data
})
}
// 出账检核--分期出账
export function billSplitApi(data) {
return request({
url: 'csf/api/fortune/split',
method: 'post',
data: data
})
}
//用保单查询结算汇率
export function commissionExchangeRateApi(data) {
return request({
url: 'csf/api/commission/commission_exchange_rate',
method: 'post',
data: data
})
}
//出账检核--设置出账年月(单个)
export function actualPayoutDateApi(data) {
return request({
url: 'csf/api/fortune/edit/actual_payout_date',
method: 'post',
data: data
})
}
//入账检核--修改入账状态
export function editStatusApi(data) {
return request({
url: 'csf/api/commission/edit/status',
method: 'put',
data: data
})
}
//出账检核--修改结算汇率
export function editExchangeRateApi(data) {
return request({
url: 'csf/api/fortune/edit/exchange_rate',
method: 'post',
data: data
})
}
// 批量设置出账年月实
export function batchActualPayoutDate(data) {
return request({
url: '/csf/api/fortune/edit/actual_payout_date/batch',
method: 'post',
data: data
})
......
......@@ -139,3 +139,10 @@ export function addSinglePremiumRemittance(data) {
data: data
})
}
// 保单详情
export function policyDetail(policyNo) {
return request({
url: `/csf/api/policy_follow/detail?policyNo=${policyNo}`,
method: 'get'
})
}
......@@ -256,3 +256,4 @@ export function getProductList(data) {
data: data
})
}
<template>
<el-dialog
v-model="dialogVisible"
:title="displayFileName"
width="70%"
:close-on-click-modal="false"
destroy-on-close
@close="handleClose"
>
<div class="preview-container">
<!-- 图片预览 -->
<div v-if="previewType === 'image'" class="preview-image-wrapper">
<img :src="previewUrl" class="preview-image" alt="预览图片" />
</div>
<!-- PDF 预览 -->
<iframe
v-else-if="previewType === 'pdf'"
:src="previewUrl"
class="preview-pdf"
frameborder="0"
></iframe>
<!-- 不支持预览 -->
<div v-else-if="previewType === 'unsupported'" class="preview-unsupported">
<el-icon :size="48" color="#909399"><Document /></el-icon>
<p>暂不支持预览此类型文件</p>
<el-button type="primary" @click="handleClose">关闭</el-button>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { Document } from '@element-plus/icons-vue'
// --- Props 定义 ---
interface Props {
modelValue: boolean // 控制弹窗显示/隐藏(支持 v-model)
fileUrl: string // 文件 URL 或 Blob URL
fileName?: string // 文件名(用于弹窗标题及类型推断)
fileType?: 'image' | 'pdf' | 'unsupported' // 手动指定文件类型(可选)
}
const props = withDefaults(defineProps<Props>(), {
fileName: '文件预览',
fileType: undefined
})
// --- Emits 定义 ---
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'close'): void
}>()
// --- 内部状态 ---
// 用于实际绑定到 img/iframe 的 URL,关闭时会清空
const previewUrl = ref('')
// 弹窗显示状态(双向绑定)
const dialogVisible = computed({
get: () => props.modelValue,
set: val => emit('update:modelValue', val)
})
// 最终显示的标题
const displayFileName = computed(() => props.fileName || '文件预览')
// --- 文件类型推断 ---
const previewType = computed<'image' | 'pdf' | 'unsupported'>(() => {
// 1. 若外部显式传入 fileType,优先使用
if (props.fileType) {
return props.fileType
}
// 2. 根据 fileUrl 或 fileName 的后缀名自动判断
const url = props.fileUrl || ''
const name = props.fileName || ''
const lowerUrl = url.toLowerCase()
const lowerName = name.toLowerCase()
const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg']
const isImage = (str: string) => imageExts.some(ext => str.endsWith(ext))
const isPdf = (str: string) => str.endsWith('.pdf')
if (isImage(lowerUrl) || isImage(lowerName)) return 'image'
if (isPdf(lowerUrl) || isPdf(lowerName)) return 'pdf'
return 'unsupported'
})
// --- 同步外部 URL 到预览 URL ---
// 当弹窗打开时,将 fileUrl 赋值给 previewUrl
// 当弹窗关闭时,清空 previewUrl,释放资源
watch(
() => props.modelValue,
visible => {
if (visible && props.fileUrl) {
previewUrl.value = props.fileUrl
} else if (!visible) {
previewUrl.value = ''
}
},
{ immediate: true }
)
// 如果弹窗已打开但 fileUrl 发生变化(例如父组件动态修改),同步更新预览内容
watch(
() => props.fileUrl,
newUrl => {
if (dialogVisible.value && newUrl) {
previewUrl.value = newUrl
}
}
)
// --- 关闭处理 ---
const handleClose = () => {
dialogVisible.value = false // 关闭弹窗
previewUrl.value = '' // 清空预览地址
emit('close') // 通知父组件
}
</script>
<style scoped>
.preview-container {
width: 100%;
min-height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
.preview-image-wrapper {
width: 100%;
text-align: center;
}
.preview-image {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
.preview-pdf {
width: 100%;
height: 70vh;
}
.preview-unsupported {
text-align: center;
padding: 40px;
}
.preview-unsupported p {
margin: 20px 0;
color: #909399;
}
</style>
......@@ -34,7 +34,7 @@
:type="btn.type || 'primary'"
:icon="btn.icon"
:size="btn.size || 'default'"
@click="handleButtonClick(btn)"
@click.stop="handleButtonClick(btn)"
:disabled="btn.disabled"
:loading="btn.loading"
:class="btn.customClass"
......@@ -54,7 +54,7 @@
:type="btn.type || 'primary'"
:icon="btn.icon"
:size="btn.size || 'default'"
@click="handleButtonClick(btn)"
@click.stop="handleButtonClick(btn)"
:disabled="btn.disabled"
:loading="btn.loading"
:class="btn.customClass"
......@@ -316,6 +316,9 @@ const checkConditions = () => {
// 处理按钮点击
const handleButtonClick = async (btn: OperationButton) => {
console.log('====================================')
console.log('按钮')
console.log('====================================')
// 触发通用按钮点击事件
emit('btn-click', btn)
......@@ -362,6 +365,9 @@ const handleSizeChange = (size: number) => {
const handleCurrentChange = (page: number) => {
currentPage.value = page
console.log('====================================')
console.log('当前页')
console.log('====================================')
emit('current-change', page)
}
......
......@@ -11,6 +11,7 @@ const useUserStore = defineStore('user', {
state: () => ({
token: getToken(),
id: '',
realName:'',
name: '',
nickName: '',
avatar: '',
......@@ -74,6 +75,7 @@ const useUserStore = defineStore('user', {
this.id = user.userId
this.name = user.userName
this.realName = user.realName
this.nickName = user.nickName
this.avatar = avatar
this.isSuperAdmin = user.isSuperAdmin
......
{
"paymentMethod": "CHECK",
"paymentAmount": "111",
"paymentCurrency": "HKD",
"paymentRel": "SBR",
"payer": "111",
"payingBank": "bank_1002",
"paymentAccount": "1111",
"currency": "",
"paymentVoucherList": [
{
"fileName": "icon5.png",
"fileType": "png",
"fileUrl": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/b4241a95a39d4655a79c706d7ec37f85.png",
"url": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/b4241a95a39d4655a79c706d7ec37f85.png"
},
{
"fileName": "cardSix1.png",
"fileType": "png",
"fileUrl": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/41bd22390f6a4a69a5ed8d9c8758ef94.png",
"url": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/41bd22390f6a4a69a5ed8d9c8758ef94.png"
}
],
"accountVerificationList": [
{
"fileName": "icon6.png",
"fileType": "png",
"fileUrl": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/ce6f4781b8f3443f92eddbf13ecefe42.png",
"url": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/ce6f4781b8f3443f92eddbf13ecefe42.png"
},
{
"fileName": "icon4.png",
"fileType": "png",
"fileUrl": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/24e9786aa52d48caba0bfc48e7f4f146.png",
"url": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/24e9786aa52d48caba0bfc48e7f4f146.png"
}
],
"apiPremiumRemittanceFileDtoList": [
{
"fileName": "icon5.png",
"fileType": "png",
"fileUrl": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/bca0400a90ae4c1da2373e3cf4de0fc7.png",
"url": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/bca0400a90ae4c1da2373e3cf4de0fc7.png"
},
{
"fileName": "homeSelect1.png",
"fileType": "png",
"fileUrl": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/138932043c6244cb8a006c2ab81b4bab.png",
"url": "https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/138932043c6244cb8a006c2ab81b4bab.png"
}
],
"id": 1768282536268
"id": 2,
"fortuneBizId": "fortune_LCESmX7iy1TJRcaE",
"reconciliationYearMonth": null,
"fortuneBizType": "R",
"isPart": 0,
"expectedFortuneBizId": "expected_fortune_kVBKKtNB38QcFKI6",
"payableNo": "R-CSF26000001",
"policyNo": "111",
"policyCurrency": "美元",
"premium": 10000,
"productName": "来佣000",
"insuranceCompany": "友邦保險",
"commissionBizId": "commission_b9els2m9Lkocu4RB",
"commissionExpectedBizId": "commission_expected_mUlJXrNqmpioXbsY",
"commissionPaidAmount": 38981,
"commissionPaidRatio": 50,
"fortunePeriod": 1,
"fortuneTotalPeriod": 1,
"broker": "张平",
"brokerBizId": "client_user_mdeCtfYb9oRbYnAF",
"team": "AGHS",
"teamBizId": "dept_fgUS1281YFdsq3",
"fortuneName": "销售佣金",
"fortuneType": "1",
"ruleAmount": -100,
"ruleCurrency": "HKD",
"exchangeRate": 1,
"originalCurrency": null,
"originalAmount": null,
"originalToHkdRate": null,
"payoutCurrency": null,
"payoutAmount": null,
"hkdToPayoutRate": null,
"hkdAmount": -100,
"fortunePaidAmount": 0,
"fortuneUnpaidAmount": -100,
"currentPaymentAmount": -100,
"currentPaymentHkdAmount": -100,
"currentPaymentRatio": 0,
"fortuneUnpaidRatio": 100,
"status": "0",
"payoutDate": null,
"actualPayoutDate": null,
"isTax": 0,
"taxAmount": null,
"netAmount": null,
"salaryBizId": null,
"baseRuleBizId": null,
"settlementBizId": "1",
"calculationFormula": null,
"remark": null,
"creatorId": null,
"reconciliationOperator": null,
"updaterId": null,
"createTime": "2026-04-17 15:47:21",
"updateTime": "2026-04-27 14:53:11"
}
import { ElMessage } from 'element-plus'
export function copyToClipboard(text) {
// 方案1:使用现代 Clipboard API
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text).catch((err) => {
console.error('Clipboard API 写入失败:', err);
ElMessage.error('复制失败')
// 降级到传统方案
fallbackCopyText(text);
});
}
// 方案2:降级使用传统方法
else {
fallbackCopyText(text);
}
}
// 传统复制方法
// 传统降级复制方法
function fallbackCopyText(text, typeName = '内容') {
const textarea = document.createElement('textarea');
textarea.value = text;
// 防止页面滚动或闪烁
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
textarea.style.left = '-9999px';
document.body.appendChild(textarea);
try {
textarea.select();
textarea.setSelectionRange(0, 99999); // 对于移动设备
document.execCommand('copy');
console.log('传统方法复制成功');
ElMessage.success(`${typeName}已复制`)
textarea.setSelectionRange(0, 99999); // 兼容移动端
const successful = document.execCommand('copy');
if (successful) {
ElMessage.success(`${typeName}已复制`);
} else {
throw new Error('execCommand 返回失败');
}
} catch (err) {
console.error('传统方法复制失败:', err)
ElMessage.error('复制失败');
console.error('传统方法复制失败:', err);
ElMessage.error('复制失败,请手动复制');
} finally {
document.body.removeChild(textarea);
}
}
export default {
copyToClipboard
}
// 主导出函数,接收 text 和 typeName 两个参数
export default function copyToClipboard(text, typeName = '内容') {
// 1. 判断是否在安全上下文(HTTPS/localhost) 且 浏览器支持 Clipboard API
if (window.isSecureContext && navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(() => {
ElMessage.success(`${typeName}已复制`);
}).catch((err) => {
console.error('Clipboard API 写入失败,降级处理:', err);
// API 调用失败(如权限被拒),自动降级到传统方案
fallbackCopyText(text, typeName);
});
} else {
// 2. 非安全环境(如 HTTP)或不支持新 API,直接使用传统降级方案
fallbackCopyText(text, typeName);
}
}
\ No newline at end of file
import Decimal from 'decimal.js'
// 格式化金额为货币格式
export function formatCurrency(value, currency = '', fixedDigits = 2) {
if (value === undefined || value === null) return currency + '0.00'
......@@ -206,11 +207,57 @@ export function inputThousands(value) {
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
return parts.join('.')
}
/**
* 金额计算(乘/除),保留指定位数小数(四舍五入)
* @param {number|string|Decimal} a - 被乘数(或被除数)
* @param {number|string|Decimal} b - 乘数(或除数)
* @param {number} digits - 小数位数(默认2)
* @param {string} [type] - 'Divided' 表示除法,否则乘法
* @returns {number|null} 计算结果(数字类型)
*/
export function calculateAmount(a, b, digits = 2, type) {
if (a == null || b == null) return null
try {
const da = new Decimal(a)
const db = new Decimal(b)
let result
if (type === 'Divided') {
if (db.isZero()) return null // 除零保护
result = da.dividedBy(db)
} else {
result = da.times(db)
}
// 四舍五入到指定位数,并转为 Number(若需要字符串可用 .toFixed(digits))
// return result.toDecimalPlaces(digits, Decimal.ROUND_HALF_UP).toNumber()
return result.toFixed(digits, Decimal.ROUND_HALF_UP)
} catch (e) {
return null
}
}
/**
* 格式化数字,保留最多 maxDigits 位小数,自动去除尾随零
* @param {number|null|undefined} value - 原始数值
* @param {number} maxDigits - 最多保留的小数位数,默认 5
* @param {string} defaultValue - 无效值时的占位符,默认 '-'
* @returns {string} 格式化后的字符串
*/
export function formatNumberToMaxDigits(value, maxDigits = 5, defaultValue = '-') {
if (value === undefined || value === null || isNaN(Number(value))) {
return defaultValue;
}
const num = Number(value);
// 先固定保留 maxDigits 位小数(四舍五入)
const fixed = num.toFixed(maxDigits);
// 移除末尾的0,如果小数部分全是0则连小数点一起移除
const trimmed = fixed.replace(/\.?0+$/, '');
return trimmed;
}
export default {
formatCurrency,
numberFormat,
formatThousands,
formatThousandsSimple,
inputThousands
inputThousands,
calculateAmount
}
......@@ -597,7 +597,42 @@ export const validateIdCardSimple = (rule, value, callback) => {
callback()
}
/**
* 英文姓名校验规则
*/
export const validateEnglish2 = (value) => {
if (!value) {
// 如果值为空且字段不是必填的,直接通过校验
return
}
// 1. 基本字符检查
if (!/^[A-Za-z\s\-'.]+$/.test(value)) {
return "包含非法字符,只允许英文字母、空格、-、'和."
}
// 2. 首字母大写检查
// if (!/^[A-Z]/.test(value)) {
// return callback(new Error('名字应以大写字母开头'))
// }
// 3. 长度检查
if (value.length < 2) {
return '名字至少需要2个字符'
}
// 4. 连续特殊字符检查
if (/[\s\-'.]{2,}/.test(value)) {
return '不能连续使用特殊字符'
}
// 5. 开头或结尾不能是特殊字符
if (/^[\s\-'.]|[\s\-'.]$/.test(value.trim())) {
return '名字不能以特殊字符开头或结尾'
}
return ''
}
// 将身份证验证方法添加到默认导出对象中
export default {
validateEnglish,
......@@ -605,5 +640,6 @@ export default {
validatePhone,
validateBMI,
validateIdCard,
validateIdCardSimple
validateIdCardSimple,
validateEnglish2
}
......@@ -42,7 +42,8 @@
:border="true"
>
<el-table-column type="selection" width="55" :reserve-selection="true" />
<el-table-column prop="fortuneAccountBizId" label="业务ID" min-width="120" sortable />
<el-table-column prop="businessNo" label="业务编号" min-width="120" />
<!-- <el-table-column prop="fortuneAccountBizId" label="业务ID" 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="hkdAmount" label="出账金额" width="120" sortable>
......@@ -62,11 +63,17 @@
</el-tag>
</template>
</el-table-column>
<el-table-column
<!-- <el-table-column
prop="fortuneAccountDate"
label="出账日"
min-width="150"
show-overflow-tooltip
/> -->
<el-table-column
prop="fortuneAccountMonth"
label="出账年月"
min-width="150"
show-overflow-tooltip
/>
<!-- <el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip /> -->
<el-table-column fixed="right" label="操作" min-width="120">
......@@ -174,8 +181,23 @@
</template>
</el-table-column>
<el-table-column label="出账机构" prop="billOrg" width="150">
<template #default="scope">
<!-- <template #default="scope">
<el-input v-model="scope.row.billOrg" placeholder="请输入" clearable />
</template> -->
<template #default="scope">
<el-select
v-model="scope.row.billOrg"
style="width: 100%"
placeholder="请选择"
clearable
>
<el-option
v-for="item in csf_bill_org"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="原币种金额" prop="fromAmount" width="150">
......@@ -303,9 +325,9 @@
:showAction="true"
:showClose="true"
@close="showSalarySetting = false"
@confirm = "salaryDataSetting"
@confirm="salaryDataSetting"
>
<el-date-picker
<el-date-picker
v-model="fortuneAccountDate"
type="date"
placeholder="选择薪资日"
......@@ -341,7 +363,7 @@ import { debounce } from 'lodash-es'
const amountInput = usePositiveDecimal(4) // 默认2位小数
const { proxy } = getCurrentInstance()
const { bx_currency_type } = proxy.useDict('bx_currency_type')
const { bx_currency_type, csf_bill_org } = proxy.useDict('bx_currency_type', 'csf_bill_org')
const searchFormRef = ref(null)
const searchParams = ref({})
const searchConfig = ref([
......@@ -351,7 +373,7 @@ const searchConfig = ref([
label: '转介人',
api: '/insurance/base/api/userSaleExpand/page',
keywordField: 'realName',
requestParams: { pageNo: 1, pageSize: 20 },
requestParams: { pageNo: 1, pageSize: 9999 },
placeholder: '输入转介人名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'clientUserBizId',
......@@ -420,8 +442,8 @@ const debounceChangeRateMap = new WeakMap()
const debounceChangeToAmountMap = new WeakMap()
// 表格操作菜单
const dropdownItems = [
{ label: '拆分出账', value: 'splitBilling' },
{ label: '设置出账日', value: 'settingSalaryDate' },
{ label: '拆分出账', value: 'splitBilling' }
// { label: '设置出账日', value: 'settingSalaryDate' }
// { label: '查看记录', value: 'viewRecord' }
]
//=============拆分出账开始================
......@@ -470,7 +492,7 @@ const handleFromAmountBlur = row => {
// 汇率输入处理
const handleExchangeRateInput = (val, row) => {
row.exchangeRate = amountInput.filterInput(val, 4)
row.exchangeRate = amountInput.filterInput(val, 5)
getDebouncedChangeToAmount(row)(row)
}
......@@ -684,39 +706,40 @@ const handleSelect = (e, row) => {
billCurrentPage.value = 1
billTableList.value = []
getSplitTableList()
}else if(e==='settingSalaryDate'){
} else if (e === 'settingSalaryDate') {
console.log('更新薪资数据')
fortuneAccountDate.value= currentRow.value.fortuneAccountDate || ''
showSalarySetting.value = true;
fortuneAccountDate.value = currentRow.value.fortuneAccountDate || ''
showSalarySetting.value = true
}
}
const salaryDataSetting = async (e)=>{
try{
const params = {
fortuneAccountBizId : currentRow.value.fortuneAccountBizId,
fortuneAccountDate:fortuneAccountDate.value
};
const response = await updatePolicyFortuneAccount(params)
if(response.code==200){
showSalarySetting.value = false;
ElMessage.success('修改成功')
getList()
}
}catch (error) {
const salaryDataSetting = async e => {
try {
const params = {
fortuneAccountBizId: currentRow.value.fortuneAccountBizId,
fortuneAccountDate: fortuneAccountDate.value
}
const response = await updatePolicyFortuneAccount(params)
if (response.code == 200) {
showSalarySetting.value = false
ElMessage.success('修改成功')
getList()
}
} catch (error) {
console.error('获取数据失败:', error)
ElMessage.error('修改失败')
}
}
// 分页事件
const handleSizeChange = val => {
pageSize.value = val
getList()
const params = searchFormRef.value.getFormData()
getList(params)
}
const handleCurrentChange = val => {
currentPage.value = val
getList()
const params = searchFormRef.value.getFormData()
getList(params)
}
// 设置当前页的选中状态
......@@ -793,7 +816,7 @@ const getList = async (searchParams = {}) => {
payoutDate: undefined,
pageNo: currentPage.value,
pageSize: pageSize.value,
statusList:searchParams.statusList || ['6']
statusList: searchParams.statusList || ['6']
}
const response = await getReferrerFortuneList(params)
......@@ -829,6 +852,7 @@ const getStatusType = status => {
// 查询
const handleQuery = () => {
currentPage.value = 1
const params = searchFormRef.value.getFormData()
console.log('父组件发起查询:', params)
clearAllSelection()
......
<template>
<div class='app-container'>
<CommonPage :operationBtnList='operationBtnList' :visibleDefaultButtons='visibleDefaultButtons'
:showSearchForm='true' :show-pagination='true' :total='pageTotal' :current-page='currentPage'
:page-size='pageSize' @size-change='handleSizeChange' @current-change='handleCurrentChange'>
<div class="app-container">
<CommonPage
:operationBtnList="operationBtnList"
:visibleDefaultButtons="visibleDefaultButtons"
:showSearchForm="true"
:show-pagination="true"
:total="pageTotal"
:current-page="currentPage"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<!-- 搜索区域 -->
<template #searchForm>
<SearchForm ref="searchFormRef" :config="searchConfig" />
</template>
<!-- 列表区域 -->
<template #table>
<el-table :data="tableData" v-loading="loading" ref="tableRef"
row-key="salarySplitNo" :reserve-selection="true" :border="true">
<el-table-column v-for="(column, index) in tableColumns" :key="index" :fixed="column.fixed"
:prop="column.prop" :label="column.label" :width="column.width" :sortable="column.sortable"
:formatter="column.formatter" />
<el-table
:data="tableData"
v-loading="loading"
ref="tableRef"
row-key="salarySplitNo"
:reserve-selection="true"
:border="true"
>
<el-table-column
v-for="(column, index) in tableColumns"
:key="index"
:fixed="column.fixed"
:prop="column.prop"
:label="column.label"
:width="column.width"
:sortable="column.sortable"
:formatter="column.formatter"
/>
</el-table>
</template>
</CommonPage>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import CommonPage from '@/components/commonPage'
import {salarySummary,exportPayRoll} from '@/api/financial/commission'
import { salarySummary, exportPayRoll } from '@/api/financial/commission'
import { ElMessageBox, ElMessage } from 'element-plus'
import SearchForm from '@/components/SearchForm/SearchForm.vue'
import { formatNumberToMaxDigits } from '@/utils/number'
const searchFormRef = ref(null)
const searchParams = ref({})
const searchConfig = ref([
......@@ -37,13 +57,13 @@ const searchConfig = ref([
label: '转介人',
api: '/insurance/base/api/userSaleExpand/page',
keywordField: 'realName',
requestParams: { pageNo: 1, pageSize: 20 },
requestParams: { pageNo: 1, pageSize: 9999 },
placeholder: '输入转介人名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'clientUserBizId',
labelKey: 'realName',
multiple: true,
transform: (res) => {
transform: res => {
return res?.data.records || []
}
},
......@@ -57,25 +77,36 @@ const searchConfig = ref([
{
type: 'input',
prop: 'billOrg',
label: '出账机构',
label: '出账机构'
},
{
type: 'input',
prop: 'businessNo',
label: '业务编号'
}
])
const tableColumns = ref([
{ prop: 'salarySplitNo', label: '发放编号', sortable: true, width: '150',fixed:'left'},
{ prop: 'brokerName', label: '转介人', sortable: true, width: '150',fixed:'left'},
{ prop: 'internalNumber', label: '内部编号', sortable: true, width: '150'},
{ prop: 'team', label: '所属团队', sortable: true, width: '150'},
{ prop: 'fromAmount', label: '原币种金额', sortable: true, width: '150'},
{ prop: 'currency', label: '原币种', sortable: true, width: '150'},
{ prop: 'exchangeRate', label: '汇率(原币种->目标币种)', sortable: true, width: '150'},
{ prop: 'toAmount', label: '目标金额', sortable: true, width: '150'},
{ prop: 'toCurrency', label: '目标币种', sortable: true, width: '150'},
{ prop: 'fortuneAccountMonth', label: '出账月(实)', sortable: true, width: '150'},
{ prop: 'billOrg', label: '出账机构', sortable: true, width: '150'},
{ prop: 'hkdAmount', label: '本期总出账金额(原币种)', sortable: true, width: '150'},
{ prop: 'fortuneAccountBizId', label: '出账记录业务id', sortable: true, width: '150'},
{ prop: 'salarySplitNo', label: '发放编号', sortable: true, width: '150', fixed: 'left' },
{ prop: 'businessNo', label: '业务编号', width: '150', fixed: 'left' },
{ prop: 'brokerName', label: '转介人', sortable: true, width: '150', fixed: 'left' },
{ prop: 'internalNumber', label: '内部编号', sortable: true, width: '150' },
{ prop: 'team', label: '所属团队', sortable: true, width: '150' },
{ prop: 'fromAmount', label: '原币种金额', sortable: true, width: '150' },
{ prop: 'currency', label: '原币种', sortable: true, width: '150' },
{
prop: 'exchangeRate',
label: '汇率(原币种->目标币种)',
sortable: true,
width: '150',
formatter: row => formatNumberToMaxDigits(row.exchangeRate, 5, '-')
},
{ prop: 'toAmount', label: '目标金额', sortable: true, width: '150' },
{ prop: 'toCurrency', label: '目标币种', sortable: true, width: '150' },
{ prop: 'fortuneAccountMonth', label: '出账月(实)', sortable: true, width: '150' },
{ prop: 'billOrg', label: '出账机构', sortable: true, width: '150' },
{ prop: 'hkdAmount', label: '本期总出账金额(原币种)', sortable: true, width: '150' }
// { prop: 'fortuneAccountBizId', label: '出账记录业务id', sortable: true, width: '150'},
])
// 添加表格引用
const tableRef = ref()
......@@ -89,27 +120,25 @@ const currentPage = ref(1)
const pageSize = ref(10)
const pageTotal = ref(0)
// 分页事件
const handleSizeChange = (val) => {
const handleSizeChange = val => {
pageSize.value = val
getList()
}
const handleCurrentChange = (val) => {
const handleCurrentChange = val => {
currentPage.value = val
getList()
}
// 获取数据列表
const getList = async (searchParams = {}) => {
loading.value = true
try {
const params = {
...searchParams,
startMonth:searchParams.payoutMonth ? searchParams.payoutMonth[0] : '',
endMonth:searchParams.payoutMonth ? searchParams.payoutMonth[1] :'',
payoutMonth:undefined,
startMonth: searchParams.payoutMonth ? searchParams.payoutMonth[0] : '',
endMonth: searchParams.payoutMonth ? searchParams.payoutMonth[1] : '',
payoutMonth: undefined,
pageNo: currentPage.value,
pageSize: pageSize.value
}
......@@ -129,6 +158,7 @@ getList()
// 查询
const handleQuery = () => {
currentPage.value = 1
const params = searchFormRef.value.getFormData()
console.log('父组件发起查询:', params)
getList(params)
......@@ -147,23 +177,20 @@ const handleExport = async () => {
// 获取搜索参数
let params = searchFormRef.value?.getFormData()
params = {
...params,
startMonth:params.payoutMonth ? params.payoutMonth[0] : '',
endMonth:params.payoutMonth ? params.payoutMonth[1] :'',
payoutMonth:undefined,
}
...params,
startMonth: params.payoutMonth ? params.payoutMonth[0] : '',
endMonth: params.payoutMonth ? params.payoutMonth[1] : '',
payoutMonth: undefined
}
const response = await exportPayRoll(params)
if(response.data && response.data.url){
if (response.data && response.data.url) {
window.open(response.data.url)
}else{
} else {
ElMessage.error('导出失败')
}
}
const visibleDefaultButtons = ref(['export','reset', 'query'])
const visibleDefaultButtons = ref(['export', 'reset', 'query'])
// 按钮配置
const operationBtnList = ref([
{
......@@ -176,13 +203,12 @@ const operationBtnList = ref([
direction: 'right',
click: handleQuery
},
{
{
key: 'export',
direction: 'right',
click: handleExport
}
])
</script>
<style scoped></style>
\ No newline at end of file
<style scoped></style>
......@@ -369,13 +369,14 @@
<template #table>
<el-table v-loading="tableLoading" :data="tableData" @row-click="handleExport">
<!-- <el-table-column type="selection" width="55" align="center" /> -->
<el-table-column label="姓名" align="center" prop="nameCn" width="200" />
<el-table-column label="姓名" align="center" prop="nameCn" width="200" fixed="left" />
<el-table-column label="姓名(英文)" align="center" prop="namePyEn" width="200" />
<el-table-column label="性别" align="center" prop="gender">
<template #default="scope">
<dict-tag :options="fetchDictData('sys_gender')" :value="scope.row.gender" />
</template>
</el-table-column>
<el-table-column label="证件类型" align="center" prop="documentType">
<el-table-column label="证件类型" align="center" prop="documentType" width="150">
<template #default="scope">
<dict-tag
:options="fetchDictData('csf_id_type')"
......@@ -384,8 +385,8 @@
</template>
</el-table-column>
<el-table-column label="证件号码" align="center" prop="idNumber" />
<el-table-column label="电话号码" align="center" prop="mobile" />
<el-table-column label="证件号码" align="center" prop="idNumber" width="200" />
<el-table-column label="电话号码" align="center" prop="mobile" width="200" />
<el-table-column label="出生日期" align="center" width="180" prop="birthday">
<template #default="scope">
<span>{{ scope.row.birthday ? formatToDate(scope.row.birthday) : '--' }}</span>
......
......@@ -12,7 +12,7 @@
<span class="iconfont icon-yanqiweiwancheng"></span>
<span
>{{ parseTime(processInfo.createTime) }}{{
processInfo.userBizId || '--'
processInfo.creatorName || '--'
}}创建</span
>
</div>
......@@ -157,11 +157,13 @@ const route = useRoute()
const router = useRouter()
const activeName = ref()
const loading = ref(false)
console.log('userStore', userStore)
const processInfo = ref({
fnaNo: '--',
status: '未保存',
createTime: proxy.parseTime(new Date()),
customerName: userStore.name
creatorName: userStore.realName
}) // 流程详情信息
const updateStatus = ref(false)
const dictTypeLists = ref([])
......@@ -321,7 +323,7 @@ const getDictsData = async () => {
projectBizId: userStore.projectInfo.projectBizId,
tenantBizId: userStore.projectInfo.tenantBizId,
fieldBizId: 'field_olk1qZe81qHHKXbw',
fieldValueBizId: 'field_value_yXzTigvgUdRMFpoR',
fieldValueBizId: 'field_value_yXzTigvgUdRMFpoR'
}
const response6 = await secondAdditonalList(params6)
if (response6.code == 200) {
......@@ -399,6 +401,9 @@ function getProcessInfo(fnaBizId, changeTab, currentTab) {
getProcessDetail(fnaBizId).then(async res => {
if (res.code == 200) {
processInfo.value = res.data
// if (!processInfo.value.creatorName) {
// processInfo.value.creatorName = userStore.realName
// }
tabsList.value.forEach(item => {
if (res.data[item.key] && item.status) {
item.status = '1'
......
......@@ -61,6 +61,16 @@
</el-select>
</el-form-item>
</el-col>
<el-col :sm="12" :lg="6" :xs="24">
<el-form-item label="创建人" prop="creatorName">
<el-input
v-model="queryParams.creatorName"
placeholder="请输入创建人"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
......@@ -76,22 +86,26 @@
max-height="380px"
>
<el-table-column type="index" width="100" label="序号" />
<el-table-column label="客户姓名" align="center" prop="customerName" width="180" >
<template #default="scope">
<el-table-column label="客户姓名" align="center" prop="customerName" width="180">
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.customerName,'客户姓名')">{{ scope.row.customerName }}</span>
<span @click="copyToClipboard(scope.row.customerName, '客户姓名')">{{
scope.row.customerName
}}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="中文姓名" align="center" prop="customerNameCn" width="180" >
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.customerNameCn,'中文姓名')">{{ scope.row.customerNameCn }}</span>
</el-button>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column label="中文姓名" align="center" prop="customerNameCn" width="180">
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.customerNameCn, '中文姓名')">{{
scope.row.customerNameCn
}}</span>
</el-button>
</template>
</el-table-column>
<!-- <el-table-column label="状态" align="center" prop="status" width="100" :formatter="getDictLabel('csf_fna_status')"/> -->
<el-table-column label="流程状态" sortable align="center" prop="status" width="200">
<el-table-column label="流程状态" sortable align="center" prop="status" width="200">
<template #default="scope">
<span>{{ selectDictLabel(csf_fna_status, scope.row.status) }}</span>
</template>
......@@ -101,13 +115,16 @@
align="center"
prop="fnaNo"
width="240"
show-overflow-tooltip>
<template #default="scope">
show-overflow-tooltip
>
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.fnaNo,'流程编号')">{{ scope.row.fnaNo }}</span>
<span @click="copyToClipboard(scope.row.fnaNo, '流程编号')">{{
scope.row.fnaNo
}}</span>
</el-button>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column
label="预约编号"
align="center"
......@@ -117,12 +134,14 @@
>
<template #default="scope">
<el-button link size="default">
<span @click="copyToClipboard(scope.row.appointmentNo,'预约编号')">{{ scope.row.appointmentNo }}</span>
<span @click="copyToClipboard(scope.row.appointmentNo, '预约编号')">{{
scope.row.appointmentNo
}}</span>
</el-button>
</template>
</el-table-column>
<!-- <el-table-column label="新单编号" align="center" prop="policyId" /> -->
</template>
</el-table-column>
<el-table-column label="保单号" align="center" prop="policyNo" width="180" />
<el-table-column label="创建人" align="center" prop="creatorName" width="180" />
<el-table-column label="创建时间" sortable align="center" prop="createTime" width="200">
<template #default="scope">
......@@ -181,7 +200,7 @@ import { getFnaList, deleteFna, subProcess } from '@/api/sign/fna'
import useUserStore from '@/store/modules/user'
import { MoreFilled, InfoFilled } from '@element-plus/icons-vue'
import useDictStore from '@/store/modules/dict'
import copyToClipboard from '@/utils/copyToClipboard';
import copyToClipboard from '@/utils/copyToClipboard'
const dictStore = useDictStore()
const userStore = useUserStore()
const router = useRouter()
......@@ -395,7 +414,6 @@ function handleUpdate(row) {
}
getList()
</script>
<style lang="scss" scoped>
.bottomBtn {
......
......@@ -487,7 +487,7 @@ const processInfo = ref({
fnaNo: '暂无',
status: '未完成',
createTime: proxy.parseTime(new Date()),
customerName: userStore.name
creatorName: userStore.realName
})
const editStatus = ref(null) //编辑状态
const execlDialog = ref(false)
......
......@@ -232,7 +232,7 @@ import {
getItineraryExprot
} from '@/api/sign/appointment'
import useUserStore from '@/store/modules/user'
import { copyToClipboard } from '@/utils/copyToClipboard'
import copyToClipboard from '@/utils/copyToClipboard'
const dictStore = useDictStore()
const userStore = useUserStore()
const router = useRouter()
......
......@@ -386,6 +386,7 @@ const handleReset = () => {
loadTableData()
}
const handleQuery = async () => {
currentPage.value = 1
const params = searchFormRef.value.getFormData()
console.log('params', params)
......@@ -421,6 +422,8 @@ const operationBtnList = ref([
// 加载表格数据
const loadTableData = async (searchParams = {}) => {
loading.value = true
// console.log('searchFormRef.value', searchFormRef.value)
// const searchParams = searchFormRef.value.getFormData() || {}
try {
const params = {
pageNo: currentPage.value,
......@@ -443,11 +446,13 @@ loadTableData()
// 分页事件
const handleSizeChange = val => {
pageSize.value = val
loadTableData()
const params = searchFormRef.value.getFormData()
loadTableData(params)
}
const handleCurrentChange = val => {
currentPage.value = val
loadTableData()
const params = searchFormRef.value.getFormData()
loadTableData(params)
}
// 表格数据
const tableData = ref([])
......
......@@ -157,15 +157,14 @@
/>
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<div style="display: flex; gap: 8px;">
<div style="display: flex; gap: 8px">
<!-- 👇 新增:查看按钮 -->
<!-- <el-button
link
type="primary"
size="small"
@click="viewFile(scope.row)"
:disabled="!scope.row.fileUrl"
:disabled="!scope.row.fileUrl"
>
查看
</el-button> -->
......@@ -183,7 +182,6 @@
</el-button>
</template>
</el-popconfirm>
</div>
</template>
</el-table-column>
......@@ -209,14 +207,14 @@
:showClose="true"
@close="handleDialogClose"
>
<FileUploader
:tenant-biz-id="userStore.projectInfo.tenantBizId"
:project-biz-id="userStore.projectInfo.projectBizId"
:object-biz-id="props.policyBizId"
:object-type="'DOCUMENT'"
:project-type="'pc'"
:upload-concurrency="3"
ref="uploaderRef"
<FileUploader
:tenant-biz-id="userStore.projectInfo.tenantBizId"
:project-biz-id="userStore.projectInfo.projectBizId"
:object-biz-id="props.policyBizId"
:object-type="'DOCUMENT'"
:project-type="'pc'"
:upload-concurrency="3"
ref="uploaderRef"
/>
<!-- <FileUpload
v-model="files"
......@@ -239,7 +237,7 @@ import CommonDialog from '@/components/commonDialog'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Delete, Edit, Search, Share, Upload } from '@element-plus/icons-vue'
import { getPolicyfollow, getProductList } from '@/api/sign/underwritingMain'
import { uploadOssFileList,delUploadFile } from '@/api/common'
import { uploadOssFileList, delUploadFile } from '@/api/common'
import { getProcessDetail } from '@/api/sign/fna'
import { premiumReconciliationList } from '@/api/sign/policy'
import { loadDicts, getDictLabel } from '@/utils/useDict'
......@@ -247,7 +245,7 @@ import { getNowTime, formatToDate, formatToDateTime } from '@/utils/date'
import EditableTable from '@/components/csf-common/EditableTable.vue'
import useUserStore from '@/store/modules/user'
import FileUploader from '@/components/LargeFileUploader/index.vue';
import FileUploader from '@/components/LargeFileUploader/index.vue'
const { proxy } = getCurrentInstance()
proxy.useDictLists([
......@@ -289,17 +287,17 @@ const props = defineProps({
default: ''
}
})
const uploaderRef = ref(null);
const uploaderRef = ref(null)
// 【关键】当弹窗关闭时,强制清空上传组件列表
const handleDialogClose = () => {
if (uploaderRef.value) {
// 调用子组件暴露的清空方法
// 你需要在子组件中使用 defineExpose 暴露 clearList 方法
uploaderRef.value.clearList();
fileUploadDialogFlag.value = false
uploaderRef.value.clearList()
fileUploadDialogFlag.value = false
getAttachmentListDetail(props.policyBizId)
}
};
}
const emit = defineEmits(['update:modelValue', 'submit', 'cancel', 'saveRow'])
const introducerTableData = ref([])
......@@ -317,9 +315,9 @@ const introducerConfig = [
broker: 'realName', // 选中后自动填 broker = raw.realName
brokerName: 'realName',
internalCode: 'internalNumber',
team: 'deptName',
team: 'teamName',
phone: 'phone',
teamBizId:'deptBizId'
teamBizId: 'teamBizId'
},
transform: res => {
return (res?.data.records || []).map(item => ({
......@@ -541,7 +539,7 @@ const policyInfoFormConfig = ref([
type: 'select',
prop: 'professionalInvestor',
label: '专业投资者',
defaultValue :'No',
defaultValue: 'No',
options: [
{ label: '是', value: 'Yes' },
{ label: '否', value: 'No' }
......@@ -692,7 +690,8 @@ const basicPlanFormConfig = ref([
categoryCodeList: [basicPlanFormData.value.insuranceCategoryCode || ''],
insuranceCompanyBizIdList: [basicPlanFormData.value.insuranceCompanyBizId || ''],
pageNo: 1,
pageSize: 20
pageSize: 20,
productName: 'productName'
}),
placeholder: '请选择产品名称',
debounceWait: 500,
......@@ -1042,7 +1041,9 @@ const activeTab = ref('basic')
const handleBeforeLeave = async (newTabName, oldTabName) => {
// console.log('切换前确认-----------------------', newTabName, oldTabName)
// console.log(tabDirty.value)
if(props.mode === 'viewDetail'){return;}
if (props.mode === 'viewDetail') {
return
}
if (tabDirty.value[oldTabName]) {
try {
await ElMessageBox.confirm(`“${getTabLabel(oldTabName)}” 未提交,确定要切换吗?`, '提示', {
......@@ -1127,7 +1128,7 @@ const handleSubmit = () => {
activeTab: activeTab.value,
...localData.additionalPlans
})
}else if (activeTab.value === 'attachment'){
} else if (activeTab.value === 'attachment') {
tabDirty.value.attachment = false
emit('submit', {
activeTab: activeTab.value
......@@ -1141,17 +1142,16 @@ const handleCancel = () => {
emit('cancel')
}
// 删除附件
const deleteFile = (row)=>{
const deleteFile = row => {
console.log(row)
const fileBizId = row.fileBizId || '';
delUploadFile(fileBizId,userStore.projectInfo.projectBizId).then(res=>{
const fileBizId = row.fileBizId || ''
delUploadFile(fileBizId, userStore.projectInfo.projectBizId).then(res => {
console.log(res)
if(res.code===200){
if (res.code === 200) {
getAttachmentListDetail(props.policyBizId)
}else{
} else {
ElMessage.error('删除文件失败')
}
})
}
......
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