Commit 2b3989c6 by yuzhenWang

按需求修改

parent c5b88c5f
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -273,7 +273,7 @@ ...@@ -273,7 +273,7 @@
</CommonDialog> </CommonDialog>
<!-- 设置出账年月弹窗 --> <!-- 设置出账年月弹窗 -->
<CommonDialog <CommonDialog
dialogTitle="设置本期出账金额" dialogTitle="设置本期出账年月(实)"
dialogWidth="80%" dialogWidth="80%"
:openDialog="settingBillYearMonthFlag" :openDialog="settingBillYearMonthFlag"
:showAction="true" :showAction="true"
...@@ -452,7 +452,7 @@ const editTableRef = ref(null) ...@@ -452,7 +452,7 @@ const editTableRef = ref(null)
const splitTableColumns = ref([ const splitTableColumns = ref([
{ {
prop: 'splitRatio', prop: 'splitRatio',
label: '出账比例', label: '出账比例(%)',
editType: 'input', editType: 'input',
inputType: 'decimal', // integer / decimalNumber / decimal inputType: 'decimal', // integer / decimalNumber / decimal
decimalDigits: 2, // 小数位数,默认2 decimalDigits: 2, // 小数位数,默认2
...@@ -542,38 +542,7 @@ const splitTableColumns = ref([ ...@@ -542,38 +542,7 @@ const splitTableColumns = ref([
required: true, required: true,
width: 150 width: 150
}, },
// {
// prop: 'amount',
// label: '原币种金额',
// editType: 'input',
// inputType: 'decimalNumber', // integer / decimalNumber / decimal
// decimalDigits: 2, // 小数位数,默认2
// required: true
// },
// {
// prop: 'originalAmount',
// label: '本次出账原币种金额',
// editType: 'input',
// inputType: 'decimalNumber', // integer / decimalNumber / decimal
// decimalDigits: 2, // 小数位数,默认2
// required: true
// },
// {
// prop: 'exchangeRate',
// label: '结算汇率',
// editType: 'input',
// inputType: 'decimal', // integer / decimalNumber / decimal
// decimalDigits: 2, // 小数位数,默认2
// required: true
// },
// {
// prop: 'hkdAmount',
// label: '港币出账金额',
// editType: 'input',
// inputType: 'decimalNumber', // integer / decimalNumber / decimal
// decimalDigits: 2, // 小数位数,默认2
// required: true
// },
{ {
prop: 'payoutYearMonth', prop: 'payoutYearMonth',
label: '出账年月(估)', label: '出账年月(估)',
...@@ -710,7 +679,8 @@ const confirmRateExchange = async () => { ...@@ -710,7 +679,8 @@ const confirmRateExchange = async () => {
const res = await editExchangeRateApi(formData) const res = await editExchangeRateApi(formData)
ElMessage.success('结算汇率修改成功') ElMessage.success('结算汇率修改成功')
rateExchangeFlag.value = false rateExchangeFlag.value = false
loadTableData() const params = searchFormRef.value.getFormData()
loadTableData(params)
} catch (error) { } catch (error) {
ElMessage.error('结算汇率修改失败') ElMessage.error('结算汇率修改失败')
rateExchangeFlag.value = true rateExchangeFlag.value = true
...@@ -1006,7 +976,8 @@ const handleSpiltSubmit = async () => { ...@@ -1006,7 +976,8 @@ const handleSpiltSubmit = async () => {
console.log('分期出账结果', res) console.log('分期出账结果', res)
installmentsBillFlag.value = false installmentsBillFlag.value = false
ElMessage.success('分期出账已保存') ElMessage.success('分期出账已保存')
loadTableData() const searchParams = searchFormRef.value.getFormData()
loadTableData(searchParams)
} catch (error) { } catch (error) {
installmentsBillFlag.value = true installmentsBillFlag.value = true
console.log('分期出账错误', error) console.log('分期出账错误', error)
...@@ -1039,9 +1010,18 @@ const addSpiltRecord = () => { ...@@ -1039,9 +1010,18 @@ const addSpiltRecord = () => {
id: generateId(), // 必须有 rowIdKey 对应的字段(默认 'id') id: generateId(), // 必须有 rowIdKey 对应的字段(默认 'id')
payoutCurrency: 'HKD', payoutCurrency: 'HKD',
ruleCurrency: selectedRow.value.ruleCurrency, ruleCurrency: selectedRow.value.ruleCurrency,
originalCurrency: selectedRow.value.originalCurrency originalCurrency: selectedRow.value.originalCurrency,
hkdToPayoutRate: '1'
} }
if (selectedRow.value.originalCurrency && selectedRow.value.originalCurrency == 'HKD') {
newRow.originalToHkdRate = '1'
}
// if (selectedRow.value.payoutCurrency && selectedRow.value.payoutCurrency == 'HKD') {
// newRow.hkdToPayoutRate = '1'
// }
console.log('====================================')
console.log('newRow', newRow)
console.log('====================================')
// 插入到表格数据最前面 // 插入到表格数据最前面
splitTableData.value.push(newRow) splitTableData.value.push(newRow)
} }
...@@ -1380,11 +1360,13 @@ loadTableData() ...@@ -1380,11 +1360,13 @@ loadTableData()
// 分页事件 // 分页事件
const handleSizeChange = val => { const handleSizeChange = val => {
pageSize.value = val pageSize.value = val
loadTableData() const params = searchFormRef.value.getFormData()
loadTableData(params)
} }
const handleCurrentChange = val => { const handleCurrentChange = val => {
currentPage.value = val currentPage.value = val
loadTableData() const params = searchFormRef.value.getFormData()
loadTableData(params)
} }
// 表格数据 // 表格数据
const tableData = ref([]) const tableData = ref([])
...@@ -1407,15 +1389,13 @@ const handleSelect = (e, row) => { ...@@ -1407,15 +1389,13 @@ const handleSelect = (e, row) => {
ruleCurrency: row.ruleCurrency, ruleCurrency: row.ruleCurrency,
originalCurrency: row.originalCurrency, originalCurrency: row.originalCurrency,
payoutCurrency: row.payoutCurrency, payoutCurrency: row.payoutCurrency,
hkdToPayoutRate: row.hkdToPayoutRate ? row.hkdToPayoutRate : '', hkdToPayoutRate: '',
exchangeRate: row.exchangeRate ? row.exchangeRate : '', exchangeRate: row.exchangeRate ? row.exchangeRate : '',
originalToHkdRate: row.originalToHkdRate ? row.originalToHkdRate : '', originalToHkdRate: '',
payoutAmount: row.payoutAmount ? Number(row.payoutAmount).toFixed(2) : '', payoutAmount: row.payoutAmount ? Number(row.payoutAmount).toFixed(2) : '',
ruleAmount: row.ruleAmount ? Number(row.ruleAmount).toFixed(2) : '', ruleAmount: row.ruleAmount ? Number(row.ruleAmount).toFixed(2) : '',
hkdAmount: row.hkdAmount ? Number(row.hkdAmount).toFixed(2) : '' hkdAmount: row.hkdAmount ? Number(row.hkdAmount).toFixed(2) : ''
} }
rateExchangeFlag.value = true rateExchangeFlag.value = true
} }
} }
...@@ -1465,6 +1445,7 @@ const updatePayoutAmountapi = async data => { ...@@ -1465,6 +1445,7 @@ const updatePayoutAmountapi = async data => {
const updatePayRollStatusDisable = ref(true) const updatePayRollStatusDisable = ref(true)
const multipleSelection = ref([]) const multipleSelection = ref([])
const handleSelectionChange = val => { const handleSelectionChange = val => {
multipleSelection.value = val multipleSelection.value = val
console.log('全选:', val) console.log('全选:', val)
// 完成检核按钮是否禁用 // 完成检核按钮是否禁用
...@@ -1481,7 +1462,8 @@ const submitSettingBillYearMonth = async () => { ...@@ -1481,7 +1462,8 @@ const submitSettingBillYearMonth = async () => {
if (res.code === 200) { if (res.code === 200) {
settingBillYearMonthFlag.value = false settingBillYearMonthFlag.value = false
ElMessage.success('完成检核,等待关账') ElMessage.success('完成检核,等待关账')
loadTableData() const params = searchFormRef.value.getFormData()
loadTableData(params)
} }
} catch (error) { } catch (error) {
console.error('检核失败:', error) console.error('检核失败:', error)
......
...@@ -976,15 +976,135 @@ const updatePayRecordFormConfig = [ ...@@ -976,15 +976,135 @@ const updatePayRecordFormConfig = [
// label: '备注' // label: '备注'
// } // }
] ]
// const addPayRecordFormConfig = [
// {
// type: 'select',
// prop: 'fortuneBizType',
// label: '应付单类型',
// options: [
// { value: 'R', label: '关联保单应付单' },
// { value: 'U', label: '非关联保单应付单' }
// ]
// },
// {
// type: 'input',
// prop: 'policyNo',
// label: '关联保单号',
// visible: formData => formData.fortuneBizType === 'R'
// },
// {
// type: 'input',
// prop: 'fortunePeriod',
// label: '佣金期数',
// inputType: 'decimal',
// visible: formData => formData.fortuneBizType === 'R',
// rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
// },
// {
// type: 'input',
// prop: 'fortuneTotalPeriod',
// label: '总期数',
// inputType: 'decimal',
// visible: formData => formData.fortuneBizType === 'R',
// rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
// },
// {
// type: 'date',
// prop: 'payoutDate',
// label: '出账日(估)',
// placeholder: '请选择'
// },
// {
// type: 'date',
// prop: 'actualPayoutDate',
// label: '出账日(实)',
// placeholder: '请选择',
// maxDate: 'today'
// },
// {
// type: 'input',
// prop: 'hkdAmount',
// label: '出账金额',
// rules: [
// { required: true, message: '请输入', trigger: 'blur' },
// { pattern: /^-?\d+(\.\d{1,2})?$/, message: '小数(最多两位)', trigger: 'blur' }
// ]
// },
// {
// type: 'select',
// prop: 'currency',
// label: '出账币种',
// dictType: 'bx_currency_type',
// defaultValue: 'HKD'
// },
// {
// type: 'input',
// prop: 'defaultExchangeRate',
// label: '结算汇率(入账检核时的汇率)',
// rules: [
// { required: true, message: '请输入', trigger: 'blur' },
// { pattern: /^-?\d+(\.\d{1,6})?$/, message: '小数(最多6位)', trigger: 'blur' }
// ]
// },
// {
// type: 'select',
// prop: 'fortuneType',
// label: '出账项目',
// dictType: 'csf_fortune_type'
// },
// {
// type: 'select',
// prop: 'brokerBizId',
// label: '转介人',
// api: '/insurance/base/api/userSaleExpand/page',
// keywordField: 'realName',
// requestParams: { pageNo: 1, pageSize: 200 },
// placeholder: '输入转介人名称搜索',
// debounceWait: 500, // 自定义防抖时间
// valueKey: 'clientUserBizId',
// labelKey: 'realName',
// onChangeExtraFields: {
// broker: 'realName', // 自动同步 raw.name 到 reconciliationCompany
// reconciliationCompanyCode: 'code'
// },
// transform: res => {
// return res?.data.records || []
// }
// },
// {
// type: 'select',
// prop: 'status',
// label: '出账状态',
// dictType: 'csf_expected_fortune_status'
// },
// {
// type: 'input',
// prop: 'exchangeRate',
// label: '结算汇率',
// inputType: 'decimal',
// rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
// // defaultValue: 1
// },
// {
// type: 'input',
// prop: 'remark',
// label: '备注'
// }
// ]
const addPayRecordFormConfig = [ const addPayRecordFormConfig = [
{ {
type: 'select', type: 'select',
prop: 'fortuneBizType', prop: 'fortuneBizType',
label: '应付单类型', label: '应付单类型',
options: [ options: fortuneBizTypeOptions,
{ value: 'R', label: '关联保单应付单' }, rules: [{ required: true, message: '应付单类型必填', trigger: 'blur' }]
{ value: 'U', label: '非关联保单应付单' } },
] {
type: 'select',
prop: 'status',
label: '出账状态',
dictType: 'csf_expected_fortune_status',
rules: [{ required: true, message: '出账状态必填', trigger: 'blur' }]
}, },
{ {
type: 'input', type: 'input',
...@@ -993,64 +1113,54 @@ const addPayRecordFormConfig = [ ...@@ -993,64 +1113,54 @@ const addPayRecordFormConfig = [
visible: formData => formData.fortuneBizType === 'R' visible: formData => formData.fortuneBizType === 'R'
}, },
{ {
type: 'input', type: 'month',
prop: 'fortunePeriod',
label: '佣金期数',
inputType: 'decimal',
visible: formData => formData.fortuneBizType === 'R',
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
{
type: 'input',
prop: 'fortuneTotalPeriod',
label: '总期数',
inputType: 'decimal',
visible: formData => formData.fortuneBizType === 'R',
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
{
type: 'date',
prop: 'payoutDate', prop: 'payoutDate',
label: '出账日(估)', label: '出账月(估)',
placeholder: '请选择' placeholder: '请选择',
maxDate: 'today',
rules: [{ required: true, message: '出账月(估)必填', trigger: 'blur' }]
}, },
{ {
type: 'date', type: 'month',
prop: 'actualPayoutDate', prop: 'actualPayoutDate',
label: '出账日(实)', label: '出账月(实)',
placeholder: '请选择', placeholder: '请选择',
maxDate: 'today' maxDate: 'today',
rules: [{ required: true, message: '出账月(实)必填', trigger: 'blur' }]
}, },
{ {
type: 'input', type: 'input',
prop: 'hkdAmount', prop: 'statusDesc',
label: '出账金额', label: '修改理由'
rules: [ },
{ required: true, message: '请输入', trigger: 'blur' }, {
{ pattern: /^-?\d+(\.\d{1,2})?$/, message: '小数(最多两位)', trigger: 'blur' } type: 'input',
] prop: 'fortuneName',
label: '出账项目名称',
rules: [{ required: true, message: '出账项目名称必填', trigger: 'blur' }]
}, },
{ {
type: 'select', type: 'select',
prop: 'currency', prop: 'fortuneType',
label: '出账币种', label: '出账项目类型',
dictType: 'bx_currency_type', dictType: 'csf_fortune_type',
defaultValue: 'HKD' rules: [{ required: true, message: '出账项目类型必填', trigger: 'blur' }]
}, },
{ {
type: 'input', type: 'input',
prop: 'defaultExchangeRate', prop: 'fortunePeriod',
label: '结算汇率(入账检核时的汇率)', label: '佣金期数',
rules: [ inputType: 'decimal',
{ required: true, message: '请输入', trigger: 'blur' }, visible: formData => formData.fortuneBizType === 'R',
{ pattern: /^-?\d+(\.\d{1,6})?$/, message: '小数(最多6位)', trigger: 'blur' } rules: [{ required: true, pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
]
}, },
{ {
type: 'select', type: 'input',
prop: 'fortuneType', prop: 'fortuneTotalPeriod',
label: '出账项目', label: '总期数',
dictType: 'csf_fortune_type' inputType: 'decimal',
visible: formData => formData.fortuneBizType === 'R',
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
}, },
{ {
type: 'select', type: 'select',
...@@ -1066,32 +1176,125 @@ const addPayRecordFormConfig = [ ...@@ -1066,32 +1176,125 @@ const addPayRecordFormConfig = [
onChangeExtraFields: { onChangeExtraFields: {
broker: 'realName', // 自动同步 raw.name 到 reconciliationCompany broker: 'realName', // 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode: 'code' reconciliationCompanyCode: 'code'
// team: 'deptName',
// teamBizId: 'deptBizId'
},
transform: res => {
return res?.data.records || []
},
rules: [{ required: true, message: '转介人必填', trigger: 'blur' }]
},
{
type: 'select',
prop: 'teamBizId',
label: '所属团队',
api: '/csf/api/team/page',
keywordField: 'teamName',
requestParams: { pageNo: 1, pageSize: 200 },
placeholder: '输入所属团队名称搜索',
debounceWait: 500, // 自定义防抖时间
valueKey: 'teamBizId',
labelKey: 'teamName',
onChangeExtraFields: {
// broker: 'realName', // 自动同步 raw.name 到 reconciliationCompany
// reconciliationCompanyCode: 'code',
team: 'teamName',
teamBizId: 'teamBizId'
}, },
transform: res => { transform: res => {
return res?.data.records || [] return res?.data.records || []
} }
// rules: [{ required: true, message: '所属团队必填', trigger: 'blur' }]
}, },
// {
// type: 'input',
// prop: 'defaultExchangeRate',
// label: '结算汇率(入账检核时的汇率)',
// rules: [
// { required: true, message: '请输入', trigger: 'blur' },
// { pattern: /^-?\d+(\.\d{1,6})?$/, message: '小数(最多6位)', trigger: 'blur' }
// ]
// },
{ {
type: 'select', type: 'select',
prop: 'status', prop: 'ruleCurrency',
label: '出账状态', label: '保单币种',
dictType: 'csf_expected_fortune_status' dictType: 'bx_currency_type',
rules: [{ required: true, message: '保单币种必填', trigger: 'blur' }]
},
{
type: 'select',
prop: 'originalCurrency',
label: '原币种',
dictType: 'bx_currency_type',
rules: [{ required: true, message: '原币种必填', trigger: 'blur' }]
},
{
type: 'select',
prop: 'payoutCurrency',
label: '发放币种',
dictType: 'bx_currency_type',
rules: [{ required: true, message: '发放币种必填', trigger: 'blur' }]
}, },
{ {
type: 'input', type: 'input',
prop: 'exchangeRate', prop: 'originalAmount',
label: '结算汇率', label: '原币种金额',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
},
{
type: 'input',
prop: 'originalToHkdRate',
label: '汇率3(原币种->港币)',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
// defaultValue: 1
},
{
type: 'input',
prop: 'hkdAmount',
label: '港币金额',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
},
{
type: 'input',
prop: 'hkdToPayoutRate',
label: '汇率1(港币->发放币种)',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
// defaultValue: 1
},
{
type: 'input',
prop: 'payoutAmount',
label: '实际发放金额',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
},
{
type: 'input',
prop: 'defaultExchangeRate',
label: '汇率2(保单币种->港币)入账检核汇率',
inputType: 'decimal', inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }] rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
// defaultValue: 1 // defaultValue: 1
}, },
{ {
type: 'input', type: 'input',
prop: 'ruleAmount',
label: '保单币种金额',
inputType: 'decimal',
rules: [{ required: true, message: '只能输入正整数和小数', trigger: 'blur' }]
},
{
type: 'input',
prop: 'remark', prop: 'remark',
label: '备注' label: '备注'
} }
] ]
//是否实收 //是否实收
const typeOptions = [ const typeOptions = [
{ value: 1, label: '预计' }, { value: 1, label: '预计' },
...@@ -1121,7 +1324,7 @@ const handleInputChange = async (formType, prop, value, item) => { ...@@ -1121,7 +1324,7 @@ const handleInputChange = async (formType, prop, value, item) => {
commissionPeriod: fortunePeriod commissionPeriod: fortunePeriod
}) })
if (res.code == 200) { if (res.code == 200) {
addPayRecordFormModel.value.exchangeRate = res.data addPayRecordFormModel.value.defaultExchangeRate = res.data
} else { } else {
ElMessage.error('查询结算汇率失败') ElMessage.error('查询结算汇率失败')
} }
...@@ -1141,9 +1344,9 @@ const handleInputChange = async (formType, prop, value, item) => { ...@@ -1141,9 +1344,9 @@ const handleInputChange = async (formType, prop, value, item) => {
await nextTick() await nextTick()
addPayRecordFormModel.value.payoutAmount = payoutAmount addPayRecordFormModel.value.payoutAmount = payoutAmount
} }
const exchangeRate = addPayRecordFormModel.value.exchangeRate const defaultExchangeRate = addPayRecordFormModel.value.defaultExchangeRate
if (exchangeRate) { if (defaultExchangeRate) {
const ruleAmount = calculateAmount(hkdAmount, exchangeRate, 2, 'Divided') const ruleAmount = calculateAmount(hkdAmount, defaultExchangeRate, 2, 'Divided')
await nextTick() await nextTick()
addPayRecordFormModel.value.ruleAmount = ruleAmount addPayRecordFormModel.value.ruleAmount = ruleAmount
} }
...@@ -1165,9 +1368,9 @@ const handleInputChange = async (formType, prop, value, item) => { ...@@ -1165,9 +1368,9 @@ const handleInputChange = async (formType, prop, value, item) => {
await nextTick() await nextTick()
addPayRecordFormModel.value.payoutAmount = payoutAmount addPayRecordFormModel.value.payoutAmount = payoutAmount
} }
const exchangeRate = addPayRecordFormModel.value.exchangeRate const defaultExchangeRate = addPayRecordFormModel.value.defaultExchangeRate
if (exchangeRate) { if (defaultExchangeRate) {
const ruleAmount = calculateAmount(hkdAmount, exchangeRate, 2, 'Divided') const ruleAmount = calculateAmount(hkdAmount, defaultExchangeRate, 2, 'Divided')
await nextTick() await nextTick()
addPayRecordFormModel.value.ruleAmount = ruleAmount addPayRecordFormModel.value.ruleAmount = ruleAmount
} }
...@@ -1190,18 +1393,18 @@ const handleInputChange = async (formType, prop, value, item) => { ...@@ -1190,18 +1393,18 @@ const handleInputChange = async (formType, prop, value, item) => {
} }
//3.计算保单币种金额 港币金额*保单币种->港币汇率 //3.计算保单币种金额 港币金额*保单币种->港币汇率
if (prop == 'hkdAmount' && value && addPayRecordFormModel.value.exchangeRate) { if (prop == 'hkdAmount' && value && addPayRecordFormModel.value.defaultExchangeRate) {
// 计算保单币种金额 // 计算保单币种金额
const exchangeRate = addPayRecordFormModel.value.exchangeRate const defaultExchangeRate = addPayRecordFormModel.value.defaultExchangeRate
const hkdAmount = value const hkdAmount = value
const ruleAmount = calculateAmount(hkdAmount, exchangeRate, 2, 'Divided') const ruleAmount = calculateAmount(hkdAmount, defaultExchangeRate, 2, 'Divided')
await nextTick() await nextTick()
addPayRecordFormModel.value.ruleAmount = ruleAmount addPayRecordFormModel.value.ruleAmount = ruleAmount
} else if (prop == 'exchangeRate' && value && addPayRecordFormModel.value.hkdAmount) { } else if (prop == 'defaultExchangeRate' && value && addPayRecordFormModel.value.hkdAmount) {
// 计算保单币种金额 // 计算保单币种金额
const exchangeRate = value const defaultExchangeRate = value
const hkdAmount = addPayRecordFormModel.value.hkdAmount const hkdAmount = addPayRecordFormModel.value.hkdAmount
const ruleAmount = calculateAmount(hkdAmount, exchangeRate, 2, 'Divided') const ruleAmount = calculateAmount(hkdAmount, defaultExchangeRate, 2, 'Divided')
await nextTick() await nextTick()
addPayRecordFormModel.value.ruleAmount = ruleAmount addPayRecordFormModel.value.ruleAmount = ruleAmount
} }
......
...@@ -376,10 +376,9 @@ const addReceivablesFormConfig = [ ...@@ -376,10 +376,9 @@ const addReceivablesFormConfig = [
visible: formData => formData.commissionBizType === 'R', visible: formData => formData.commissionBizType === 'R',
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }] rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
}, },
// 等待key
{ {
type: 'month', type: 'month',
prop: 'commissionDateMonth', prop: 'commissionDate',
label: '入账月(估)', label: '入账月(估)',
placeholder: '请选择' placeholder: '请选择'
}, },
......
...@@ -181,7 +181,7 @@ ...@@ -181,7 +181,7 @@
<el-dialog <el-dialog
v-model="previewDialogVisible" v-model="previewDialogVisible"
:title="previewFileName" :title="previewFileName"
width="80%" width="90%"
:close-on-click-modal="false" :close-on-click-modal="false"
destroy-on-close destroy-on-close
@close="closePreview" @close="closePreview"
...@@ -191,19 +191,10 @@ ...@@ -191,19 +191,10 @@
<div v-if="previewFileType === 'image'" class="preview-image-wrapper"> <div v-if="previewFileType === 'image'" class="preview-image-wrapper">
<img :src="previewUrl" class="preview-image" alt="预览图片" /> <img :src="previewUrl" class="preview-image" alt="预览图片" />
</div> </div>
<!-- PDF 预览区域(滚动多页) -->
<!-- PDF 预览(pdf.js 插件) -->
<div v-else-if="previewFileType === 'pdf'" class="pdf-viewer"> <div v-else-if="previewFileType === 'pdf'" class="pdf-viewer">
<div class="pdf-toolbar"> <div class="pdf-toolbar">
<el-button-group> <!-- 只保留缩放按钮 -->
<el-button size="small" :disabled="pdfCurrentPage <= 1" @click="prevPage">
<el-icon><ArrowLeft /></el-icon> 上一页
</el-button>
<el-button size="small" :disabled="pdfCurrentPage >= pdfTotalPages" @click="nextPage">
下一页 <el-icon><ArrowRight /></el-icon>
</el-button>
</el-button-group>
<span class="page-info"> 第 {{ pdfCurrentPage }} / {{ pdfTotalPages }} 页 </span>
<el-button-group> <el-button-group>
<el-button size="small" @click="zoomOut"> <el-button size="small" @click="zoomOut">
<el-icon><ZoomOut /></el-icon> 缩小 <el-icon><ZoomOut /></el-icon> 缩小
...@@ -212,12 +203,16 @@ ...@@ -212,12 +203,16 @@
<el-icon><ZoomIn /></el-icon> 放大 <el-icon><ZoomIn /></el-icon> 放大
</el-button> </el-button>
</el-button-group> </el-button-group>
<span class="page-info">共 {{ pdfTotalPages }} 页</span>
</div> </div>
<div class="pdf-canvas-wrapper"> <div
<canvas ref="pdfCanvasRef" class="pdf-canvas"></canvas> class="pdf-scroll-wrapper"
v-loading="pdfLoading"
element-loading-text="正在渲染页面..."
>
<div ref="pdfScrollContainer" class="pdf-scroll-container"></div>
</div> </div>
</div> </div>
<!-- 不支持预览的文件类型 --> <!-- 不支持预览的文件类型 -->
<div v-else-if="previewFileType === 'unsupported'" class="preview-unsupported"> <div v-else-if="previewFileType === 'unsupported'" class="preview-unsupported">
<el-icon :size="48" color="#909399"><Document /></el-icon> <el-icon :size="48" color="#909399"><Document /></el-icon>
...@@ -235,7 +230,7 @@ ...@@ -235,7 +230,7 @@
</template> </template>
<script setup name="FileUpload"> <script setup name="FileUpload">
import { ref, nextTick } from 'vue' import { ref, nextTick, shallowRef } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { downloadFilesAsZip } from '@/utils/zipDownload' // 引入刚才封装的工具 import { downloadFilesAsZip } from '@/utils/zipDownload' // 引入刚才封装的工具
import CommonDialog from '@/components/commonDialog' import CommonDialog from '@/components/commonDialog'
...@@ -244,7 +239,7 @@ import { getToken } from '@/utils/auth' ...@@ -244,7 +239,7 @@ import { getToken } from '@/utils/auth'
import { addFile, getAppointmentFile, delFile, editAppointmentFile } from '@/api/sign/appointment' import { addFile, getAppointmentFile, delFile, editAppointmentFile } from '@/api/sign/appointment'
import useDictStore from '@/store/modules/dict' import useDictStore from '@/store/modules/dict'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { ArrowLeft, ArrowRight, ZoomIn, ZoomOut, Document } from '@element-plus/icons-vue' import { ArrowLeft, ArrowRight, ZoomIn, ZoomOut, Document, Loading } from '@element-plus/icons-vue'
const userStore = useUserStore() const userStore = useUserStore()
import { import {
uploadMaterialList, uploadMaterialList,
...@@ -257,16 +252,14 @@ import { ...@@ -257,16 +252,14 @@ import {
} from '@/api/common' } from '@/api/common'
import * as PDFJS from 'pdfjs-dist' import * as PDFJS from 'pdfjs-dist'
// 设置 worker 路径(重要!) PDFJS.GlobalWorkerOptions.workerSrc = '/js/pdf.worker.min.mjs'
PDFJS.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.js',
import.meta.url
).toString()
const props = defineProps({ const props = defineProps({
activeName: { type: String, default: '' }, //tab名称 activeName: { type: String, default: '' }, //tab名称
idsObj: { type: Object, default: () => ({}) }, //父组件传递过来的id对象 idsObj: { type: Object, default: () => ({}) }, //父组件传递过来的id对象
pageSource: { type: String, default: '' } //页面来源 pageSource: { type: String, default: '' } //页面来源
}) })
// 在定义其他响应式变量的附近添加
const pdfLoading = ref(false) // PDF 加载状态
const acceptUploadType = ref('.pdf,.jpg,.jpeg,.png,.bmp,.gif,.svg') const acceptUploadType = ref('.pdf,.jpg,.jpeg,.png,.bmp,.gif,.svg')
const uploadRef = ref(null) const uploadRef = ref(null)
const dictStore = useDictStore() //获取字典数据 const dictStore = useDictStore() //获取字典数据
...@@ -281,17 +274,63 @@ const headers = ref({ Authorization: 'Bearer ' + getToken() }) ...@@ -281,17 +274,63 @@ const headers = ref({ Authorization: 'Bearer ' + getToken() })
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + '/oss/api/oss/upload') // 上传的服务器地址 const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + '/oss/api/oss/upload') // 上传的服务器地址
// PDF 预览相关 // PDF 预览相关
const pdfCanvasRef = ref(null) // canvas 元素引用 const pdfCanvasRef = ref(null) // canvas 元素引用
const pdfDoc = ref(null) // pdf 文档实例 // 修改 pdfDoc 的定义
const pdfCurrentPage = ref(1) // 当前页 const pdfDoc = shallowRef(null) // pdf 文档实例
const pdfTotalPages = ref(0) // 总页数
const pdfScale = ref(1.2) // 缩放比例 const pdfScale = ref(1.2) // 缩放比例
// 新增标志:是否取消PDF加载
let pdfLoadingCanceled = false
// ==================== 文件预览弹窗 ====================
const previewDialogVisible = ref(false)
const previewUrl = ref('')
const previewFileName = ref('')
const previewFileType = ref('') // 'image', 'pdf', 'unsupported'
const pdfScrollContainer = ref(null) // 滚动容器的 ref
const pdfTotalPages = ref(0) // 总页数(仅供显示)
const isRendering = ref(false) // 防止重复渲染
const loadPdf = async url => {
pdfLoadingCanceled = false
pdfLoading.value = true
try {
if (pdfDoc.value) {
await pdfDoc.value.destroy().catch(() => {})
pdfDoc.value = null
}
// 清空滚动容器
if (pdfScrollContainer.value) {
pdfScrollContainer.value.innerHTML = ''
}
const loadingTask = PDFJS.getDocument(url)
pdfDoc.value = await loadingTask.promise
if (pdfLoadingCanceled) {
if (pdfDoc.value) pdfDoc.value.destroy()
pdfLoading.value = false
return
}
pdfTotalPages.value = pdfDoc.value.numPages
await renderAllPages(pdfScale.value) // 渲染所有页面
pdfLoading.value = false
} catch (err) {
if (!pdfLoadingCanceled) {
console.error('PDF 加载失败', err)
ElMessage.error('PDF 文件加载失败,请检查文件链接')
previewDialogVisible.value = false
}
pdfLoading.value = false
}
}
// 渲染指定页 // 修改 renderPdfPage,增加有效性检查
const renderPdfPage = async pageNum => { const renderPdfPage = async pageNum => {
if (!pdfDoc.value) return if (!pdfDoc.value || pdfLoadingCanceled) return
try {
const page = await pdfDoc.value.getPage(pageNum) const page = await pdfDoc.value.getPage(pageNum)
const viewport = page.getViewport({ scale: pdfScale.value }) const viewport = page.getViewport({ scale: pdfScale.value })
const canvas = pdfCanvasRef.value const canvas = pdfCanvasRef.value
if (!canvas) return
const context = canvas.getContext('2d') const context = canvas.getContext('2d')
canvas.height = viewport.height canvas.height = viewport.height
canvas.width = viewport.width canvas.width = viewport.width
...@@ -301,108 +340,67 @@ const renderPdfPage = async pageNum => { ...@@ -301,108 +340,67 @@ const renderPdfPage = async pageNum => {
viewport: viewport viewport: viewport
} }
await page.render(renderContext).promise await page.render(renderContext).promise
}
// 加载并渲染 PDF
const loadPdf = async url => {
try {
// 重置状态
pdfDoc.value = null
pdfCurrentPage.value = 1
pdfTotalPages.value = 0
const loadingTask = PDFJS.getDocument(url)
pdfDoc.value = await loadingTask.promise
pdfTotalPages.value = pdfDoc.value.numPages
await renderPdfPage(pdfCurrentPage.value)
} catch (err) { } catch (err) {
console.error('PDF 加载失败', err) if (!pdfLoadingCanceled) {
ElMessage.error('PDF 文件加载失败,请检查文件链接') console.error('渲染PDF页失败', err)
previewDialogVisible.value = false
} }
}
// 上一页
const prevPage = () => {
if (pdfCurrentPage.value > 1) {
pdfCurrentPage.value--
renderPdfPage(pdfCurrentPage.value)
} }
} }
const renderAllPages = async scale => {
if (!pdfDoc.value || pdfLoadingCanceled || isRendering.value) return
isRendering.value = true
pdfLoading.value = true // 显示加载状态
// 下一页 const container = pdfScrollContainer.value
const nextPage = () => { if (!container) {
if (pdfCurrentPage.value < pdfTotalPages.value) { isRendering.value = false
pdfCurrentPage.value++ return
renderPdfPage(pdfCurrentPage.value)
} }
} // 清空之前的 canvas
container.innerHTML = ''
// 放大/缩小 try {
const zoomIn = () => { const promises = []
pdfScale.value = Math.min(pdfScale.value + 0.2, 3.0) for (let pageNum = 1; pageNum <= pdfTotalPages.value; pageNum++) {
renderPdfPage(pdfCurrentPage.value) if (pdfLoadingCanceled) break
} const page = await pdfDoc.value.getPage(pageNum)
const zoomOut = () => { const viewport = page.getViewport({ scale })
pdfScale.value = Math.max(pdfScale.value - 0.2, 0.5)
renderPdfPage(pdfCurrentPage.value) // 创建 canvas 元素
} const canvas = document.createElement('canvas')
const closePreview = () => { canvas.className = 'pdf-page-canvas'
// 清理 PDF 资源 const context = canvas.getContext('2d')
if (pdfDoc.value) { canvas.height = viewport.height
pdfDoc.value.destroy() canvas.width = viewport.width
pdfDoc.value = null // 添加一些底部间距,便于区分页面
canvas.style.marginBottom = '16px'
canvas.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)'
container.appendChild(canvas)
// 渲染该页
const renderTask = page.render({
canvasContext: context,
viewport: viewport
})
promises.push(renderTask.promise)
}
await Promise.all(promises)
} catch (err) {
if (!pdfLoadingCanceled) {
console.error('渲染多页 PDF 失败', err)
ElMessage.error('渲染 PDF 页面失败')
}
} finally {
isRendering.value = false
pdfLoading.value = false
} }
pdfTotalPages.value = 0
pdfCurrentPage.value = 1
pdfScale.value = 1.0
previewUrl.value = ''
} }
// ==================== 文件预览弹窗 ====================
const previewDialogVisible = ref(false)
const previewUrl = ref('')
const previewFileName = ref('')
const previewFileType = ref('') // 'image', 'pdf', 'unsupported'
// 预览文件(页面内弹窗,不打开新窗口) // 修改 previewFile 中的 PDF 分支
// function previewFile(file) {
// console.log('====================================')
// console.log('file', file)
// console.log('====================================')
// // const url = file.url || file.fileUrl
// let url = ''
// if (file.fileKey) {
// // url = `https://files.csf.hk/${file.fileKey}`
// url = `https://csf-hk.oss-cn-hongkong.aliyuncs.com/PC/test/pdf/2026/05/13/a482331b118142d782ac6ba7697eae15.pdf`
// } else {
// url = file.url
// }
// if (!url) {
// ElMessage.warning('文件地址不存在')
// return
// }
// const ext = (file.originalName || '').split('.').pop().toLowerCase()
// previewUrl.value = url
// previewFileName.value = file.originalName || '文件'
// if (['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'svg'].includes(ext)) {
// previewFileType.value = 'image'
// previewDialogVisible.value = true
// } else if (ext === 'pdf') {
// previewFileType.value = 'pdf'
// previewDialogVisible.value = true
// } else {
// // 不支持预览的文件类型,弹窗显示提示
// previewFileType.value = 'unsupported'
// previewDialogVisible.value = true
// }
// console.log('====================================')
// console.log('previewUrl.value', previewUrl.value)
// console.log('previewFileName.value', previewFileName.value)
// console.log('previewFileType.value', previewFileType.value)
// console.log('====================================')
// }
function previewFile(file) { function previewFile(file) {
console.log('====================================')
console.log('file', file)
console.log('====================================')
const url = file.url || file.fileUrl const url = file.url || file.fileUrl
if (!url) { if (!url) {
ElMessage.warning('文件地址不存在') ElMessage.warning('文件地址不存在')
...@@ -418,7 +416,8 @@ function previewFile(file) { ...@@ -418,7 +416,8 @@ function previewFile(file) {
} else if (ext === 'pdf') { } else if (ext === 'pdf') {
previewFileType.value = 'pdf' previewFileType.value = 'pdf'
previewDialogVisible.value = true previewDialogVisible.value = true
// 确保 DOM 更新后再加载 PDF // 先清理旧的资源
closePreview()
nextTick(() => { nextTick(() => {
loadPdf(previewUrl.value) loadPdf(previewUrl.value)
}) })
...@@ -427,6 +426,29 @@ function previewFile(file) { ...@@ -427,6 +426,29 @@ function previewFile(file) {
previewDialogVisible.value = true previewDialogVisible.value = true
} }
} }
const closePreview = () => {
pdfLoadingCanceled = true // 取消任何进行中的渲染
pdfLoading.value = false
isRendering.value = false
if (pdfDoc.value) {
pdfDoc.value.destroy().catch(() => {})
pdfDoc.value = null
}
pdfTotalPages.value = 0
pdfScale.value = 1.2
if (pdfScrollContainer.value) {
pdfScrollContainer.value.innerHTML = ''
}
}
const zoomIn = () => {
pdfScale.value = Math.min(pdfScale.value + 0.2, 3.0)
renderAllPages(pdfScale.value)
}
const zoomOut = () => {
pdfScale.value = Math.max(pdfScale.value - 0.2, 0.5)
renderAllPages(pdfScale.value)
}
// 图片查看相关状态 // 图片查看相关状态
const imageViewerVisible = ref(false) const imageViewerVisible = ref(false)
const imageUrl = ref('') const imageUrl = ref('')
...@@ -867,6 +889,61 @@ defineExpose({ ...@@ -867,6 +889,61 @@ defineExpose({
padding: 8px 0; padding: 8px 0;
border-bottom: 1px solid #e4e7ed; border-bottom: 1px solid #e4e7ed;
margin-bottom: 12px; margin-bottom: 12px;
.page-info {
font-size: 14px;
color: #606266;
}
}
.pdf-scroll-wrapper {
flex: 1;
overflow-y: auto; /* 垂直滚动 */
border: 1px solid #ebeef5;
border-radius: 4px;
background: #f9fafc;
}
.pdf-scroll-container {
display: flex;
flex-direction: column;
align-items: center; /* 页面居中显示 */
padding: 16px;
}
.pdf-page-canvas {
display: block;
max-width: 100%; /* 自适应宽度,防止溢出容器 */
height: auto;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin-bottom: 16px;
}
.pdf-loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 300px;
color: #409eff;
gap: 12px;
.el-icon {
font-size: 32px;
}
}
.pdf-viewer {
display: flex;
flex-direction: column;
height: 70vh;
}
.pdf-toolbar {
display: flex;
justify-content: center;
align-items: center;
gap: 16px;
padding: 8px 0;
border-bottom: 1px solid #e4e7ed;
margin-bottom: 12px;
.page-info { .page-info {
font-size: 14px; font-size: 14px;
......
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