Commit 08e9e2d3 by yuzhenWang

用户体验优化1

parent 634376d0
......@@ -490,7 +490,6 @@ export function billBatchSave(data) {
})
}
// 拆分出账查询-计算目标金额
export function billCalculateToAmount(data) {
return request({
......@@ -508,3 +507,35 @@ export function CommissionExpectedChangeStatus(data) {
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 editExchangeRateApi(data) {
return request({
url: 'csf/api/fortune/edit/exchange_rate',
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>
......@@ -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,6 +42,7 @@
:border="true"
>
<el-table-column type="selection" width="55" :reserve-selection="true" />
<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 />
......@@ -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,7 +325,7 @@
:showAction="true"
:showClose="true"
@close="showSalarySetting = false"
@confirm = "salaryDataSetting"
@confirm="salaryDataSetting"
>
<el-date-picker
v-model="fortuneAccountDate"
......@@ -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([
......@@ -421,7 +443,7 @@ const debounceChangeToAmountMap = new WeakMap()
// 表格操作菜单
const dropdownItems = [
{ label: '拆分出账', value: 'splitBilling' },
{ label: '设置出账日', value: 'settingSalaryDate' },
{ label: '设置出账日', value: 'settingSalaryDate' }
// { label: '查看记录', value: 'viewRecord' }
]
//=============拆分出账开始================
......@@ -684,30 +706,29 @@ 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 salaryDataSetting = async e => {
try {
const params = {
fortuneAccountBizId : currentRow.value.fortuneAccountBizId,
fortuneAccountDate:fortuneAccountDate.value
};
fortuneAccountBizId: currentRow.value.fortuneAccountBizId,
fortuneAccountDate: fortuneAccountDate.value
}
const response = await updatePolicyFortuneAccount(params)
if(response.code==200){
showSalarySetting.value = false;
if (response.code == 200) {
showSalarySetting.value = false
ElMessage.success('修改成功')
getList()
}
}catch (error) {
} catch (error) {
console.error('获取数据失败:', error)
ElMessage.error('修改失败')
}
}
// 分页事件
const handleSizeChange = val => {
......
......@@ -62,7 +62,8 @@ const searchConfig = ref([
])
const tableColumns = ref([
{ prop: 'salarySplitNo', label: '发放编号', sortable: true, width: '150',fixed:'left'},
{ 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'},
......
......@@ -12,7 +12,7 @@
<span class="iconfont icon-yanqiweiwancheng"></span>
<span
>{{ parseTime(processInfo.createTime) }}{{
processInfo.userBizId || '--'
processInfo.creatorName || '--'
}}创建</span
>
</div>
......@@ -161,7 +161,7 @@ const processInfo = ref({
fnaNo: '--',
status: '未保存',
createTime: proxy.parseTime(new Date()),
customerName: userStore.name
creatorName: userStore.name
}) // 流程详情信息
const updateStatus = ref(false)
const dictTypeLists = ref([])
......@@ -321,7 +321,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 +399,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.name
}
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,17 +86,21 @@
max-height="380px"
>
<el-table-column type="index" width="100" label="序号" />
<el-table-column label="客户姓名" align="center" prop="customerName" width="180" >
<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" >
<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>
<span @click="copyToClipboard(scope.row.customerNameCn, '中文姓名')">{{
scope.row.customerNameCn
}}</span>
</el-button>
</template>
</el-table-column>
......@@ -101,10 +115,13 @@
align="center"
prop="fnaNo"
width="240"
show-overflow-tooltip>
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>
......@@ -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" /> -->
<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 {
......
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