Commit 08e9e2d3 by yuzhenWang

用户体验优化1

parent 634376d0
...@@ -490,7 +490,6 @@ export function billBatchSave(data) { ...@@ -490,7 +490,6 @@ export function billBatchSave(data) {
}) })
} }
// 拆分出账查询-计算目标金额 // 拆分出账查询-计算目标金额
export function billCalculateToAmount(data) { export function billCalculateToAmount(data) {
return request({ return request({
...@@ -508,3 +507,35 @@ export function CommissionExpectedChangeStatus(data) { ...@@ -508,3 +507,35 @@ export function CommissionExpectedChangeStatus(data) {
data: 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) { ...@@ -139,3 +139,10 @@ export function addSinglePremiumRemittance(data) {
data: 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) { ...@@ -256,3 +256,4 @@ export function getProductList(data) {
data: 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) => { ...@@ -597,7 +597,42 @@ export const validateIdCardSimple = (rule, value, callback) => {
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 { export default {
validateEnglish, validateEnglish,
...@@ -605,5 +640,6 @@ export default { ...@@ -605,5 +640,6 @@ export default {
validatePhone, validatePhone,
validateBMI, validateBMI,
validateIdCard, validateIdCard,
validateIdCardSimple validateIdCardSimple,
validateEnglish2
} }
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
:border="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="businessNo" label="业务编号" min-width="120" />
<el-table-column prop="fortuneAccountBizId" label="业务ID" min-width="120" sortable /> <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="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 />
...@@ -62,11 +63,17 @@ ...@@ -62,11 +63,17 @@
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <!-- <el-table-column
prop="fortuneAccountDate" prop="fortuneAccountDate"
label="出账日" label="出账日"
min-width="150" min-width="150"
show-overflow-tooltip 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 prop="remark" label="备注" min-width="150" show-overflow-tooltip /> -->
<el-table-column fixed="right" label="操作" min-width="120"> <el-table-column fixed="right" label="操作" min-width="120">
...@@ -174,8 +181,23 @@ ...@@ -174,8 +181,23 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="出账机构" prop="billOrg" width="150"> <el-table-column label="出账机构" prop="billOrg" width="150">
<template #default="scope"> <!-- <template #default="scope">
<el-input v-model="scope.row.billOrg" placeholder="请输入" clearable /> <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> </template>
</el-table-column> </el-table-column>
<el-table-column label="原币种金额" prop="fromAmount" width="150"> <el-table-column label="原币种金额" prop="fromAmount" width="150">
...@@ -303,9 +325,9 @@ ...@@ -303,9 +325,9 @@
:showAction="true" :showAction="true"
:showClose="true" :showClose="true"
@close="showSalarySetting = false" @close="showSalarySetting = false"
@confirm = "salaryDataSetting" @confirm="salaryDataSetting"
> >
<el-date-picker <el-date-picker
v-model="fortuneAccountDate" v-model="fortuneAccountDate"
type="date" type="date"
placeholder="选择薪资日" placeholder="选择薪资日"
...@@ -341,7 +363,7 @@ import { debounce } from 'lodash-es' ...@@ -341,7 +363,7 @@ import { debounce } from 'lodash-es'
const amountInput = usePositiveDecimal(4) // 默认2位小数 const amountInput = usePositiveDecimal(4) // 默认2位小数
const { proxy } = getCurrentInstance() 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 searchFormRef = ref(null)
const searchParams = ref({}) const searchParams = ref({})
const searchConfig = ref([ const searchConfig = ref([
...@@ -421,7 +443,7 @@ const debounceChangeToAmountMap = new WeakMap() ...@@ -421,7 +443,7 @@ const debounceChangeToAmountMap = new WeakMap()
// 表格操作菜单 // 表格操作菜单
const dropdownItems = [ const dropdownItems = [
{ label: '拆分出账', value: 'splitBilling' }, { label: '拆分出账', value: 'splitBilling' },
{ label: '设置出账日', value: 'settingSalaryDate' }, { label: '设置出账日', value: 'settingSalaryDate' }
// { label: '查看记录', value: 'viewRecord' } // { label: '查看记录', value: 'viewRecord' }
] ]
//=============拆分出账开始================ //=============拆分出账开始================
...@@ -684,30 +706,29 @@ const handleSelect = (e, row) => { ...@@ -684,30 +706,29 @@ const handleSelect = (e, row) => {
billCurrentPage.value = 1 billCurrentPage.value = 1
billTableList.value = [] billTableList.value = []
getSplitTableList() getSplitTableList()
}else if(e==='settingSalaryDate'){ } else if (e === 'settingSalaryDate') {
console.log('更新薪资数据') console.log('更新薪资数据')
fortuneAccountDate.value= currentRow.value.fortuneAccountDate || '' fortuneAccountDate.value = currentRow.value.fortuneAccountDate || ''
showSalarySetting.value = true; showSalarySetting.value = true
} }
} }
const salaryDataSetting = async (e)=>{ const salaryDataSetting = async e => {
try{ try {
const params = { const params = {
fortuneAccountBizId : currentRow.value.fortuneAccountBizId, fortuneAccountBizId: currentRow.value.fortuneAccountBizId,
fortuneAccountDate:fortuneAccountDate.value fortuneAccountDate: fortuneAccountDate.value
}; }
const response = await updatePolicyFortuneAccount(params) const response = await updatePolicyFortuneAccount(params)
if(response.code==200){ if (response.code == 200) {
showSalarySetting.value = false; showSalarySetting.value = false
ElMessage.success('修改成功') ElMessage.success('修改成功')
getList() getList()
} }
}catch (error) { } catch (error) {
console.error('获取数据失败:', error) console.error('获取数据失败:', error)
ElMessage.error('修改失败') ElMessage.error('修改失败')
} }
} }
// 分页事件 // 分页事件
const handleSizeChange = val => { const handleSizeChange = val => {
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
</template> </template>
</CommonPage> </CommonPage>
</div> </div>
</template> </template>
...@@ -62,7 +62,8 @@ const searchConfig = ref([ ...@@ -62,7 +62,8 @@ const searchConfig = ref([
]) ])
const tableColumns = 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: 'brokerName', label: '转介人', sortable: true, width: '150',fixed:'left'},
{ prop: 'internalNumber', label: '内部编号', sortable: true, width: '150'}, { prop: 'internalNumber', label: '内部编号', sortable: true, width: '150'},
{ prop: 'team', label: '所属团队', sortable: true, width: '150'}, { prop: 'team', label: '所属团队', sortable: true, width: '150'},
...@@ -151,7 +152,7 @@ const handleExport = async () => { ...@@ -151,7 +152,7 @@ const handleExport = async () => {
startMonth:params.payoutMonth ? params.payoutMonth[0] : '', startMonth:params.payoutMonth ? params.payoutMonth[0] : '',
endMonth:params.payoutMonth ? params.payoutMonth[1] :'', endMonth:params.payoutMonth ? params.payoutMonth[1] :'',
payoutMonth:undefined, payoutMonth:undefined,
} }
const response = await exportPayRoll(params) const response = await exportPayRoll(params)
if(response.data && response.data.url){ if(response.data && response.data.url){
...@@ -185,4 +186,4 @@ const operationBtnList = ref([ ...@@ -185,4 +186,4 @@ const operationBtnList = ref([
</script> </script>
<style scoped></style> <style scoped></style>
\ No newline at end of file
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<span class="iconfont icon-yanqiweiwancheng"></span> <span class="iconfont icon-yanqiweiwancheng"></span>
<span <span
>{{ parseTime(processInfo.createTime) }}{{ >{{ parseTime(processInfo.createTime) }}{{
processInfo.userBizId || '--' processInfo.creatorName || '--'
}}创建</span }}创建</span
> >
</div> </div>
...@@ -161,7 +161,7 @@ const processInfo = ref({ ...@@ -161,7 +161,7 @@ const processInfo = ref({
fnaNo: '--', fnaNo: '--',
status: '未保存', status: '未保存',
createTime: proxy.parseTime(new Date()), createTime: proxy.parseTime(new Date()),
customerName: userStore.name creatorName: userStore.name
}) // 流程详情信息 }) // 流程详情信息
const updateStatus = ref(false) const updateStatus = ref(false)
const dictTypeLists = ref([]) const dictTypeLists = ref([])
...@@ -321,7 +321,7 @@ const getDictsData = async () => { ...@@ -321,7 +321,7 @@ const getDictsData = async () => {
projectBizId: userStore.projectInfo.projectBizId, projectBizId: userStore.projectInfo.projectBizId,
tenantBizId: userStore.projectInfo.tenantBizId, tenantBizId: userStore.projectInfo.tenantBizId,
fieldBizId: 'field_olk1qZe81qHHKXbw', fieldBizId: 'field_olk1qZe81qHHKXbw',
fieldValueBizId: 'field_value_yXzTigvgUdRMFpoR', fieldValueBizId: 'field_value_yXzTigvgUdRMFpoR'
} }
const response6 = await secondAdditonalList(params6) const response6 = await secondAdditonalList(params6)
if (response6.code == 200) { if (response6.code == 200) {
...@@ -399,6 +399,9 @@ function getProcessInfo(fnaBizId, changeTab, currentTab) { ...@@ -399,6 +399,9 @@ function getProcessInfo(fnaBizId, changeTab, currentTab) {
getProcessDetail(fnaBizId).then(async res => { getProcessDetail(fnaBizId).then(async res => {
if (res.code == 200) { if (res.code == 200) {
processInfo.value = res.data processInfo.value = res.data
if (!processInfo.value.creatorName) {
processInfo.value.creatorName = userStore.name
}
tabsList.value.forEach(item => { tabsList.value.forEach(item => {
if (res.data[item.key] && item.status) { if (res.data[item.key] && item.status) {
item.status = '1' item.status = '1'
......
...@@ -61,6 +61,16 @@ ...@@ -61,6 +61,16 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </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-row>
</el-form> </el-form>
</template> </template>
...@@ -76,22 +86,26 @@ ...@@ -76,22 +86,26 @@
max-height="380px" max-height="380px"
> >
<el-table-column type="index" width="100" label="序号" /> <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"> <template #default="scope">
<el-button link size="default"> <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> </el-button>
</template> </template>
</el-table-column> </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"> <template #default="scope">
<el-button link size="default"> <el-button link size="default">
<span @click="copyToClipboard(scope.row.customerNameCn,'中文姓名')">{{ scope.row.customerNameCn }}</span> <span @click="copyToClipboard(scope.row.customerNameCn, '中文姓名')">{{
</el-button> scope.row.customerNameCn
</template> }}</span>
</el-table-column> </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="状态" 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"> <template #default="scope">
<span>{{ selectDictLabel(csf_fna_status, scope.row.status) }}</span> <span>{{ selectDictLabel(csf_fna_status, scope.row.status) }}</span>
</template> </template>
...@@ -101,13 +115,16 @@ ...@@ -101,13 +115,16 @@
align="center" align="center"
prop="fnaNo" prop="fnaNo"
width="240" width="240"
show-overflow-tooltip> show-overflow-tooltip
<template #default="scope"> >
<template #default="scope">
<el-button link size="default"> <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> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
label="预约编号" label="预约编号"
align="center" align="center"
...@@ -117,12 +134,14 @@ ...@@ -117,12 +134,14 @@
> >
<template #default="scope"> <template #default="scope">
<el-button link size="default"> <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> </el-button>
</template> </template>
</el-table-column> </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="policyNo" width="180" />
<el-table-column label="创建人" align="center" prop="creatorName" width="180" />
<el-table-column label="创建时间" sortable align="center" prop="createTime" width="200"> <el-table-column label="创建时间" sortable align="center" prop="createTime" width="200">
<template #default="scope"> <template #default="scope">
...@@ -181,7 +200,7 @@ import { getFnaList, deleteFna, subProcess } from '@/api/sign/fna' ...@@ -181,7 +200,7 @@ import { getFnaList, deleteFna, subProcess } from '@/api/sign/fna'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { MoreFilled, InfoFilled } from '@element-plus/icons-vue' import { MoreFilled, InfoFilled } from '@element-plus/icons-vue'
import useDictStore from '@/store/modules/dict' import useDictStore from '@/store/modules/dict'
import copyToClipboard from '@/utils/copyToClipboard'; import copyToClipboard from '@/utils/copyToClipboard'
const dictStore = useDictStore() const dictStore = useDictStore()
const userStore = useUserStore() const userStore = useUserStore()
const router = useRouter() const router = useRouter()
...@@ -395,7 +414,6 @@ function handleUpdate(row) { ...@@ -395,7 +414,6 @@ function handleUpdate(row) {
} }
getList() getList()
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.bottomBtn { .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