Commit c5483caa by yuzhenWang

Merge branch 'test' into 'feature-20250827wyz-写业务'

Test

See merge request !14
parents ab707eaa 3b2adcf6
{
"name": "ruoyi",
"name": "csf",
"version": "3.9.0",
"description": "CSF-CORE",
"author": "若依",
"author": "csf",
"license": "MIT",
"type": "module",
"scripts": {
......@@ -15,17 +15,17 @@
},
"repository": {
"type": "git",
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
"url": ""
},
"dependencies": {
"@element-plus/icons-vue": "2.3.1",
"@element-plus/icons-vue": "^2.3.1",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "13.3.0",
"axios": "1.9.0",
"clipboard": "2.0.11",
"dayjs": "^1.11.18",
"echarts": "5.6.0",
"element-plus": "2.9.9",
"element-plus": "^2.9.9",
"file-saver": "2.0.5",
"fuse.js": "6.6.2",
"js-beautify": "1.14.11",
......@@ -37,7 +37,8 @@
"vue": "3.5.16",
"vue-cropper": "1.1.1",
"vue-router": "4.5.1",
"vuedraggable": "4.1.0"
"vuedraggable": "4.1.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "5.2.4",
......
import request from '@/utils/request'
// 获取保单来佣列表
export function getPolicyCommissionList(data) {
return request({
url: '/csf/api/commission/list/page/vo',
method: 'post',
data: data
})
}
// 更新保单来佣信息
export function updatePolicyCommission(data) {
return request({
url: '/csf/api/commission/update',
method: 'post',
data: data
})
}
// 生成可出账记录
export function generateCommissionRecord(data) {
return request({
url: '/csf/api/commission/generate/fortune',
method: 'post',
data: data
})
}
// 更新保单发佣信息
export function updatePolicyFortune(data) {
return request({
url: '/csf/api/fortune/update',
method: 'post',
data: data
})
}
// 获取保单发佣列表
export function getPolicyFortuneList(data) {
return request({
url: '/csf/api/fortune/list/page/vo',
method: 'post',
data: data
})
}
// 下载选中的发佣数据
export function downloadPolicyFortune(data) {
return request({
url: '/csf/api/fortune/download/raw',
method: 'post',
data: data,
responseType: 'blob'
})
}
// 生成出账清单
// /csf/api/fortune/download/account
export function downloadPolicyFortuneAccount(data) {
return request({
url: '/csf/api/fortune/download/account',
method: 'post',
data: data,
responseType: 'blob'
})
}
// 按照人的维度,查询出账列表
export function getReferrerFortuneList(data) {
return request({
url: '/csf/api/fortune/list/page/fortuneAccount',
method: 'post',
data: data
})
}
// 删除来佣数据
// /csf/api/commission/delete
export function deletePolicyCommission(data) {
return request({
url: '/csf/api/commission/delete',
method: 'post',
data: data
})
}
// 更新出账状态
// /csf/api/fortune/update/status
export function updatePolicyFortuneStatus(data) {
return request({
url: '/csf/api/fortune/update/status',
method: 'post',
data: data
})
}
// 完成出账
// /csf/api/fortune/complete
export function completePolicyFortune(data) {
return request({
url: '/csf/api/fortune/complete/fortuneAccount',
method: 'post',
data: data
})
}
// 获取对账公司
// /csf/api/reconciliation_company/list/page
export function getReconciliationCompanyList(data) {
return request({
url: '/csf/api/reconciliation_company/list/page',
method: 'post',
data: data
})
}
// 删除出账
// /csf/api/fortune/delete
export function deletePolicyFortune(data) {
return request({
url: '/csf/api/fortune/delete/fortuneAccount',
method: 'post',
data: data
})
}
// 修改出账信息
// /csf/api/fortune/update/fortuneAccount
export function updatePolicyFortuneAccount(data) {
return request({
url: '/csf/api/fortune/update/fortuneAccount',
method: 'post',
data: data
})
}
// 新建出账
// /csf/api/fortune/add/fortuneAccount
export function addPolicyFortuneAccount(data) {
return request({
url: '/csf/api/fortune/add/fortuneAccount',
method: 'post',
data: data
})
}
// 删除发佣
// /csf/api/fortune/delete/fortuneAccount
export function deletePolicyFortuneAccount(data) {
return request({
url: '/csf/api/fortune/delete',
method: 'post',
data: data
})
}
// 新建来佣
// /csf/api/commission/add
export function addPolicyCommission(data) {
return request({
url: '/csf/api/commission/add',
method: 'post',
data: data
})
}
import request from '@/utils/request'
// 分页获取新单跟进列表
export function getPolicyFollowList(data) {
return request({
url: '/csf/api/policy_follow/list/page/vo',
method: 'post',
data: data
})
}
// 获取预计来佣列表
// /csf/api/commission/list/page/commission_expected
export function getExpectedCommissionList(data) {
return request({
url: '/csf/api/policy/list/page/commission_expected',
method: 'post',
data: data
})
}
// 更新至保单库
export function updateToPolicyLib(data) {
return request({
url: '/csf/api/policy_follow/addToPolicy',
method: 'post',
data: data
})
}
\ No newline at end of file
......@@ -3,9 +3,9 @@ import request from '@/utils/request'
// 查询字典类型列表
export function listType(query) {
return request({
url: '/system/dict/type/list',
method: 'get',
params: query
url: '/user/api/sysDict/type/list',
method: 'post',
data: query
})
}
......
<template>
<div class="upload-file">
<el-upload
multiple
:multiple = "multiple"
:action="uploadFileUrl"
:before-upload="handleBeforeUpload"
:file-list="fileList"
......@@ -17,218 +17,250 @@
v-if="!disabled"
>
<!-- 上传按钮 -->
<el-button type="primary">选取文件</el-button>
<el-button size="default" type="primary" :disabled="isDisabled">{{ uploadBtnText }}</el-button>
<!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip">
请上传
<template v-if="fileSize">大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
<template v-if="fileType">格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
的文件
</div>
</el-upload>
<!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip && !disabled">
请上传
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
的文件
</div>
<!-- 文件列表 -->
<transition-group ref="uploadFileList" class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
<transition-group ref="uploadFileList" class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul" v-if="uploadList.length > 0">
<li :key="file.url" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
</el-link>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(index)" type="danger" v-if="!disabled">&nbsp;删除</el-link>
<el-link :underline="false" @click="handleDelete(index)" type="danger" v-if="!disabled">删除</el-link>
</div>
</li>
</transition-group>
</div>
</template>
<script setup>
<script>
import { getToken } from "@/utils/auth"
import Sortable from 'sortablejs'
const props = defineProps({
modelValue: [String, Object, Array],
// 上传接口地址
action: {
type: String,
default: "/common/upload"
},
// 上传携带的参数
data: {
type: Object
export default {
name: "FileUpload",
props: {
// 上传按钮文字
uploadBtnText: {
type: String,
default: "选取文件"
},
// 响应类型
responseType: {
type: String,
default: "json"
},
// 值
value: [String, Object, Array],
// 上传接口地址
action: {
type: String,
default: "/common/upload"
},
// 上传携带的参数
data: {
type: Object
},
// 数量限制
limit: {
type: Number,
default: 5
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 5
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ["doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "pdf"]
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: true
},
// 禁用组件(仅查看文件)
disabled: {
type: Boolean,
default: false
},
// 拖动排序
drag: {
type: Boolean,
default: true
},
multiple:{
type: Boolean,
default: true
},
// 禁用上传按钮
isDisabled: {
type: Boolean,
default: false
},
},
// 数量限制
limit: {
type: Number,
default: 5
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 5
data() {
return {
number: 0,
uploadList: [],
baseUrl: import.meta.env.VITE_APP_BASE_API,
uploadFileUrl: import.meta.env.VITE_APP_BASE_API + this.action, // 上传文件服务器地址
headers: {
Authorization: "Bearer " + getToken(),
},
fileList: []
}
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ["doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "pdf"]
mounted() {
if (this.drag && !this.disabled) {
this.$nextTick(() => {
const element = this.$refs.uploadFileList?.$el || this.$refs.uploadFileList
// 添加元素存在性检查,防止undefined错误
if (element) {
Sortable.create(element, {
ghostClass: 'file-upload-darg',
onEnd: (evt) => {
const movedItem = this.fileList.splice(evt.oldIndex, 1)[0]
this.fileList.splice(evt.newIndex, 0, movedItem)
this.$emit("input", this.listToString(this.fileList))
}
})
}
})
}
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: true
watch: {
value: {
handler(val) {
if (val) {
let temp = 1
// 首先将值转为数组
const list = Array.isArray(val) ? val : this.value.split(',')
// 然后将数组转为对象数组
this.fileList = list.map(item => {
if (typeof item === "string") {
item = { name: item, url: item }
}
item.uid = item.uid || new Date().getTime() + temp++
return item
})
} else {
this.fileList = []
return []
}
},
deep: true,
immediate: true
}
},
// 禁用组件(仅查看文件)
disabled: {
type: Boolean,
default: false
computed: {
// 是否显示提示
showTip() {
return this.isShowTip && (this.fileType || this.fileSize)
},
},
// 拖动排序
drag: {
type: Boolean,
default: true
}
})
const { proxy } = getCurrentInstance()
const emit = defineEmits()
const number = ref(0)
const uploadList = ref([])
const baseUrl = import.meta.env.VITE_APP_BASE_API
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action) // 上传文件服务器地址
const headers = ref({ Authorization: "Bearer " + getToken() })
const fileList = ref([])
const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize)
)
watch(() => props.modelValue, val => {
if (val) {
let temp = 1
// 首先将值转为数组
const list = Array.isArray(val) ? val : props.modelValue.split(',')
// 然后将数组转为对象数组
fileList.value = list.map(item => {
if (typeof item === "string") {
item = { name: item, url: item }
methods: {
// 上传前校检格式和大小
handleBeforeUpload(file) {
// 校检文件类型
if (this.fileType) {
const fileName = file.name.split('.')
const fileExt = fileName[fileName.length - 1]
const isTypeOk = this.fileType.indexOf(fileExt) >= 0
if (!isTypeOk) {
this.$modal.msgError(`文件格式不正确,请上传${this.fileType.join("/")}格式文件!`)
return false
}
}
item.uid = item.uid || new Date().getTime() + temp++
return item
})
} else {
fileList.value = []
return []
}
},{ deep: true, immediate: true })
// 上传前校检格式和大小
function handleBeforeUpload(file) {
// 校检文件类型
if (props.fileType.length) {
const fileName = file.name.split('.')
const fileExt = fileName[fileName.length - 1]
const isTypeOk = props.fileType.indexOf(fileExt) >= 0
if (!isTypeOk) {
proxy.$modal.msgError(`文件格式不正确,请上传${props.fileType.join("/")}格式文件!`)
return false
}
}
// 校检文件名是否包含特殊字符
if (file.name.includes(',')) {
proxy.$modal.msgError('文件名不正确,不能包含英文逗号!')
return false
}
// 校检文件大小
if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize
if (!isLt) {
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`)
return false
}
}
proxy.$modal.loading("正在上传文件,请稍候...")
number.value++
return true
}
// 文件个数超出
function handleExceed() {
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`)
}
// 上传失败
function handleUploadError(err) {
proxy.$modal.msgError("上传文件失败")
proxy.$modal.closeLoading()
}
// 上传成功回调
function handleUploadSuccess(res, file) {
if (res.code === 200) {
uploadList.value.push({ name: res.fileName, url: res.fileName })
uploadedSuccessfully()
} else {
number.value--
proxy.$modal.closeLoading()
proxy.$modal.msgError(res.msg)
proxy.$refs.fileUpload.handleRemove(file)
uploadedSuccessfully()
}
}
// 删除文件
function handleDelete(index) {
fileList.value.splice(index, 1)
emit("update:modelValue", listToString(fileList.value))
}
// 上传结束处理
function uploadedSuccessfully() {
if (number.value > 0 && uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value)
uploadList.value = []
number.value = 0
emit("update:modelValue", listToString(fileList.value))
proxy.$modal.closeLoading()
}
}
// 获取文件名称
function getFileName(name) {
// 如果是url那么取最后的名字 如果不是直接返回
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1)
} else {
return name
}
}
// 对象转成指定字符串分隔
function listToString(list, separator) {
let strs = ""
separator = separator || ","
for (let i in list) {
if (list[i].url) {
strs += list[i].url + separator
// 校检文件名是否包含特殊字符
if (file.name.includes(',')) {
this.$modal.msgError('文件名不正确,不能包含英文逗号!')
return false
}
// 校检文件大小
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize
if (!isLt) {
this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`)
return false
}
}
this.$modal.loading("正在上传文件,请稍候...")
this.number++
return true
},
// 文件个数超出
handleExceed() {
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`)
},
// 上传失败
handleUploadError(err) {
this.$modal.msgError("上传文件失败,请重试")
this.$modal.closeLoading()
},
// 上传成功回调
handleUploadSuccess(res, file) {
console.log(res)
if (res.code === 200) {
if (this.responseType !== "onlyStatus") {
this.uploadList.push({ name: res.fileName, url: res.fileUrl })
}
this.uploadedSuccessfully(res.code)
} else {
this.number--
this.$modal.closeLoading()
this.$modal.msgError(res.msg)
this.$refs.fileUpload.handleRemove(file)
this.uploadedSuccessfully(res.code)
}
},
// 删除文件
handleDelete(index) {
this.fileList.splice(index, 1)
this.$emit("input", this.listToString(this.fileList))
},
// 上传结束处理
uploadedSuccessfully(code) {
if (this.number > 0 && this.uploadList.length === this.number && this.responseType !== "onlyStatus") {
this.fileList = this.fileList.concat(this.uploadList)
}
this.uploadList = []
this.number = 0
this.$emit("uploadEnd", this.uploadList.length > 0 ? this.listToString(this.fileList):code)
this.$modal.closeLoading()
},
// 获取文件名称
getFileName(name) {
// 如果是url那么取最后的名字 如果不是直接返回
if (name &&name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1)
} else {
return name
}
},
// 对象转成指定字符串分隔
listToString(list, separator) {
let strs = ""
separator = separator || ","
for (let i in list) {
strs += list[i].url + separator
}
return strs != '' ? strs.substr(0, strs.length - 1) : ''
}
}
return strs != '' ? strs.substr(0, strs.length - 1) : ''
}
// 初始化拖拽排序
onMounted(() => {
if (props.drag && !props.disabled) {
nextTick(() => {
const element = proxy.$refs.uploadFileList?.$el || proxy.$refs.uploadFileList
Sortable.create(element, {
ghostClass: 'file-upload-darg',
onEnd: (evt) => {
const movedItem = fileList.value.splice(evt.oldIndex, 1)[0]
fileList.value.splice(evt.newIndex, 0, movedItem)
emit('update:modelValue', listToString(fileList.value))
}
})
})
}
})
</script>
<style scoped lang="scss">
.file-upload-darg {
opacity: 0.5;
......@@ -242,7 +274,6 @@ onMounted(() => {
line-height: 2;
margin-bottom: 10px;
position: relative;
transition: none !important;
}
.upload-file-list .ele-upload-list__item-content {
display: flex;
......@@ -253,4 +284,7 @@ onMounted(() => {
.ele-upload-list__item-content-action .el-link {
margin-right: 10px;
}
</style>
.el-upload__tip{
margin-left: 10px;
}
</style>
\ No newline at end of file
<template>
<el-dialog
:model-value="visible"
:title="title"
width="80%"
top="5vh"
:close-on-click-modal="false"
class="detail-dialog"
@update:model-value="handleVisibleChange"
@close="handleClose"
>
<el-tabs v-model="activeTab" class="detail-tabs">
<!-- 保单信息 -->
<el-tab-pane label="保单信息" name="policy">
<el-descriptions :column="2" border>
<el-descriptions-item label="保单号">{{ detailData.policyNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="客户名称">{{ detailData.customerName || '-' }}</el-descriptions-item>
<el-descriptions-item label="签单日期">{{ detailData.signDate || '-' }}</el-descriptions-item>
<el-descriptions-item label="签单人">{{ detailData.signer || '-' }}</el-descriptions-item>
<el-descriptions-item label="供款年期">{{ detailData.paymentTerm || '-' }}</el-descriptions-item>
<el-descriptions-item label="产品名称">{{ detailData.productName || '-' }}</el-descriptions-item>
<el-descriptions-item label="保险公司">{{ detailData.insurer || '-' }}</el-descriptions-item>
<el-descriptions-item label="对账公司">{{ detailData.reconciliationCompany || '-' }}</el-descriptions-item>
<el-descriptions-item label="保单持有人">{{ detailData.policyHolder || '-' }}</el-descriptions-item>
<el-descriptions-item label="受保人">{{ detailData.insured || '-' }}</el-descriptions-item>
<el-descriptions-item label="币种">{{ detailData.currency || '-' }}</el-descriptions-item>
<el-descriptions-item label="首期保费">{{ numberWithCommas(detailData.initialPremium || 0) }}</el-descriptions-item>
<el-descriptions-item label="新单状态">{{ convertStatusToDict(detailData.status) || '-' }}</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<!-- 入账信息 -->
<el-tab-pane label="入账信息" name="income">
<el-table :data="expectedCommissionList" border size="small">
<el-table-column type="index" width="30" />
<el-table-column prop="reconciliationCompany" label="对账公司" align="center" />
<el-table-column prop="commissionPeriod" label="佣金期数" align="center" />
<el-table-column prop="totalPeriod" label="总佣金期数" align="center" />
<el-table-column prop="commissionName" label="来佣名称" align="center" />
<!-- <el-table-column prop="commissionType" label="来佣类型" align="center" /> -->
<el-table-column prop="amount" label="来佣金额" align="center" />
<el-table-column prop="currency" label="币种" align="center" />
<el-table-column prop="commissionDate" label="来佣日期" align="center" />
<el-table-column prop="commissionStatusLabel" label="来佣状态" align="center" />
<el-table-column prop="remark" label="备注" align="center" />
<el-table-column prop="creatorId" label="创建人" align="center" />
<el-table-column prop="updaterId" label="更新人" align="center" />
<el-table-column prop="createTime" label="创建时间" align="center" />
<el-table-column prop="updateTime" label="更新时间" align="center" />
<!-- <template #append>
<div style="padding: 20px;display: flex;justify-content: end;">
<el-pagination
v-model:current-page="currentPage1"
:page-size="pageSize1"
background
layout="total, prev, pager, next"
:total="total1"
@current-change="handleCurrentChange('expectedCommissionList')"
/>
</div>
</template> -->
</el-table>
</el-tab-pane>
<!-- 出账信息 -->
<el-tab-pane label="出账信息" name="expense">
<el-table :data="policyFortuneList" border size="small">
<el-table-column type="index" width="30" />
<el-table-column prop="fortunePeriod" label="佣金期数" align="center" />
<el-table-column prop="fortuneTotalPeriod" label="总佣金期数" align="center" />
<el-table-column prop="broker" label="转介人" align="center" />
<el-table-column prop="team" label="所属团队" align="center" />
<el-table-column prop="fortuneName" label="出账名称" align="center" />
<el-table-column prop="amount" label="金额" align="center" />
<el-table-column prop="currency" label="币种" align="center" />
<el-table-column prop="payoutDate" label="出账日期" align="center" />
<el-table-column prop="fortuneStatusLabel" label="出账状态" align="center" />
<el-table-column prop="remark" label="备注" align="center" />
</el-table>
</el-tab-pane>
<!-- 转介人信息 -->
<el-tab-pane label="转介人信息" name="broker">
<el-table :data="detailData.brokerList" border size="small">
<el-table-column prop="brokerName" label="姓名" align="center" />
<el-table-column prop="team" label="团队" align="center" />
<el-table-column prop="brokerRatio" label="介绍费占比" align="center" />
<el-table-column prop="remark" label="备注" align="center" />
</el-table>
</el-tab-pane>
<!-- 其他信息 -->
<!-- <el-tab-pane label="其他信息" name="other">
<el-descriptions :column="2" border>
<el-descriptions-item label="创建时间">{{ detailData.createTime || '-' }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ detailData.updateTime || '-' }}</el-descriptions-item>
<el-descriptions-item label="创建人">{{ detailData.createBy || '-' }}</el-descriptions-item>
<el-descriptions-item label="更新人">{{ detailData.updateBy || '-' }}</el-descriptions-item>
<el-descriptions-item label="备注信息" :span="2">{{ detailData.remark || '-' }}</el-descriptions-item>
</el-descriptions>
</el-tab-pane> -->
</el-tabs>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">关闭</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch } from 'vue'
import { numberWithCommas } from '@/utils/index.js'
// 定义props
const props = defineProps({
visible: {
type: Boolean,
default: false
},
detailData: {
type: Object,
default: () => ({})
},
title: {
type: String,
default: '数据详情'
},
policyFollowStatusList: {
type: Array,
default: () => []
},
expectedCommissionList: {
type: Array,
default: () => []
},
policyFortuneList: {
type: Array,
default: () => []
},
})
// 定义emits
const emit = defineEmits(['update:visible', 'close', 'current-change'])
// 响应式数据
const activeTab = ref('policy')
const currentPage1 = ref(1)
const total1 = ref(1000)
const pageSize1 = ref(100)
// 监听visible变化
watch(() => props.visible, (newVal) => {
if (newVal) {
activeTab.value = 'policy'
}
})
// 处理visible变化
const handleVisibleChange = (value) => {
emit('update:visible', value)
}
// 状态转换函数
const convertStatusToDict = (status) => {
const dictItem = props.policyFollowStatusList.find(item => item.itemValue == status)
return dictItem?.itemLabel ?? status
}
// 关闭弹窗
const handleClose = () => {
emit('update:visible', false)
emit('close')
}
// 表格分页当前页改变
const handleCurrentChange = (val) => {
console.log(`当前页: ${val}`)
emit('current-change', val,currentPage1.value)
}
</script>
<style scoped>
.detail-dialog {
max-height: 80vh;
}
.detail-tabs {
max-height: 60vh;
overflow-y: auto;
}
.detail-tabs .el-tab-pane {
padding: 20px;
}
.broker-section {
margin-top: 20px;
}
.broker-section h4 {
margin-bottom: 15px;
color: #303133;
font-weight: 600;
}
/* 响应式调整详情弹窗 */
@media (max-width: 768px) {
.detail-dialog {
width: 95% !important;
}
.detail-tabs .el-descriptions {
font-size: 12px;
}
}
</style>
\ No newline at end of file
import dayjs from 'dayjs'
import { de } from 'element-plus/es/locales.mjs'
export function formatIsoToDateTime(isoStr) {
// 处理 null/undefined 情况,避免报错
......
......@@ -378,7 +378,7 @@ export const selectComponents = [
'color-format': '',
disabled: false,
required: true,
size: 'default',
size: '', // 将 'default' 改为 '' 或 'small'
regList: [],
changeTag: true,
document: 'https://element-plus.org/zh-CN/component/color-picker',
......@@ -449,4 +449,4 @@ export const trigger = {
'el-time-picker': 'change',
'el-date-picker': 'change',
'el-rate': 'change',
}
}
\ No newline at end of file
......@@ -387,4 +387,9 @@ export function camelCase(str) {
export function isNumberStr(str) {
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
}
// 数字千分位格式化,保留2位小数
export function numberWithCommas(x, fixed = 2) {
return x.toFixed(fixed).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
<template>
<div class="financial-billing-page app-container">
<!-- 查询区域 -->
<el-card class="search-card">
<el-form :model="queryParams" label-width="80px">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="保单号">
<el-input
v-model="queryParams.policyNo"
placeholder="请输入保单号"
clearable
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="出账日期">
<el-date-picker
v-model="queryParams.billingDate"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- 数据表格 -->
<el-card>
<el-row :gutter="20">
<el-text class="mx-1" type="danger" style="margin-right: 20px;">勾选出账记录,点击“下载选中项”按钮,下载导入模版</el-text>
</el-row>
<div class="button-row" style="display: flex;gap: 1rem;justify-content: end;">
<el-button
type="primary"
:disabled="selectedRows.length === 0"
@click="downloadSelected"
>
下载选中项
</el-button>
<FileUpload :fileType="['xlsx', 'xls']"
:action="'/csf/api/fortune/upload/excel'"
:uploadBtnText="'批量导入'"
:isShowTip="false"
@uploadEnd = 'getUploadFileFunc'
:responseType="'onlyStatus'"
/>
<el-button
type="success"
:disabled="selectedRows.length === 0"
@click="generateBillingList"
>
生成出账清单
</el-button>
</div>
<el-table
:data="tableData"
@selection-change="handleSelectionChange"
v-loading="loading"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="policyNo" label="保单号" min-width="120" />
<el-table-column prop="fortunePeriod" label="出账期数" width="100" />
<el-table-column prop="fortuneTotalPeriod" label="预计总期数" width="100" />
<el-table-column prop="fortuneName" label="出账项目" min-width="120" />
<el-table-column prop="broker" label="转介人" min-width="100" />
<el-table-column prop="team" label="所属团队" min-width="120" />
<el-table-column prop="amount" label="出账金额" width="120">
<template #default="{ row }">
{{ formatCurrency(row.amount) }}
</template>
</el-table-column>
<el-table-column prop="currency" label="出账币种" width="100" />
<el-table-column prop="status" label="出账状态" width="100">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)">
{{ convertStatusToDict(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip />
<!-- <el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button size="small" @click="handleEdit(row)">修改</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
<el-button size="small" type="info" @click="handleView(row)">查看</el-button>
</template>
</el-table-column> -->
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="getList"
@current-change="getList"
/>
</el-card>
<!-- 新建/编辑对话框 -->
<el-dialog
:title="dialogTitle"
v-model="dialogVisible"
width="600px"
>
<el-form :model="formData" label-width="100px">
<el-form-item label="保单号" required>
<el-input v-model="formData.policyNo" />
</el-form-item>
<el-form-item label="对账公司" required>
<el-select v-model="formData.company">
<el-option
v-for="item in companyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="出账期数" required>
<el-input-number v-model="formData.billingPeriod" :min="1" />
</el-form-item>
<el-form-item label="预计总期数" required>
<el-input-number v-model="formData.totalPeriods" :min="1" />
</el-form-item>
<el-form-item label="出账项目" required>
<el-input v-model="formData.billingItem" />
</el-form-item>
<el-form-item label="转介人">
<el-input v-model="formData.referrer" />
</el-form-item>
<el-form-item label="所属团队">
<el-input v-model="formData.team" />
</el-form-item>
<el-form-item label="出账金额" required>
<el-input-number v-model="formData.amount" :min="0" :precision="2" />
</el-form-item>
<el-form-item label="出账币种" required>
<el-select v-model="formData.currency">
<el-option label="人民币" value="CNY" />
<el-option label="美元" value="USD" />
<el-option label="港币" value="HKD" />
</el-select>
</el-form-item>
<el-form-item label="出账状态" required>
<el-select v-model="formData.status">
<el-option label="待出账" value="pending" />
<el-option label="已出账" value="completed" />
<el-option label="已取消" value="cancelled" />
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.remark"
type="textarea"
:rows="3"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确认</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
// 引入API
import { getPolicyFortuneList, downloadPolicyFortune ,downloadPolicyFortuneAccount} from "@/api/financial/commission"
// 查询参数
const queryParams = reactive({
policyNo: '',
company: '',
billingDate: [],
pageNum: 1,
pageSize: 100
})
// 表格数据
const tableData = ref([])
const total = ref(0)
const loading = ref(false)
const selectedRows = ref([])
// 对话框相关
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formData = reactive({
policyNo: '',
company: '',
billingPeriod: 1,
totalPeriods: 1,
billingItem: '',
referrer: '',
team: '',
amount: 0,
currency: 'CNY',
status: 'pending',
remark: ''
})
// 下载选中项
const downloadSelected = async () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请选择要下载的项')
return
}
try {
// API调用
const response = await downloadPolicyFortune({
fortuneBizIdList: selectedRows.value.map(row => row.fortuneBizId)
})
if (response) {
ElMessage.success('下载成功')
// 触发文件下载
// 处理下载响应
const blob = response instanceof Blob ? response : new Blob([response], { type: 'application/vnd.ms-excel;charset=utf-8' })
// 创建Blob对象,指定正确的MIME类型
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
// 不需要指定文件名,浏览器会自动使用默认文件名
link.download = `出账管理_${new Date().getTime()}.xlsx`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
} else {
ElMessage.error('下载失败')
}
} catch (error) {
ElMessage.error('下载失败')
}
}
const getUploadFileFunc = (data) => {
console.log(data)
if(data===200){
ElMessage.success('上传成功');
getList()
}else{
ElMessage.error('上传失败')
}
}
// 获取数据列表
const getList = async () => {
loading.value = true
try {
// API调用
const response = await getPolicyFortuneList(queryParams)
tableData.value = response.data.records
total.value = response.data.total
loading.value = false
} catch (error) {
loading.value = false
ElMessage.error('获取数据失败')
}
}
// 查询
const handleQuery = () => {
queryParams.pageNum = 1
getList()
}
// 重置查询
const resetQuery = () => {
Object.assign(queryParams, {
policyNo: '',
company: '',
billingDate: [],
pageNum: 1,
pageSize: 10
})
getList()
}
// 选择行变化
const handleSelectionChange = (selection) => {
selectedRows.value = selection
}
// 新建出账记录
const handleCreate = () => {
dialogTitle.value = '新建出账记录'
Object.assign(formData, {
policyNo: '',
company: '',
billingPeriod: 1,
totalPeriods: 1,
billingItem: '',
referrer: '',
team: '',
amount: 0,
currency: 'CNY',
status: 'pending',
remark: ''
})
dialogVisible.value = true
}
// 编辑出账记录
const handleEdit = (row) => {
dialogTitle.value = '编辑出账记录'
Object.assign(formData, { ...row })
dialogVisible.value = true
}
// 删除出账记录
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确认删除这条出账记录吗?', '提示', {
type: 'warning'
})
// await api.deleteBilling(row.id)
ElMessage.success('删除成功')
getList()
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('删除失败')
}
}
}
// 查看详情
const handleView = (row) => {
// 这里可以跳转到详情页面或者打开详情对话框
ElMessage.info(`查看保单号:${row.policyNo}`)
}
const generateBillingList = async () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要生成清单的记录')
return
}
ElMessageBox.confirm(
`确认生成 ${selectedRows.value.length} 条记录的出账清单吗?`,
'提示',
{
type: 'warning'
}
).then(async () => {
console.log('开始生成出账清单')
const response = await downloadPolicyFortuneAccount({
fortuneBizIdList: selectedRows.value.map(item => item.fortuneBizId)
})
console.log(response)
// 调用下载API
if(response){
// 处理下载响应
const blob = response instanceof Blob ? response : new Blob([response], { type: 'application/vnd.ms-excel;charset=utf-8' })
// 创建Blob对象,指定正确的MIME类型
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
// 不需要指定文件名,浏览器会自动使用默认文件名
link.download = `出账清单_${new Date().getTime()}.xlsx`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
}else{
ElMessage.error('下载失败')
}
selectedRows.value = []
}).catch((error) => {
if (error !== 'cancel') {
console.log('生成出账清单出错:', error)
}
})
}
// 提交表单
const submitForm = async () => {
try {
// 表单验证
if (!formData.policyNo || !formData.company || !formData.billingItem) {
ElMessage.warning('请填写必填字段')
return
}
// 调用保存API
// if (formData.id) {
// await api.updateBilling(formData)
// } else {
// await api.createBilling(formData)
// }
ElMessage.success('保存成功')
dialogVisible.value = false
getList()
} catch (error) {
ElMessage.error('保存失败')
}
}
// 格式化金额
const formatCurrency = (amount) => {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
}).format(amount)
}
// 获取状态标签类型
const getStatusType = (status) => {
const types = {
0: 'warning',
1: 'success',
2: 'info'
}
return types[status] || 'info'
}
import { listType } from '@/api/system/dict/type'
const dictLists = ref([])
// 获取出账状态字典值
const getDictLists = () => {
return new Promise((resolve, reject) => {
listType({typeList: ['csf_fortune_status']}).then(res => {
if (res.code === 200 && res.data) {
const dictData = res.data.find(item => item.dictType === 'csf_fortune_status');
dictLists.value = dictData?.dictItemList || [];
console.log('获取到的字典数据:', dictLists.value);
resolve(dictLists.value);
} else {
dictLists.value = [];
resolve([]);
}
}).catch(error => {
console.error('获取状态列表失败:', error);
dictLists.value = [];
reject(error);
});
});
}
// 返回数据中状态需要转换为字典值
const convertStatusToDict = (status) => {
const dictItem = dictLists.value.find(item => item.itemValue == status);
return dictItem?.itemLabel ?? status;
}
// 初始化
onMounted(() => {
getDictLists()
getList()
})
</script>
<style scoped>
.financial-billing-page {
padding: 20px;
}
.search-card {
margin-bottom: 20px;
}
.operation-card {
margin-bottom: 20px;
}
.el-table {
margin-bottom: 20px;
}
.el-pagination {
justify-content: flex-end;
margin-top: 20px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.financial-billing-page {
padding: 10px;
}
.search-card {
margin-bottom: 15px;
}
.operation-card {
margin-bottom: 15px;
}
.el-col {
margin-bottom: 10px;
}
}
</style>
\ No newline at end of file
<template>
<div class="financial-income-page">
<!-- 查询区域 -->
<el-card class="search-card">
<el-form :model="searchForm" label-width="80px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="保单号">
<el-input v-model="searchForm.policyNo" placeholder="请输入保单号" clearable />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="对账公司">
<el-select v-model="searchForm.reconciliationCompany" placeholder="请选择对账公司" clearable>
<el-option
v-for="item in companyOptions"
:key="item.value"
:label="item.label"
:value="item.label"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="入账日期">
<el-date-picker
v-model="searchForm.incomeDateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="4">
<div class="search-buttons">
<el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
<el-button :icon="RefreshLeft" @click="resetForm">重置</el-button>
</div>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- 操作区域 -->
<div class="action-area">
<el-button type="primary" :icon="Plus" @click="handleAdd">新增入账</el-button>
<!-- 导入区域 -->
<el-card class="import-card">
<div class="import-content">
<div class="import-actions">
<FileUpload :fileType="['xlsx', 'xls']"
:action="'/csf/api/commission/upload/excel'"
@uploadEnd = 'getUploadFileFunc'
:responseType="'onlyStatus'"
/>
<el-button text @click="downloadTemplate" size="small" class="download-template-btn">
下载模板
</el-button>
</div>
</div>
</el-card>
</div>
<!-- 列表区域 -->
<el-card class="table-card">
<el-table
v-loading="tableLoading"
:data="tableData"
border
style="width: 100%"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="policyNo" label="保单号" min-width="120" align="center" />
<el-table-column prop="reconciliationCompany" label="对账公司" min-width="120" align="center" />
<el-table-column prop="commissionPeriod" label="当前入账期数" min-width="100" align="center" />
<el-table-column prop="totalPeriod" label="预计总期数" min-width="100" align="center" />
<el-table-column prop="commissionName" label="入账项目" min-width="120" align="center" />
<el-table-column prop="amount" label="入账金额" min-width="100" align="center" />
<el-table-column prop="currency" label="入账币种" min-width="80" align="center" />
<el-table-column prop="commissionDate" label="入账日期" min-width="120" align="center" />
<el-table-column prop="status" label="比对状态" min-width="100" align="center">
<template #default="scope">
<span>{{convertStatusToDict(scope.row.status) }}</span>
</template>
</el-table-column>>
<el-table-column prop="remark" label="备注" min-width="150" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="180" align="center" fixed="right">
<template #default="scope">
<el-button text size="small" type="primary" @click="handleEdit(scope.row)">编辑</el-button>
<el-button text size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination">
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
<!-- 底部确认按钮 -->
<div class="footer-actions" v-if="selectedRows.length > 0">
<el-button
type="primary"
size="large"
@click="handleGenerateBilling"
:disabled="selectedRows.length === 0"
>
确认生成可出账记录
</el-button>
</div>
<!-- 编辑对话框 -->
<!-- 入账记录对话框(新增/编辑共用) -->
<el-dialog
v-model="incomeDialogVisible"
:title="incomeDialogTitle"
width="600px"
:before-close="handleCloseDialog"
>
<el-form :model="incomeForm" label-width="100px" :rules="incomeFormRules" ref="incomeFormRef">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="保单号" prop="policyNo">
<el-input v-model="incomeForm.policyNo" placeholder="请输入保单号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="对账公司" prop="reconciliationCompany">
<el-select v-model="incomeForm.reconciliationCompany" placeholder="请选择对账公司">
<el-option
v-for="item in companyOptions"
:key="item.value"
:label="item.label"
:value="item.label"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当前期数" prop="commissionPeriod">
<el-input-number v-model="incomeForm.commissionPeriod" :min="1" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="总期数" prop="totalPeriod">
<el-input-number v-model="incomeForm.totalPeriod" :min="1" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入账项目" prop="commissionName">
<el-input v-model="incomeForm.commissionName" placeholder="请输入入账项目" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入账金额" prop="amount">
<el-input v-model="incomeForm.amount" placeholder="请输入金额" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="币种" prop="currency">
<el-select v-model="incomeForm.currency" placeholder="请选择币种">
<el-option
v-for="item in currencyTypeOptions"
:key="item.itemValue"
:label="item.itemLabel"
:value="item.itemValue"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="入账日期" prop="commissionDate">
<el-date-picker
v-model="incomeForm.commissionDate"
type="date"
placeholder="选择日期"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input
v-model="incomeForm.remark"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="incomeDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitIncomeForm" :loading="incomeLoading">
{{ isEditMode ? '更新' : '新增' }}
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { Search, RefreshLeft, UploadFilled, Plus, Document } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import * as XLSX from 'xlsx'
// 导入有关来佣的接口
import { getPolicyCommissionList, downloadPolicyFortune ,generateCommissionRecord,updatePolicyCommission} from "@/api/financial/commission"
import FileUpload from "@/components/FileUpload/index"
import { useRouter } from 'vue-router'
const router = useRouter()
import { listType } from '@/api/system/dict/type'
// 通过dictType=csf_commission_status获取比对状态字典值,获取对象中的dictItemList
const dictLists = ref([]);
const currencyTypeOptions = ref([])
const getLists = () => {
return new Promise((resolve, reject) => {
listType({typeList: ['csf_commission_status','bx_currency_type']}).then(res => {
if (res.code === 200 && res.data) {
const dictData = res.data.find(item => item.dictType === 'csf_commission_status');
dictLists.value = dictData?.dictItemList || [];
// 处理币种字典值
const currencyData = res.data.find(item => item.dictType === 'bx_currency_type');
currencyTypeOptions.value = currencyData?.dictItemList || [];
console.log('获取到的币种字典数据:', currencyTypeOptions.value);
resolve(dictLists.value,currencyTypeOptions.value);
} else {
dictLists.value = [];
currencyTypeOptions.value = [];
resolve([],[]);
}
}).catch(error => {
console.error('获取状态列表失败:', error);
dictLists.value = [];
reject(error);
});
});
}
// 返回数据中状态需要转换为字典值
const convertStatusToDict = (status) => {
const dictItem = dictLists.value.find(item => item.itemValue == status);
return dictItem?.itemLabel ?? status;
}
// 增加通过表格排序,排序字段sortField,升降序sortOrder
const handleSortChange = (column) => {
pagination.sortField = column.prop
pagination.sortOrder = column.order === 'ascending' ? 'ascend' : 'descend'
fetchTableData()
}
// 搜索表单数据
const searchForm = reactive({
policyNo: '',
reconciliationCompany: '',
incomeDateRange: []
})
// 对账公司选项
const companyOptions = ref([])
// 获取对账公司列表
import { getReconciliationCompanyList } from '@/api/financial/commission'
const fetchReconciliationCompanyList = async () => {
try {
const response = await getReconciliationCompanyList({
pageNo: 1,
pageSize: 1000
})
if (response.code === 200) {
companyOptions.value = response.data.records.map(item => ({
label: item.companyName,
value: item.reconciliationCompanyBizId
}))
} else {
ElMessage.error(response.msg)
}
} catch (error) {
ElMessage.error('获取对账公司列表失败')
}
}
// 表格数据
const tableData = ref([])
const tableLoading = ref(false)
const selectedRows = ref([])
// 分页数据
const pagination = reactive({
currentPage: 1,
pageSize: 10,
total: 0
})
onMounted(() => {
// 获取字典值后,再调用接口获取来佣列表
initialData();
})
const initialData = async () => {
await getLists();
fetchTableData();
fetchReconciliationCompanyList()
}
const getUploadFileFunc = (data) => {
console.log(data)
if(data===200){
ElMessage.success('上传成功');
fetchTableData()
}else{
ElMessage.error('上传失败')
}
}
// 获取列表数据
// 调用接口获取来佣列表
const fetchTableData = async () => {
tableLoading.value = true
try {
// 调用接口获取来佣列表
const res = await getPolicyCommissionList({
pageNo: pagination.currentPage,
pageSize: pagination.pageSize,
policyNo: searchForm.policyNo,
reconciliationCompany: searchForm.reconciliationCompany,
startTime: searchForm.incomeDateRange[0] ? `${searchForm.incomeDateRange[0]} 00:00:00` : '',
endTime: searchForm.incomeDateRange[1] ? `${searchForm.incomeDateRange[1]} 23:59:59` : ''
})
tableData.value = res.data.records
pagination.total = res.data.total
} catch (error) {
console.error('获取数据失败:', error)
ElMessage.error('获取数据失败')
} finally {
tableLoading.value = false
}
}
// 处理查询
const handleSearch = () => {
pagination.currentPage = 1
fetchTableData()
ElMessage.success('查询成功')
}
// 重置表单
const resetForm = () => {
searchForm.policyNo = ''
searchForm.reconciliationCompany = ''
searchForm.incomeDateRange = []
ElMessage.success('已重置筛选条件')
}
// 处理分页大小变化
const handleSizeChange = (val) => {
pagination.pageSize = val
fetchTableData()
}
// 处理分页页码变化
const handleCurrentChange = (val) => {
pagination.currentPage = val
fetchTableData()
}
// 处理表格选择变化
const handleSelectionChange = (rows) => {
selectedRows.value = rows
}
// 下载模板
const downloadTemplate = () => {
// 下载地址
const templateUrl = 'https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/xlsx/2025/10/14/54ce715eabab4f1abd8652ba0fca0c51.xlsx'
// 修改下载文件名
const fileName = '对账单导入模板.xlsx'
// 下载文件
window.open(templateUrl, '_blank', `download=${fileName}-${new Date().getTime()}`)
}
// 新增入账
const handleAdd = () => {
incomeDialogTitle.value = '新增入账记录'
isEditMode.value = false
// 重置表单数据
Object.assign(incomeForm, {
commissionBizId: '',
policyNo: '',
reconciliationCompany: '',
commissionPeriod: 1,
totalPeriod: 1,
commissionName: '',
amount: 0,
currency: 'HKD',
commissionDate: '',
remark: ''
})
// 重置表单验证
if (incomeFormRef.value) {
incomeFormRef.value.clearValidate()
}
incomeDialogVisible.value = true
}
import { deletePolicyCommission } from '@/api/financial/commission'
// 删除
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除保单 ${row.policyNo} 的入账记录吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
// 调用删除接口
deletePolicyCommission({
commissionBizId: row.commissionBizId
}).then((res) => {
if(res.code === 200){
ElMessage.success('删除成功')
fetchTableData()
}else{
ElMessage.error('删除失败')
}
}).catch(() => {
ElMessage.error('删除失败')
})
})
}
// 生成可出账记录
// 调用generateCommissionRecord接口
const handleGenerateBilling = async () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请选择要生成可出账记录的记录')
return
}
try {
console.log(selectedRows.value)
await generateCommissionRecord({
commissionBizIdList: selectedRows.value.map(row => row.commissionBizId)
})
ElMessage.success('生成成功')
// 这里可以添加路由跳转到 financialBilling 组件
// router.push('/financialCenter/financial/billing')
} catch (error) {
console.error('生成失败:', error)
ElMessage.error('生成失败')
}
}
// 入账记录对话框相关
const incomeDialogVisible = ref(false)
const incomeDialogTitle = ref('入账记录')
const incomeLoading = ref(false)
const isEditMode = ref(false)
const incomeFormRef = ref()
// 入账表单数据
const incomeForm = reactive({
commissionBizId: '',
policyNo: '',
reconciliationCompany: '',
commissionPeriod: 1,
totalPeriod: 1,
commissionName: '',
amount: 0,
currency: 'CNY',
commissionDate: '',
remark: ''
})
// 表单验证规则
const incomeFormRules = {
policyNo: [{ required: true, message: '请输入保单号', trigger: 'blur' }],
reconciliationCompany: [{ required: true, message: '请选择对账公司', trigger: 'change' }],
commissionName: [{ required: true, message: '请输入入账项目', trigger: 'blur' }],
amount: [{ required: true, message: '请输入金额', trigger: 'blur' }],
commissionDate: [{ required: true, message: '请选择入账日期', trigger: 'change' }]
}
// 编辑入账记录
const handleEdit = (row) => {
incomeDialogTitle.value = `编辑入账记录 - ${row.policyNo}`
isEditMode.value = true
// 填充表单数据
Object.assign(incomeForm, {
commissionBizId: row.commissionBizId,
policyNo: row.policyNo,
reconciliationCompany: row.reconciliationCompany,
commissionPeriod: row.commissionPeriod || 1,
totalPeriod: row.totalPeriod || 1,
commissionName: row.commissionName,
amount: row.amount,
currency: row.currency || 'CNY',
commissionDate: row.commissionDate,
remark: row.remark || ''
})
// 重置表单验证
if (incomeFormRef.value) {
incomeFormRef.value.clearValidate()
}
incomeDialogVisible.value = true
}
// 提交入账表单(新增/编辑共用)
import { addPolicyCommission } from '@/api/financial/commission'
const submitIncomeForm = async () => {
if (!incomeFormRef.value) {
ElMessage.error('请先初始化表单')
return
}
try {
// 表单验证
const valid = await incomeFormRef.value.validate()
if (!valid) {
ElMessage.warning('请完善表单信息')
return
}
incomeLoading.value = true
if (isEditMode.value) {
// 编辑模式 - 调用更新接口
await updatePolicyCommission(incomeForm)
ElMessage.success('更新成功')
} else {
// 新增模式 - 调用新增接口
// 注意:这里需要根据实际的新增API进行调整
await addPolicyCommission(incomeForm)
ElMessage.success('新增成功')
}
incomeDialogVisible.value = false
fetchTableData() // 刷新表格数据
} catch (error) {
if (error && error.errorFields) {
ElMessage.warning('请完善表单信息')
} else {
console.error(isEditMode.value ? '更新失败:' : '新增失败:', error)
ElMessage.error(isEditMode.value ? '更新失败' : '新增失败')
}
} finally {
incomeLoading.value = false
}
}
// 关闭对话框处理
const handleCloseDialog = (done) => {
if (incomeLoading.value) {
ElMessage.info('正在保存,请稍候...')
return
}
ElMessageBox.confirm('确定要关闭吗?未保存的修改将会丢失', '提示', {
type: 'warning'
}).then(() => {
done()
}).catch(() => {})
}
</script>
<style scoped>
.financial-income-page {
padding: 20px;
max-width: 1600px;
margin: 0 auto;
}
.search-card {
margin-bottom: 20px;
padding: 15px 20px;
}
.search-buttons {
display: flex;
gap: 10px;
align-items: center;
height: 32px;
}
.action-area {
margin-bottom: 20px;
display: flex;
flex-direction: column;
gap: 15px;
}
.import-card {
padding: 20px;
background-color: #f6ffed;
border: 1px solid #b7eb8f;
}
.import-content {
display: flex;
flex-direction: column;
gap: 15px;
}
.import-actions {
display: flex;
align-items: center;
gap: 10px;
}
.upload-excel {
display: inline-block;
}
/* 文件信息显示样式 */
.file-info {
margin-top: 15px;
padding: 12px 16px;
background-color: #f5f7fa;
border-radius: 4px;
border: 1px solid #e4e7ed;
display: flex;
align-items: center;
justify-content: space-between;
}
.file-info-content {
display: flex;
align-items: center;
gap: 8px;
}
.file-info-content .el-icon {
color: #409eff;
font-size: 16px;
}
.file-name {
font-weight: 500;
color: #303133;
}
.file-size {
color: #909399;
font-size: 12px;
}
.confirm-import-btn {
margin-left: 12px;
}
/* 下载模板按钮样式 */
.download-template-btn {
color: #409eff;
font-size: 12px;
padding: 8px 12px;
}
.download-template-btn:hover {
background-color: #ecf5ff;
}
.table-card {
padding: 15px 20px;
}
.pagination {
margin-top: 15px;
text-align: right;
}
.footer-actions {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.import-actions {
flex-wrap: wrap;
}
.download-template-btn {
margin-top: 10px;
}
.file-info {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.confirm-import-btn {
margin-left: 0;
align-self: flex-end;
}
}
@media (max-width: 992px) {
.search-card .el-col {
margin-bottom: 15px;
}
.search-buttons {
justify-content: flex-start;
}
}
@media (max-width: 768px) {
.search-card .el-col {
width: 100%;
}
.import-actions {
flex-direction: column;
align-items: flex-start;
}
.download-template-btn {
margin-top: 10px;
}
}
</style>
\ No newline at end of file
<template>
<div class="financial-salary-page">
<!-- 查询区域 -->
<el-card class="search-card">
<el-form :model="queryParams" label-width="80px">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="保单号">
<el-input
v-model="queryParams.policyNo"
placeholder="请输入保单号"
clearable
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="转介人">
<el-input
v-model="queryParams.broker"
placeholder="请输入转介人"
clearable
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item label="出账日期">
<el-date-picker
v-model="queryParams.accountDate"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- 操作区域 -->
<el-card class="operation-card">
<el-row :gutter="10">
<el-col :span="12">
<el-button type="primary" @click="handleCreate">新建薪资记录</el-button>
<el-button @click="handleImport">批量导入</el-button>
</el-col>
<el-col :span="12" style="text-align: right;">
<el-button
type="success"
:disabled="selectedRows.length === 0"
@click="completeBilling"
>
完成出账
</el-button>
</el-col>
</el-row>
</el-card>
<!-- 数据表格 -->
<el-card>
<el-table
:data="tableData"
@selection-change="handleSelectionChange"
v-loading="loading"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="broker" label="转介人" min-width="120" />
<el-table-column prop="team" label="所属团队" min-width="120" />
<el-table-column prop="amount" label="出账金额" width="120">
<template #default="{ row }">
{{ formatCurrency(row.amount) }}
</template>
</el-table-column>
<el-table-column prop="currency" label="出账币种" width="100" />
<el-table-column prop="status" label="出账状态" width="100">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)">
{{convertStatusToDict(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip />
<el-table-column label="操作" width="300" fixed="right">
<template #default="{ row }">
<el-button size="small" @click="handleEdit(row)">修改</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
<!-- <el-button size="small" type="info" @click="generateSalarySlip(row)">生成薪资单</el-button> -->
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="queryParams.pageNo"
v-model:page-size="queryParams.pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="getList"
@current-change="getList"
/>
</el-card>
<!-- 新建薪资记录对话框 -->
<el-dialog
title="新建薪资记录"
v-model="createDialogVisible"
width="500px"
>
<el-form :model="createFormData" label-width="100px">
<el-form-item label="转介人" required>
<el-select v-model="createFormData.referrer" placeholder="请选择转介人">
<el-option
v-for="item in referrerOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="出账金额" required>
<el-input-number
v-model="createFormData.amount"
:min="0"
:precision="2"
placeholder="请输入出账金额"
/>
</el-form-item>
<el-form-item label="出账项目" required>
<el-input
v-model="createFormData.billingItem"
placeholder="请输入出账项目"
/>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="createFormData.remark"
type="textarea"
:rows="3"
placeholder="请输入备注信息"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="createDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitCreateForm">确认</el-button>
</template>
</el-dialog>
<!-- 出账完成弹窗 -->
<el-dialog
title="出账完成"
v-model="billingCompleteDialogVisible"
width="400px"
>
<div style="text-align: center;">
<el-icon size="48" color="#67C23A">
<SuccessFilled />
</el-icon>
<p style="margin-top: 16px; font-size: 16px;">出账操作已完成!</p>
</div>
<template #footer>
<!-- <el-button type="primary" @click="generateSalarySlips">生成薪资单</el-button> -->
<el-button @click="billingCompleteDialogVisible = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { SuccessFilled } from '@element-plus/icons-vue'
import { getReferrerFortuneList,updatePolicyFortuneStatus } from '@/api/financial/commission'
// 查询参数
const queryParams = reactive({
broker: '',
accountDate: [],
accountDateStart: '',
accountDateEnd: '',
pageNo: 1,
pageSize: 10,
sortField: '',
sortOrder: 'desc'
})
// 表格数据
const tableData = ref([])
const total = ref(0)
const loading = ref(false)
const selectedRows = ref([])
// 对话框相关
const createDialogVisible = ref(false)
const billingCompleteDialogVisible = ref(false)
// 新建表单数据
const createFormData = reactive({
referrer: '',
amount: 0,
billingItem: '',
remark: ''
})
// 转介人选项
const referrerOptions = [
{ label: '张三', value: 'zhangsan' },
{ label: '李四', value: 'lisi' },
{ label: '王五', value: 'wangwu' },
{ label: '赵六', value: 'zhaoliu' }
]
// 初始化
onMounted(() => {
getDictLists()
getList()
})
// 获取数据列表
const getList = async () => {
loading.value = true
try {
if (queryParams.accountDate.length > 0) {
queryParams.accountDateStart = queryParams.accountDate[0]
queryParams.accountDateEnd = queryParams.accountDate[1]
}
const response = await getReferrerFortuneList(queryParams)
tableData.value = response.data.records
total.value = response.data.total
loading.value = false
} catch (error) {
loading.value = false
// ElMessage.error('获取数据失败')
}
}
const getStatusType = (status) => {
const types = {
0: 'warning',
1: 'success',
2: 'info'
}
return types[status] || 'info'
}
// 查询
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
// 重置查询
const resetQuery = () => {
Object.assign(queryParams, {
broker: '',
accountDateStart: '',
accountDateEnd: '',
pageNo: 1,
pageSize: 10,
sortField: '',
sortOrder: 'desc'
})
getList()
}
// 选择行变化
const handleSelectionChange = (selection) => {
selectedRows.value = selection
}
// 新建薪资记录
const handleCreate = () => {
Object.assign(createFormData, {
referrer: '',
amount: 0,
billingItem: '',
remark: ''
})
createDialogVisible.value = true
}
// 编辑薪资记录
const handleEdit = (row) => {
// 这里可以打开编辑对话框
ElMessage.info(`编辑转介人:${row.referrer}`)
}
import { addPolicyFortuneAccount } from '@/api/financial/commission'
const deletePolicyFortuneO = async (fortuneAccountBizId) => {
try {
await deletePolicyFortune({ fortuneAccountBizId })
} catch (error) {
ElMessage.error('删除出账失败')
}
}
// 删除薪资记录
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确认删除这条薪资记录吗?', '提示', {
type: 'warning'
})
await deletePolicyFortuneO(row.fortuneAccountBizId)
ElMessage.success('删除成功')
getList()
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('删除失败')
}
}
}
// 批量导入
const handleImport = () => {
ElMessage.info('批量导入功能待实现')
}
// 完成出账
const completeBilling = async () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要出账的记录')
return
}
try {
await ElMessageBox.confirm(
`确认完成 ${selectedRows.value.length} 条记录的出账操作吗?`,
'提示',
{ type: 'warning' }
)
console.log(selectedRows.value)
const arr = selectedRows.value.map(item => item.fortuneAccountBizId)
// 调用出账API
await fetchCompletePolicyFortune(arr)
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('出账操作失败')
}
}
}
// 生成薪资单(单条)
const generateSalarySlip = (row) => {
// 调用生成薪资单API
// await api.generateSalarySlip(row.id)
ElMessage.success(`已为 ${row.referrer} 生成薪资单`)
}
// 生成薪资单(批量)
const generateSalarySlips = () => {
// 调用批量生成薪资单API
// await api.generateSalarySlips(selectedRows.value.map(item => item.id))
ElMessage.success(`已为 ${selectedRows.value.length} 条记录生成薪资单`)
billingCompleteDialogVisible.value = false
selectedRows.value = []
}
// 提交新建表单
const submitCreateForm = async () => {
try {
// 表单验证
if (!createFormData.referrer || !createFormData.amount || !createFormData.billingItem) {
ElMessage.warning('请填写必填字段')
return
}
// 调用保存API
// await api.createSalary(createFormData)
ElMessage.success('新建成功')
createDialogVisible.value = false
getList()
} catch (error) {
ElMessage.error('新建失败')
}
}
// 格式化金额
const formatCurrency = (amount) => {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
}).format(amount)
}
import { listType } from '@/api/system/dict/type'
const dictLists = ref([])
// 获取出账状态字典值
const getDictLists = () => {
return new Promise((resolve, reject) => {
listType({typeList: ['csf_fortune_status']}).then(res => {
if (res.code === 200 && res.data) {
const dictData = res.data.find(item => item.dictType === 'csf_fortune_status');
dictLists.value = dictData?.dictItemList || [];
console.log('获取到的字典数据:', dictLists.value);
resolve(dictLists.value);
} else {
dictLists.value = [];
resolve([]);
}
}).catch(error => {
console.error('获取状态列表失败:', error);
dictLists.value = [];
reject(error);
});
});
}
// 返回数据中状态需要转换为字典值
const convertStatusToDict = (status) => {
const dictItem = dictLists.value.find(item => item.itemValue == status);
return dictItem?.itemLabel ?? status;
}
// 更新出账状态
const updateStatus = async (row) => {
try {
const res = await updatePolicyFortuneStatus({
fortuneBizIdList: row,
status: 2
});
console.log(res)
if (res.code === 200) {
// 显示完成弹窗
billingCompleteDialogVisible.value = true
getList()
} else {
ElMessage.error(res.msg)
}
} catch (error) {
}
}
// 完成出账
import { completePolicyFortune } from '@/api/financial/commission'
const fetchCompletePolicyFortune = async (row) => {
try {
const res = await completePolicyFortune({
fortuneAccountBizIdList: row
});
console.log(res)
if (res.code === 200) {
// 显示完成弹窗
billingCompleteDialogVisible.value = true
getList()
} else {
ElMessage.error(res.msg)
}
} catch (error) {
}
}
// 删除出账
import { deletePolicyFortune } from '@/api/financial/commission'
// 删除出账
const deleteFortune = async (row) => {
try {
const res = await deletePolicyFortune({
fortuneAccountBizId: row
});
console.log(res)
if (res.code === 200) {
// 显示完成弹窗
billingCompleteDialogVisible.value = true
} else {
ElMessage.error(res.msg)
}
} catch (error) {
}
}
</script>
<style scoped>
.financial-salary-page {
padding: 20px;
}
.search-card {
margin-bottom: 20px;
}
.operation-card {
margin-bottom: 20px;
}
.el-table {
margin-bottom: 20px;
}
.el-pagination {
justify-content: flex-end;
margin-top: 20px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.financial-salary-page {
padding: 10px;
}
.search-card {
margin-bottom: 15px;
}
.operation-card {
margin-bottom: 15px;
}
.el-col {
margin-bottom: 10px;
}
}
</style>
\ No newline at end of file
<template>
<div class="app-container home">
<el-row :gutter="20">
<el-col :sm="24" :lg="12" style="padding-left: 20px">
<h2>若依后台管理框架</h2>
<p>
一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了若依管理系统,她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA等等,当然,您也可以对她进行深度定制,以做出更强系统。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
</p>
<p>
<b>当前版本:</b> <span>v{{ version }}</span>
</p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>
<p>
<el-button
type="primary"
icon="Cloudy"
plain
@click="goTarget('https://gitee.com/y_project/RuoYi-Vue')"
>访问码云</el-button
>
<el-button icon="HomeFilled" plain @click="goTarget('http://ruoyi.vip')"
>访问主页</el-button
>
</p>
</el-col>
<el-col :sm="24" :lg="12" style="padding-left: 50px">
<el-row>
<el-col :span="12">
<h2>技术选型</h2>
</el-col>
</el-row>
<el-row>
<el-col :span="6">
<h4>后端技术</h4>
<ul>
<li>SpringBoot</li>
<li>Spring Security</li>
<li>JWT</li>
<li>MyBatis</li>
<li>Druid</li>
<li>Fastjson</li>
<li>...</li>
</ul>
</el-col>
<el-col :span="6">
<h4>前端技术</h4>
<ul>
<li>Vue</li>
<li>Vuex</li>
<li>Element-ui</li>
<li>Axios</li>
<li>Sass</li>
<li>Quill</li>
<li>...</li>
</ul>
</el-col>
</el-row>
</el-col>
</el-row>
<el-divider />
<el-row :gutter="20">
<el-col :xs="24" :sm="24" :md="12" :lg="8">
<el-card class="update-log">
<template v-slot:header>
<div class="clearfix">
<span>联系信息</span>
</div>
</template>
<div class="body">
<p>
<i class="el-icon-s-promotion"></i> 官网:<el-link
href="http://www.ruoyi.vip"
target="_blank"
>http://www.ruoyi.vip</el-link
>
</p>
<p>
<i class="el-icon-user-solid"></i> QQ群:<s> 满937441 </s> <s> 满887144332 </s>
<s> 满180251782 </s> <s> 满104180207 </s> <s> 满186866453 </s> <s> 满201396349 </s>
<s> 满101456076 </s> <s> 满101539465 </s> <s> 满264312783 </s> <s> 满167385320 </s>
<s> 满104748341 </s> <s> 满160110482 </s> <s> 满170801498 </s> <s> 满108482800 </s>
<s> 满101046199 </s> <s> 满136919097 </s> <s> 满143961921 </s> <s> 满174951577 </s>
<s> 满161281055 </s> <s> 满138988063 </s> <s> 满151450850 </s> <s> 满224622315 </s>
<s> 满287842588 </s> <s> 满187944233 </s> <s> 满228578329 </s>
<a
href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766"
target="_blank"
>191164766</a
>
</p>
<p><i class="el-icon-chat-dot-round"></i> 微信:<a href="javascript:;">/ *若依</a></p>
<p>
<i class="el-icon-money"></i> 支付宝:<a href="javascript:;" class="支付宝信息"
>/ *若依</a
>
</p>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8">
<el-card class="update-log">
<template v-slot:header>
<div class="clearfix">
<span>更新日志</span>
</div>
</template>
<el-collapse accordion>
<el-collapse-item title="v3.9.0 - 2025-05-28">
<ol>
<li>优化菜单搜索查询页</li>
<li>导航栏显示昵称&设置</li>
<li>菜单管理新增路由名称</li>
<li>添加底部版权信息&开关</li>
<li>分配角色禁用不允许勾选</li>
<li>Excel导入导出支持多图片</li>
<li>添加页签图标显示开关功能</li>
<li>上传组件新增拖动排序属性</li>
<li>显隐列组件支持全选/全不选</li>
<li>初始密码支持自定义修改策略</li>
<li>账号密码支持自定义更新周期</li>
<li>代码生成列表支持按时间排序</li>
<li>支持富文本复制粘贴图片上传至url</li>
<li>支持文件&图片组件自定义地址&参数</li>
<li>升级tomcat到最新版本9.0.105</li>
<li>升级oshi到最新版本6.8.1</li>
<li>升级fastjson到最新版2.0.57</li>
<li>升级commons.io到最新版本2.19.0</li>
<li>package.json移除runjs依赖</li>
<li>package.json移除eslint依赖</li>
<li>package.json移除vue-meta依赖</li>
<li>修复代码生成主子表校验必填失效问题</li>
<li>优化前端树结构性能问题</li>
<li>优化前端处理路由函数代码</li>
<li>优化文件上传组件新增类型</li>
<li>优化顶部菜单搜索栏为多层级显示</li>
<li>优化文件&图片上传组件新增disabled属性</li>
<li>优化空指针异常时无法获取错误信息问题</li>
<li>优化定时任务字符包含多个括号导致数据错误</li>
<li>优化登录&注册页表头使用VUE_APP_TITLE配置值</li>
<li>优化导出Excel日期格式双击离开后与设定的格式不一致问题</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.9 - 2024-12-30">
<ol>
<li>用户管理支持分栏拖动</li>
<li>修改主题样式本地读取</li>
<li>用户头像http(s)链接支持</li>
<li>用户管理过滤掉已禁用部门</li>
<li>支持自定义显示Excel属性列</li>
<li>操作日志记录DELETE请求参数</li>
<li>白名单支持对通配符路径匹配</li>
<li>校检文件名是否包含特殊字符</li>
<li>代码生成创建表屏蔽违规的字符</li>
<li>菜单面包屑导航支持多层级显示</li>
<li>Excel注解支持wrapText是否允许内容换行</li>
<li>代码生成新增配置是否允许文件覆盖到本地</li>
<li>修复角色禁用权限不失效问题</li>
<li>修复代码生成上级菜单显示问题</li>
<li>修复导出子列表对象只能在最后的问题</li>
<li>修复TopNav无法正确获取active的问题</li>
<li>修复默认关闭Tags-Views内链页面打不开</li>
<li>升级oshi到最新版本6.6.5</li>
<li>升级tomcat到最新版本9.0.96</li>
<li>升级fastjson到最新版2.0.53</li>
<li>升级logback到最新版本1.2.13</li>
<li>升级spring-framework到最新版本5.3.39</li>
<li>升级quill到最新版本2.0.2</li>
<li>升级axios到最新版本0.28.1</li>
<li>优化身份证脱敏正则</li>
<li>优化权限更新后同步缓存</li>
<li>优化查询时间范围日期格式</li>
<li>优化参数键值更换为多行文本</li>
<li>优化导入带标题文件关闭清理</li>
<li>优化上传图片带域名不增加前缀</li>
<li>优化特殊字符密码修改失败问题</li>
<li>优化无用户编号不校验数据权限</li>
<li>优化TopNav内链菜单点击没有高亮</li>
<li>优化菜单管理切换Mini布局错乱问题</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.8 - 2024-06-30">
<ol>
<li>菜单管理新增路由名称</li>
<li>新增数据脱敏过滤注解</li>
<li>用户密码新增非法字符验证</li>
<li>限制用户操作数据权限范围</li>
<li>代码生成新增创建表结构功能</li>
<li>定时任务白名单配置范围缩小</li>
<li>优化代码生成主子表关联查询方式</li>
<li>Excel注解新增属性comboReadDict</li>
<li>Excel注解ColumnType类型新增文本</li>
<li>新增国际化资源文件配置</li>
<li>升级oshi到最新版本6.6.1</li>
<li>升级druid到最新版本1.2.23</li>
<li>升级core-js到最新版本3.37.1</li>
<li>更新HttpUtils中的User-Agent</li>
<li>更新compressionPlugin到6.1.2以兼容node18+</li>
<li>升级spring-security到安全版本,防止漏洞风险</li>
<li>升级spring-framework到安全版本,防止漏洞风险</li>
<li>优化自定义XSS注解匹配方式</li>
<li>优化缓存监控键名列表排序显示</li>
<li>优化定时任务日志默认按时间排序</li>
<li>优化默认文件大小超过2G无效的问题</li>
<li>优化查表特殊字符使用反斜杠进行转义</li>
<li>优化定时任务cron表达式小时配置显示错误问题</li>
<li>优化多个自定数据权限使用in查询,避免多次拼接</li>
<li>优化导入Excel时设置dictType属性重复查缓存问题</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.7 - 2023-12-08">
<ol>
<li>操作日志记录部门名称</li>
<li>全局数据存储用户编号</li>
<li>新增编程式判断资源访问权限</li>
<li>操作日志列表新增IP地址查询</li>
<li>定时任务新增页去除状态选项</li>
<li>代码生成支持选择前端模板类型</li>
<li>显隐列组件支持复选框弹出类型</li>
<li>通用排序属性orderBy参数限制长度</li>
<li>Excel自定义数据处理器增加单元格/工作簿对象</li>
<li>升级oshi到最新版本6.4.8</li>
<li>升级druid到最新版本1.2.20</li>
<li>升级fastjson到最新版2.0.43</li>
<li>升级pagehelper到最新版1.4.7</li>
<li>升级commons.io到最新版本2.13.0</li>
<li>升级element-ui到最新版本2.15.14</li>
<li>修复五级路由缓存无效问题</li>
<li>修复外链带端口出现的异常</li>
<li>修复树模板父级编码变量错误</li>
<li>修复字典表详情页面搜索问题</li>
<li>修复内链iframe没有传递参数问题</li>
<li>修复自定义字典样式不生效的问题</li>
<li>修复字典缓存删除方法参数错误问题</li>
<li>修复Excel导入数据临时文件无法删除问题</li>
<li>修复未登录带参数访问成功后参数丢失问题</li>
<li>修复HeaderSearch组件跳转query参数丢失问题</li>
<li>修复代码生成导入后必填项与数据库不匹配问题</li>
<li>修复Excels导入时无法获取到dictType字典值问题</li>
<li>优化下载zip方法新增遮罩层</li>
<li>优化头像上传参数新增文件名称</li>
<li>优化字典标签支持自定义分隔符</li>
<li>优化菜单管理类型为按钮状态可选</li>
<li>优化前端防重复提交数据大小限制</li>
<li>优化TopNav菜单没有图标svg不显示</li>
<li>优化数字金额大写转换精度丢失问题</li>
<li>优化富文本Editor组件检验图片格式</li>
<li>优化页签在Firefox浏览器被遮挡的问题</li>
<li>优化个人中心/基本资料修改时数据显示问题</li>
<li>优化缓存监控图表支持跟随屏幕大小自适应调整</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.6 - 2023-06-30">
<ol>
<li>支持登录IP黑名单限制</li>
<li>新增监控页面图标显示</li>
<li>操作日志新增消耗时间属性</li>
<li>屏蔽定时任务bean违规的字符</li>
<li>日志管理使用索引提升查询性能</li>
<li>日志注解支持排除指定的请求参数</li>
<li>支持自定义隐藏属性列过滤子对象</li>
<li>升级oshi到最新版本6.4.3</li>
<li>升级druid到最新版本1.2.16</li>
<li>升级fastjson到最新版2.0.34</li>
<li>升级spring-boot到最新版本2.5.15</li>
<li>升级element-ui到最新版本2.15.13</li>
<li>移除apache/commons-fileupload依赖</li>
<li>修复页面切换时布局错乱的问题</li>
<li>修复匿名注解Anonymous空指针问题</li>
<li>修复路由跳转被阻止时内部产生报错信息问题</li>
<li>修复isMatchedIp的参数判断产生空指针的问题</li>
<li>修复用户多角色数据权限可能出现权限抬升的情况</li>
<li>修复开启TopNav后一级菜单路由参数设置无效问题</li>
<li>修复DictTag组件value没有匹配的值时则展示value</li>
<li>优化文件下载出现的异常</li>
<li>优化选择图标组件高亮回显</li>
<li>优化弹窗后导航栏偏移的问题</li>
<li>优化修改密码日志存储明文问题</li>
<li>优化页签栏关闭其他出现的异常问题</li>
<li>优化页签关闭左侧选项排除首页选项</li>
<li>优化关闭当前tab页跳转最右侧tab页</li>
<li>优化缓存列表清除操作提示不变的问题</li>
<li>优化字符未使用下划线不进行驼峰式处理</li>
<li>优化用户导入更新时需获取用户编号问题</li>
<li>优化侧边栏的平台标题与VUE_APP_TITLE保持同步</li>
<li>优化导出Excel时设置dictType属性重复查缓存问题</li>
<li>连接池Druid支持新的配置connectTimeout和socketTimeout</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.5 - 2023-01-01">
<ol>
<li>定时任务违规的字符</li>
<li>重置时取消部门选中</li>
<li>新增返回警告消息提示</li>
<li>忽略不必要的属性数据返回</li>
<li>修改参数键名时移除前缓存配置</li>
<li>导入更新用户数据前校验数据权限</li>
<li>兼容Excel下拉框内容过多无法显示的问题</li>
<li>升级echarts到最新版本5.4.0</li>
<li>升级core-js到最新版本3.25.3</li>
<li>升级oshi到最新版本6.4.0</li>
<li>升级kaptcha到最新版2.3.3</li>
<li>升级druid到最新版本1.2.15</li>
<li>升级fastjson到最新版2.0.20</li>
<li>升级pagehelper到最新版1.4.6</li>
<li>优化弹窗内容过多展示不全问题</li>
<li>优化swagger-ui静态资源使用缓存</li>
<li>开启TopNav没有子菜单隐藏侧边栏</li>
<li>删除fuse无效选项maxPatternLength</li>
<li>优化导出对象的子列表为空会出现[]问题</li>
<li>优化编辑头像时透明部分会变成黑色问题</li>
<li>优化小屏幕上修改头像界面布局错位的问题</li>
<li>修复代码生成勾选属性无效问题</li>
<li>修复文件上传组件格式验证问题</li>
<li>修复回显数据字典数组异常问题</li>
<li>修复sheet超出最大行数异常问题</li>
<li>修复Log注解GET请求记录不到参数问题</li>
<li>修复调度日志点击多次数据不变化的问题</li>
<li>修复主题颜色在Drawer组件不会加载问题</li>
<li>修复文件名包含特殊字符的文件无法下载问题</li>
<li>修复table中更多按钮切换主题色未生效修复问题</li>
<li>修复某些特性的环境生成代码变乱码TXT文件问题</li>
<li>修复代码生成图片/文件/单选时选择必填无法校验问题</li>
<li>修复某些特性的情况用户编辑对话框中角色和部门无法修改问题</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.4 - 2022-09-26">
<ol>
<li>数据逻辑删除不进行唯一验证</li>
<li>Excel注解支持导出对象的子列表方法</li>
<li>Excel注解支持自定义隐藏属性列</li>
<li>Excel注解支持backgroundColor属性设置背景色</li>
<li>支持配置密码最大错误次数/锁定时间</li>
<li>登录日志新增解锁账户功能</li>
<li>通用下载方法新增config配置选项</li>
<li>支持多权限字符匹配角色数据权限</li>
<li>页面内嵌iframe切换tab不刷新数据</li>
<li>操作日志记录支持排除敏感属性字段</li>
<li>修复多文件上传报错出现的异常问题</li>
<li>修复图片预览组件src属性为null值控制台报错问题</li>
<li>升级oshi到最新版本6.2.2</li>
<li>升级fastjson到最新版2.0.14</li>
<li>升级pagehelper到最新版1.4.3</li>
<li>升级core-js到最新版本3.25.2</li>
<li>升级element-ui到最新版本2.15.10</li>
<li>优化任务过期不执行调度</li>
<li>优化字典数据使用store存取</li>
<li>优化修改资料头像被覆盖的问题</li>
<li>优化修改用户登录账号重复验证</li>
<li>优化代码生成同步后值NULL问题</li>
<li>优化定时任务支持执行父类方法</li>
<li>优化用户个人信息接口防止修改部门</li>
<li>优化布局设置使用el-drawer抽屉显示</li>
<li>优化没有权限的用户编辑部门缺少数据</li>
<li>优化日志注解记录限制请求地址的长度</li>
<li>优化excel/scale属性导出单元格数值类型</li>
<li>优化日志操作中重置按钮时重复查询的问题</li>
<li>优化多个相同角色数据导致权限SQL重复问题</li>
<li>优化表格上右侧工具条(搜索按钮显隐&右侧样式凸出)</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.3 - 2022-06-27">
<ol>
<li>新增缓存列表菜单功能</li>
<li>代码生成树表新增(展开/折叠)</li>
<li>Excel注解支持color字体颜色</li>
<li>新增Anonymous匿名访问不鉴权注解</li>
<li>用户头像上传限制只能为图片格式</li>
<li>接口使用泛型使其看到响应属性字段</li>
<li>检查定时任务bean所在包名是否为白名单配置</li>
<li>添加页签openPage支持传递参数</li>
<li>用户缓存信息添加部门ancestors祖级列表</li>
<li>升级element-ui到最新版本2.15.8</li>
<li>升级oshi到最新版本6.1.6</li>
<li>升级druid到最新版本1.2.11</li>
<li>升级fastjson到最新版2.0.8</li>
<li>升级spring-boot到最新版本2.5.14</li>
<li>降级jsencrypt版本兼容IE浏览器</li>
<li>删除多余的salt字段</li>
<li>新增获取不带后缀文件名称方法</li>
<li>新增获取配置文件中的属性值方法</li>
<li>新增内容编码/解码方便插件集成使用</li>
<li>字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)</li>
<li>优化设置分页参数默认值</li>
<li>优化对空字符串参数处理的过滤</li>
<li>优化显示顺序orderNum类型为整型</li>
<li>优化表单构建按钮不显示正则校验</li>
<li>优化字典数据回显样式下拉框显示值</li>
<li>优化R响应成功状态码与全局保持一致</li>
<li>优化druid开启wall过滤器出现的异常问题</li>
<li>优化用户管理左侧树型组件增加选中高亮保持</li>
<li>优化新增用户与角色信息&用户与岗位信息逻辑</li>
<li>优化默认不启用压缩文件缓存防止node_modules过大</li>
<li>修复字典数据显示不全问题</li>
<li>修复操作日志查询类型条件为0时会查到所有数据</li>
<li>修复Excel注解prompt/combo同时使用不生效问题</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.2 - 2022-04-01">
<ol>
<li>前端支持设置是否需要防止数据重复提交</li>
<li>开启TopNav没有子菜单情况隐藏侧边栏</li>
<li>侧边栏菜单名称过长悬停显示标题</li>
<li>用户访问控制时校验数据权限,防止越权</li>
<li>导出Excel时屏蔽公式,防止CSV注入风险</li>
<li>组件ImagePreview支持多图预览显示</li>
<li>组件ImageUpload支持多图同时选择上传</li>
<li>组件FileUpload支持多文件同时选择上传</li>
<li>服务监控新增运行参数信息显示</li>
<li>定时任务目标字符串过滤特殊字符</li>
<li>定时任务目标字符串验证包名白名单</li>
<li>代码生成列表图片支持预览</li>
<li>代码生成编辑修改打开新页签</li>
<li>代码生成新增Java类型Boolean</li>
<li>代码生成子表支持日期/字典配置</li>
<li>代码生成同步保留必填/类型选项</li>
<li>升级oshi到最新版本6.1.2</li>
<li>升级fastjson到最新版1.2.80</li>
<li>升级pagehelper到最新版1.4.1</li>
<li>升级spring-boot到最新版本2.5.11</li>
<li>升级spring-boot-mybatis到最新版2.2.2</li>
<li>添加遗漏的分页参数合理化属性</li>
<li>修改npm即将过期的注册源地址</li>
<li>修复分页组件请求两次问题</li>
<li>修复通用文件下载接口跨域问题</li>
<li>修复Xss注解字段值为空时的异常问题</li>
<li>修复选项卡点击右键刷新丢失参数问题</li>
<li>修复表单清除元素位置未垂直居中问题</li>
<li>修复服务监控中运行参数显示条件错误</li>
<li>修复导入Excel时字典字段类型为Long转义为空问题</li>
<li>修复登录超时刷新页面跳转登录页面还提示重新登录问题</li>
<li>优化加载字典缓存数据</li>
<li>优化IP地址获取到多个的问题</li>
<li>优化任务队列满时任务拒绝策略</li>
<li>优化文件上传兼容Weblogic环境</li>
<li>优化定时任务默认保存到内存中执行</li>
<li>优化部门修改缩放后出现的错位问题</li>
<li>优化Excel格式化不同类型的日期对象</li>
<li>优化菜单表关键字导致的插件报错问题</li>
<li>优化Oracle用户头像列为空时不显示问题</li>
<li>优化页面若未匹配到字典标签则返回原字典值</li>
<li>优化修复登录失效后多次请求提示多次弹窗问题</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.1 - 2022-01-01">
<ol>
<li>新增Vue3前端代码生成模板</li>
<li>新增图片预览组件</li>
<li>新增压缩插件实现打包Gzip</li>
<li>自定义xss校验注解实现</li>
<li>自定义文字复制剪贴指令</li>
<li>代码生成预览支持复制内容</li>
<li>路由支持单独配置菜单或角色权限</li>
<li>用户管理部门查询选择节点后分页参数初始</li>
<li>修复用户分配角色属性错误</li>
<li>修复打包后字体图标偶现的乱码问题</li>
<li>修复菜单管理重置表单出现的错误</li>
<li>修复版本差异导致的懒加载报错问题</li>
<li>修复Cron组件中周回显问题</li>
<li>修复定时任务多参数逗号分隔的问题</li>
<li>修复根据ID查询列表可能出现的主键溢出问题</li>
<li>修复tomcat配置参数已过期问题</li>
<li>升级clipboard到最新版本2.0.8</li>
<li>升级oshi到最新版本v5.8.6</li>
<li>升级fastjson到最新版1.2.79</li>
<li>升级spring-boot到最新版本2.5.8</li>
<li>升级log4j2到2.17.1,防止漏洞风险</li>
<li>优化下载解析blob异常提示</li>
<li>优化代码生成字典组重复问题</li>
<li>优化查询用户的角色组&岗位组代码</li>
<li>优化定时任务cron表达式小时设置24</li>
<li>优化用户导入提示溢出则显示滚动条</li>
<li>优化防重复提交标识组合为(key+url+header)</li>
<li>优化分页方法设置成通用方便灵活调用</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.0 - 2021-12-01">
<ol>
<li>新增配套并同步的Vue3前端版本</li>
<li>新增通用方法简化模态/缓存/下载/权限/页签使用</li>
<li>优化导出数据/使用通用下载方法</li>
<li>Excel注解支持自定义数据处理器</li>
<li>Excel注解支持导入导出标题信息</li>
<li>Excel导入支持@Excels注解</li>
<li>新增组件data-dict,简化数据字典使用</li>
<li>新增Jaxb依赖,防止jdk8以上出现的兼容错误</li>
<li>生产环境使用路由懒加载提升页面响应速度</li>
<li>修复五级以上菜单出现的404问题</li>
<li>防重提交注解支持配置间隔时间/提示消息</li>
<li>日志注解新增是否保存响应参数</li>
<li>任务屏蔽违规字符&参数忽略双引号中的逗号</li>
<li>升级SpringBoot到最新版本2.5.6</li>
<li>升级pagehelper到最新版1.4.0</li>
<li>升级spring-boot-mybatis到最新版2.2.0</li>
<li>升级oshi到最新版本v5.8.2</li>
<li>升级druid到最新版1.2.8</li>
<li>升级velocity到最新版本2.3</li>
<li>升级fastjson到最新版1.2.78</li>
<li>升级axios到最新版本0.24.0</li>
<li>升级dart-sass到版本1.32.13</li>
<li>升级core-js到最新版本3.19.1</li>
<li>升级jsencrypt到最新版本3.2.1</li>
<li>升级js-cookie到最新版本3.0.1</li>
<li>升级file-saver到最新版本2.0.5</li>
<li>升级sass-loader到最新版本10.1.1</li>
<li>升级element-ui到最新版本2.15.6</li>
<li>新增sendGet无参请求方法</li>
<li>禁用el-tag组件的渐变动画</li>
<li>代码生成点击预览重置激活tab</li>
<li>AjaxResult重写put方法,以方便链式调用</li>
<li>优化登录/验证码请求headers不设置token</li>
<li>优化用户个人信息接口防止修改用户名</li>
<li>优化Cron表达式生成器关闭时销毁避免缓存</li>
<li>优化注册成功提示消息类型success</li>
<li>优化aop语法,使用spring自动注入注解</li>
<li>优化记录登录信息,移除不必要的修改</li>
<li>优化mybatis全局默认的执行器</li>
<li>优化Excel导入图片可能出现的异常</li>
<li>修复代码生成模板主子表删除缺少事务</li>
<li>修复日志记录可能出现的转换异常</li>
<li>修复代码生成复选框字典遗漏问题</li>
<li>修复关闭xss功能导致可重复读RepeatableFilter失效</li>
<li>修复字符串无法被反转义问题</li>
<li>修复后端主子表代码模板方法名生成错误问题</li>
<li>修复xss过滤后格式出现的异常</li>
<li>修复swagger没有指定dataTypeClass导致启动出现warn日志</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.7.0 - 2021-09-13">
<ol>
<li>参数管理支持配置验证码开关</li>
<li>新增是否开启用户注册功能</li>
<li>定时任务支持在线生成cron表达式</li>
<li>菜单管理支持配置路由参数</li>
<li>支持自定义注解实现接口限流</li>
<li>Excel注解支持Image图片导入</li>
<li>自定义弹层溢出滚动样式</li>
<li>自定义可拖动弹窗宽度指令</li>
<li>自定义可拖动弹窗高度指令</li>
<li>修复任意账户越权问题</li>
<li>修改时检查用户数据权限范围</li>
<li>修复保存配置主题颜色失效问题</li>
<li>新增暗色菜单风格主题</li>
<li>菜单&部门新增展开/折叠功能</li>
<li>页签新增关闭左侧&添加图标</li>
<li>顶部菜单排除隐藏的默认路由</li>
<li>顶部菜单同步系统主题样式</li>
<li>跳转路由高亮相对应的菜单栏</li>
<li>代码生成主子表多选行数据</li>
<li>日期范围支持添加多组</li>
<li>升级element-ui到最新版本2.15.5</li>
<li>升级oshi到最新版本v5.8.0</li>
<li>升级commons.io到最新版本v2.11.0</li>
<li>定时任务屏蔽ldap远程调用</li>
<li>定时任务屏蔽http(s)远程调用</li>
<li>补充定时任务表字段注释</li>
<li>定时任务对检查异常进行事务回滚</li>
<li>启用父部门状态排除顶级节点</li>
<li>富文本新增上传文件大小限制</li>
<li>默认首页使用keep-alive缓存</li>
<li>修改代码生成字典回显样式</li>
<li>自定义分页合理化传入参数</li>
<li>修复字典组件值为整形不显示问题</li>
<li>修复定时任务日志执行状态显示</li>
<li>角色&菜单新增字段属性提示信息</li>
<li>修复角色分配用户页面参数类型错误提醒</li>
<li>优化布局设置动画特效</li>
<li>优化异常处理信息</li>
<li>优化错误token导致的解析异常</li>
<li>密码框新增显示切换密码图标</li>
<li>定时任务新增更多操作</li>
<li>更多操作按钮添加权限控制</li>
<li>导入用户样式优化</li>
<li>提取通用方法到基类控制器</li>
<li>优化使用权限工具获取用户信息</li>
<li>优化用户不能删除自己</li>
<li>优化XSS跨站脚本过滤</li>
<li>优化代码生成模板</li>
<li>验证码默认20s超时</li>
<li>BLOB下载时清除URL对象引用</li>
<li>代码生成导入表按创建时间排序</li>
<li>修复代码生成页面数据编辑保存之后总是跳转第一页的问题</li>
<li>修复带safari浏览器无法格式化utc日期格式yyyy-MM-dd'T'HH:mm:ss.SSS问题</li>
<li>多图上传组件移除多余的api地址&验证失败导致图片删除问题&无法删除相应图片修复</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.6.0 - 2021-07-12">
<ol>
<li>角色管理新增分配用户功能</li>
<li>用户管理新增分配角色功能</li>
<li>日志列表支持排序操作</li>
<li>优化参数&字典缓存操作</li>
<li>系统布局配置支持动态标题开关</li>
<li>菜单路由配置支持内链访问</li>
<li>默认访问后端首页新增提示语</li>
<li>富文本默认上传返回url类型</li>
<li>新增自定义弹窗拖拽指令</li>
<li>全局注册常用通用组件</li>
<li>全局挂载字典标签组件</li>
<li>ImageUpload组件支持多图片上传</li>
<li>FileUpload组件支持多文件上传</li>
<li>文件上传组件添加数量限制属性</li>
<li>富文本编辑组件添加类型属性</li>
<li>富文本组件工具栏配置视频</li>
<li>封装通用iframe组件</li>
<li>限制超级管理员不允许操作</li>
<li>用户信息长度校验限制</li>
<li>分页组件新增pagerCount属性</li>
<li>添加bat脚本执行应用</li>
<li>升级oshi到最新版本v5.7.4</li>
<li>升级element-ui到最新版本2.15.2</li>
<li>升级pagehelper到最新版1.3.1</li>
<li>升级commons.io到最新版本v2.10.0</li>
<li>升级commons.fileupload到最新版本v1.4</li>
<li>升级swagger到最新版本v3.0.0</li>
<li>修复关闭confirm提示框控制台报错问题</li>
<li>修复存在的SQL注入漏洞问题</li>
<li>定时任务屏蔽rmi远程调用</li>
<li>修复用户搜索分页变量错误</li>
<li>修复导出角色数据范围翻译缺少仅本人</li>
<li>修复表单构建选择下拉选择控制台报错问题</li>
<li>优化图片工具类读取文件</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.5.0 - 2021-05-25">
<ol>
<li>新增菜单导航显示风格TopNav(false为左侧导航菜单,true为顶部导航菜单)</li>
<li>布局设置支持保存&重置配置</li>
<li>修复树表数据显示不全&加载慢问题</li>
<li>新增IE浏览器版本过低提示页面</li>
<li>用户登录后记录最后登录IP&时间</li>
<li>页面导出按钮点击之后添加遮罩</li>
<li>富文本编辑器支持自定义上传地址</li>
<li>富文本编辑组件新增readOnly属性</li>
<li>页签TagsView新增关闭右侧功能</li>
<li>显隐列组件加载初始默认隐藏列</li>
<li>关闭头像上传窗口还原默认图片</li>
<li>个人信息添加手机&邮箱重复验证</li>
<li>代码生成模板导出按钮点击后添加遮罩</li>
<li>代码生成模板树表操作列添加新增按钮</li>
<li>代码生成模板修复主子表字段重名问题</li>
<li>升级fastjson到最新版1.2.76</li>
<li>升级druid到最新版本v1.2.6</li>
<li>升级mybatis到最新版3.5.6 阻止远程代码执行漏洞</li>
<li>升级oshi到最新版本v5.6.0</li>
<li>velocity剔除commons-collections版本,防止3.2.1版本的反序列化漏洞</li>
<li>数据监控页默认账户密码防止越权访问</li>
<li>修复firefox下表单构建拖拽会新打卡一个选项卡</li>
<li>修正后端导入表权限标识</li>
<li>修正前端操作日志&登录日志权限标识</li>
<li>设置Redis配置HashKey序列化</li>
<li>删除操作日志记录信息</li>
<li>上传媒体类型添加视频格式</li>
<li>修复请求形参未传值记录日志异常问题</li>
<li>优化xss校验json请求条件</li>
<li>树级结构更新子节点使用replaceFirst</li>
<li>优化ExcelUtil空值处理</li>
<li>日志记录过滤BindingResult对象,防止异常</li>
<li>修改主题后mini类型按钮无效问题</li>
<li>优化通用下载完成后删除节点</li>
<li>通用Controller添加响应返回消息</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.4.0 - 2021-02-22">
<ol>
<li>代码生成模板支持主子表</li>
<li>表格右侧工具栏组件支持显隐列</li>
<li>图片组件添加预览&移除功能</li>
<li>Excel注解支持Image图片导出</li>
<li>操作按钮组调整为朴素按钮样式</li>
<li>代码生成支持文件上传组件</li>
<li>代码生成日期控件区分范围</li>
<li>代码生成数据库文本类型生成表单文本域</li>
<li>用户手机邮箱&菜单组件修改允许空字符串</li>
<li>升级SpringBoot到最新版本2.2.13 提升启动速度</li>
<li>升级druid到最新版本v1.2.4</li>
<li>升级fastjson到最新版1.2.75</li>
<li>升级element-ui到最新版本2.15.0</li>
<li>修复IE11浏览器报错问题</li>
<li>优化多级菜单之间切换无法缓存的问题</li>
<li>修复四级菜单无法显示问题</li>
<li>修正侧边栏静态路由丢失问题</li>
<li>修复角色管理-编辑角色-功能权限显示异常</li>
<li>配置文件新增redis数据库索引属性</li>
<li>权限工具类增加admin判断</li>
<li>角色非自定义权限范围清空选择值</li>
<li>修复导入数据为负浮点数时丢失精度问题</li>
<li>移除path-to-regexp正则匹配插件</li>
<li>修复生成树表代码异常</li>
<li>修改ip字段长度防止ipv6地址长度不够</li>
<li>防止get请求参数值为false或0等特殊值会导致无法正确的传参</li>
<li>登录后push添加catch防止出现检查错误</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.3.0 - 2020-12-14">
<ol>
<li>新增缓存监控功能</li>
<li>支持主题风格配置</li>
<li>修复多级菜单之间切换无法缓存的问题</li>
<li>多级菜单自动配置组件</li>
<li>代码生成预览支持高亮显示</li>
<li>支持Get请求映射Params参数</li>
<li>删除用户和角色解绑关联</li>
<li>去除用户手机邮箱部门必填验证</li>
<li>Excel支持注解align对齐方式</li>
<li>Excel支持导入Boolean型数据</li>
<li>优化头像样式,鼠标移入悬停遮罩</li>
<li>代码生成预览提供滚动机制</li>
<li>代码生成删除多余的数字float类型</li>
<li>修正转换字符串的目标字符集属性</li>
<li>回显数据字典防止空值报错</li>
<li>日志记录增加过滤多文件场景</li>
<li>修改缓存Set方法可能导致嵌套的问题</li>
<li>移除前端一些多余的依赖</li>
<li>防止安全扫描YUI出现的风险提示</li>
<li>修改node-sass为dart-sass</li>
<li>升级SpringBoot到最新版本2.1.18</li>
<li>升级poi到最新版本4.1.2</li>
<li>升级oshi到最新版本v5.3.6</li>
<li>升级bitwalker到最新版本1.21</li>
<li>升级axios到最新版本0.21.0</li>
<li>升级element-ui到最新版本2.14.1</li>
<li>升级vue到最新版本2.6.12</li>
<li>升级vuex到最新版本3.6.0</li>
<li>升级vue-cli到版本4.5.9</li>
<li>升级vue-router到最新版本3.4.9</li>
<li>升级vue-cli到最新版本4.4.6</li>
<li>升级vue-cropper到最新版本0.5.5</li>
<li>升级clipboard到最新版本2.0.6</li>
<li>升级core-js到最新版本3.8.1</li>
<li>升级echarts到最新版本4.9.0</li>
<li>升级file-saver到最新版本2.0.4</li>
<li>升级fuse.js到最新版本6.4.3</li>
<li>升级js-beautify到最新版本1.13.0</li>
<li>升级js-cookie到最新版本2.2.1</li>
<li>升级path-to-regexp到最新版本6.2.0</li>
<li>升级quill到最新版本1.3.7</li>
<li>升级screenfull到最新版本5.0.2</li>
<li>升级sortablejs到最新版本1.10.2</li>
<li>升级vuedraggable到最新版本2.24.3</li>
<li>升级chalk到最新版本4.1.0</li>
<li>升级eslint到最新版本7.15.0</li>
<li>升级eslint-plugin-vue到最新版本7.2.0</li>
<li>升级lint-staged到最新版本10.5.3</li>
<li>升级runjs到最新版本4.4.2</li>
<li>升级sass-loader到最新版本10.1.0</li>
<li>升级script-ext-html-webpack-plugin到最新版本2.1.5</li>
<li>升级svg-sprite-loader到最新版本5.1.1</li>
<li>升级vue-template-compiler到最新版本2.6.12</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.2.1 - 2020-11-18">
<ol>
<li>阻止任意文件下载漏洞</li>
<li>代码生成支持上传控件</li>
<li>新增图片上传组件</li>
<li>调整默认首页</li>
<li>升级druid到最新版本v1.2.2</li>
<li>mapperLocations配置支持分隔符</li>
<li>权限信息调整</li>
<li>调整sql默认时间</li>
<li>解决代码生成没有bit类型的问题</li>
<li>升级pagehelper到最新版1.3.0</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.2.0 - 2020-10-10">
<ol>
<li>升级springboot版本到2.1.17 提升安全性</li>
<li>升级oshi到最新版本v5.2.5</li>
<li>升级druid到最新版本v1.2.1</li>
<li>升级jjwt到版本0.9.1</li>
<li>升级fastjson到最新版1.2.74</li>
<li>修改sass为node-sass,避免el-icon图标乱码</li>
<li>代码生成支持同步数据库</li>
<li>代码生成支持富文本控件</li>
<li>代码生成页面时不忽略remark属性</li>
<li>代码生成添加select必填选项</li>
<li>Excel导出类型NUMERIC支持精度浮点类型</li>
<li>Excel导出targetAttr优化获取值,防止get方法不规范</li>
<li>Excel注解支持自动统计数据总和</li>
<li>Excel注解支持设置BigDecimal精度&舍入规则</li>
<li>菜单&数据权限新增(展开/折叠 全选/全不选 父子联动)</li>
<li>允许用户分配到部门父节点</li>
<li>菜单新增是否缓存keep-alive</li>
<li>表格操作列间距调整</li>
<li>限制系统内置参数不允许删除</li>
<li>富文本组件优化,支持自定义高度&图片冲突问题</li>
<li>富文本工具栏样式对齐</li>
<li>导入excel整形值校验优化</li>
<li>修复页签关闭所有时固定标签路由不刷新问题</li>
<li>表单构建布局型组件新增按钮</li>
<li>左侧菜单文字过长显示省略号</li>
<li>修正根节点为子部门时,树状结构显示问题</li>
<li>修正调用目标字符串最大长度</li>
<li>修正菜单提示信息错误</li>
<li>修正定时任务执行一次权限标识</li>
<li>修正数据库字符串类型nvarchar</li>
<li>优化递归子节点</li>
<li>优化数据权限判断</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.1.0 - 2020-08-13">
<ol>
<li>表格工具栏右侧添加刷新&显隐查询组件</li>
<li>后端支持CORS跨域请求</li>
<li>代码生成支持选择上级菜单</li>
<li>代码生成支持自定义路径</li>
<li>代码生成支持复选框</li>
<li>Excel导出导入支持dictType字典类型</li>
<li>Excel支持分割字符串组内容</li>
<li>验证码类型支持(数组计算、字符验证)</li>
<li>升级vue-cli版本到4.4.4</li>
<li>修改 node-sass 为 dart-sass</li>
<li>表单类型为Integer/Long设置整形默认值</li>
<li>代码生成器默认mapper路径与默认mapperScan路径不一致</li>
<li>优化防重复提交拦截器</li>
<li>优化上级菜单不能选择自己</li>
<li>修复角色的权限分配后,未实时生效问题</li>
<li>修复在线用户日志记录类型</li>
<li>修复富文本空格和缩进保存后不生效问题</li>
<li>修复在线用户判断逻辑</li>
<li>唯一限制条件只返回单条数据</li>
<li>添加获取当前的环境配置方法</li>
<li>超时登录后页面跳转到首页</li>
<li>全局异常状态汉化拦截处理</li>
<li>HTML过滤器改为将html转义</li>
<li>检查字符支持小数点&降级改成异常提醒</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.0.0 - 2020-07-20">
<ol>
<li>单应用调整为多模块项目</li>
<li>升级element-ui版本到2.13.2</li>
<li>删除babel,提高编译速度。</li>
<li>新增菜单默认主类目</li>
<li>编码文件名修改为uuid方式</li>
<li>定时任务cron表达式验证</li>
<li>角色权限修改时已有权限未自动勾选异常修复</li>
<li>防止切换权限用户后登录出现404</li>
<li>Excel支持sort导出排序</li>
<li>创建用户不允许选择超级管理员角色</li>
<li>修复代码生成导入表结构出现异常页面不提醒问题</li>
<li>修复代码生成点击多次表修改数据不变化的问题</li>
<li>修复头像上传成功二次打开无法改变裁剪框大小和位置问题</li>
<li>修复布局为small者mini用户表单显示错位问题</li>
<li>修复热部署导致的强换异常问题</li>
<li>修改用户管理复选框宽度,防止部分浏览器出现省略号</li>
<li>IpUtils工具,清除Xss特殊字符,防止Xff注入攻击</li>
<li>生成domain 如果是浮点型 统一用BigDecimal</li>
<li>定时任务调整label-width,防止部署出现错位</li>
<li>调整表头固定列默认样式</li>
<li>代码生成模板调整,字段为String并且必填则加空串条件</li>
<li>代码生成字典Integer/Long使用parseInt</li>
<li>修复dict_sort不可update为0的问题&查询返回增加dict_sort升序排序</li>
<li>修正岗位导出权限注解</li>
<li>禁止加密密文返回前端</li>
<li>修复代码生成页面中的查询条件创建时间未生效的问题</li>
<li>修复首页搜索菜单外链无法点击跳转问题</li>
<li>修复菜单管理选择图标,backspace删除时不过滤数据</li>
<li>用户管理部门分支节点不可检查&显示计数</li>
<li>数据范围过滤属性调整</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v2.3.0 - 2020-06-01">
<ol>
<li>升级fastjson到最新版1.2.70 修复高危安全漏洞</li>
<li>dev启动默认打开浏览器</li>
<li>vue-cli使用默认source-map</li>
<li>slidebar eslint报错优化</li>
<li>当tags-view滚动关闭右键菜单</li>
<li>字典管理添加缓存读取</li>
<li>参数管理支持缓存操作</li>
<li>支持一级菜单(和主页同级)在main区域显示</li>
<li>限制外链地址必须以http(s)开头</li>
<li>tagview & sidebar 主题颜色与element ui(全局)同步</li>
<li>修改数据源类型优先级,先根据方法,再根据类</li>
<li>支持是否需要设置token属性,自定义返回码消息。</li>
<li>swagger请求前缀加入配置。</li>
<li>登录地点设置内容过长则隐藏显示</li>
<li>修复定时任务执行一次按钮后不提示消息问题</li>
<li>修改上级部门(选择项排除本身和下级)</li>
<li>通用http发送方法增加参数 contentType 编码类型</li>
<li>更换IP地址查询接口</li>
<li>修复页签变量undefined</li>
<li>添加校验部门包含未停用的子部门</li>
<li>修改定时任务详情下次执行时间日期显示错误</li>
<li>角色管理查询设置默认排序字段</li>
<li>swagger添加enable参数控制是否启用</li>
<li>只对json类型请求构建可重复读取inputStream的request</li>
<li>修改代码生成字典字段int类型没有自动选中问题</li>
<li>vuex用户名取值修正</li>
<li>表格树模板去掉多余的)</li>
<li>代码生成序号修正</li>
<li>全屏情况下不调整上外边距</li>
<li>代码生成Date字段添加默认格式</li>
<li>用户管理角色选择权限控制</li>
<li>修复路由懒加载报错问题</li>
<li>模板sql.vm添加菜单状态</li>
<li>设置用户名称不能修改</li>
<li>dialog添加append-to-body属性,防止ie遮罩</li>
<li>菜单区分状态和显示隐藏功能</li>
<li>升级fastjson到最新版1.2.68 修复安全加固</li>
<li>修复代码生成如果选择字典类型缺失逗号问题</li>
<li>登录请求params更换为data,防止暴露url</li>
<li>日志返回时间格式处理</li>
<li>添加handle控制允许拖动的元素</li>
<li>布局设置点击扩大范围</li>
<li>代码生成列属性排序查询</li>
<li>代码生成列支持拖动排序</li>
<li>修复时间格式不支持ios问题</li>
<li>表单构建添加父级class,防止冲突</li>
<li>定时任务并发属性修正</li>
<li>角色禁用&菜单隐藏不查询权限</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v2.2.0 - 2020-03-18">
<ol>
<li>系统监控新增定时任务功能</li>
<li>添加一个打包Web工程bat</li>
<li>修复页签鼠标滚轮按下的时候,可以关闭不可关闭的tag</li>
<li>修复点击退出登录有时会无提示问题</li>
<li>修复防重复提交注解无效问题</li>
<li>修复通知公告批量删除异常问题</li>
<li>添加菜单时路由地址必填限制</li>
<li>代码生成字段描述可编辑</li>
<li>修复用户修改个人信息导致缓存不过期问题</li>
<li>个人信息创建时间获取正确属性值</li>
<li>操作日志详细显示正确类型</li>
<li>导入表单击行数据时选中对应的复选框</li>
<li>批量替换表前缀逻辑调整</li>
<li>固定重定向路径表达式</li>
<li>升级element-ui版本到2.13.0</li>
<li>操作日志排序调整</li>
<li>修复charts切换侧边栏或者缩放窗口显示bug</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v2.1.0 - 2020-02-24">
<ol>
<li>新增表单构建</li>
<li>代码生成支持树表结构</li>
<li>新增用户导入</li>
<li>修复动态加载路由页面刷新问题</li>
<li>修复地址开关无效问题</li>
<li>汉化错误提示页面</li>
<li>代码生成已知问题修改</li>
<li>修复多数据源下配置关闭出现异常处理</li>
<li>添加HTML过滤器,用于去除XSS漏洞隐患</li>
<li>修复上传头像控制台出现异常</li>
<li>修改用户管理分页不正确的问题</li>
<li>修复验证码记录提示错误</li>
<li>修复request.js缺少Message引用</li>
<li>修复表格时间为空出现的异常</li>
<li>添加Jackson日期反序列化时区配置</li>
<li>调整根据用户权限加载菜单数据树形结构</li>
<li>调整成功登录不恢复按钮,防止多次点击</li>
<li>修改用户个人资料同步缓存信息</li>
<li>修复页面同时出现el-upload和Editor不显示处理</li>
<li>修复在角色管理页修改菜单权限偶尔未选中问题</li>
<li>配置文件新增redis密码属性</li>
<li>设置mybatis全局的配置文件</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v2.0.0 - 2019-12-02">
<ol>
<li>新增代码生成</li>
<li>新增@RepeatSubmit注解,防止重复提交</li>
<li>新增菜单主目录添加/删除操作</li>
<li>日志记录过滤特殊对象,防止转换异常</li>
<li>修改代码生成路由脚本错误</li>
<li>用户上传头像实时同步缓存,无需重新登录</li>
<li>调整切换页签后不重新加载数据</li>
<li>添加jsencrypt实现参数的前端加密</li>
<li>系统退出删除用户缓存记录</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v1.1.0 - 2019-11-11">
<ol>
<li>新增在线用户管理</li>
<li>新增按钮组功能实现(批量删除、导出、清空)</li>
<li>新增查询条件重置按钮</li>
<li>新增Swagger全局Token配置</li>
<li>新增后端参数校验</li>
<li>修复字典管理页面的日期查询异常</li>
<li>修改时间函数命名防止冲突</li>
<li>去除菜单上级校验,默认为顶级</li>
<li>修复用户密码无法修改问题</li>
<li>修复菜单类型为按钮时不显示权限标识</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v1.0.0 - 2019-10-08">
<ol>
<li>若依前后端分离系统正式发布</li>
</ol>
</el-collapse-item>
</el-collapse>
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="8">
<el-card class="update-log">
<template v-slot:header>
<div class="clearfix">
<span>捐赠支持</span>
</div>
</template>
<div class="body">
<img src="@/assets/images/pay.png" alt="donate" style="width: 100%" />
<span style="display: inline-block; height: 30px; line-height: 30px"
>你可以请作者喝杯咖啡表示鼓励</span
>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup name="Index">
const version = ref('3.9.0')
function goTarget(url) {
window.open(url, '__blank')
}
</script>
<style scoped lang="scss">
.home {
blockquote {
padding: 10px 20px;
margin: 0 0 20px;
font-size: 17.5px;
border-left: 5px solid #eee;
}
hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
}
.col-item {
margin-bottom: 20px;
}
ul {
padding: 0;
margin: 0;
}
font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 13px;
color: #676a6c;
overflow-x: hidden;
ul {
list-style-type: none;
}
h4 {
margin-top: 0px;
}
h2 {
margin-top: 10px;
font-size: 26px;
font-weight: 100;
}
p {
margin-top: 10px;
b {
font-weight: 700;
}
}
.update-log {
ol {
display: block;
list-style-type: decimal;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0;
margin-inline-end: 0;
padding-inline-start: 40px;
}
}
}
</style>
......@@ -375,7 +375,7 @@ const handleBack = () => {
if (route.query.type == 'add') {
console.log('add router', router)
// getAddInfo()
getAddInfo()
} else if (route.query.type == 'edit') {
setTimeout(() => {
getProcessInfo(route.query.fnaBizId)
......
<template>
<div class="data-management-page">
<!-- 查询区域 -->
<el-card class="search-card">
<!-- 第一行筛选条件 -->
<el-row :gutter="20" class="search-row">
<el-col :span="8">
<div class="form-item">
<label class="form-label">保单号</label>
<el-input
v-model="searchForm.policyNo"
placeholder="请输入"
clearable
size="default"
@keyup.enter="handleSearch"
/>
</div>
</el-col>
<el-col :span="8">
<div class="form-item">
<label class="form-label">客户姓名</label>
<el-input
v-model="searchForm.customerName"
placeholder="请输入"
clearable
size="default"
@keyup.enter="handleSearch"
/>
</div>
</el-col>
<el-col :span="8">
<div class="form-item">
<label class="form-label">新单状态</label>
<el-select
v-model="searchForm.status"
placeholder="请选择"
clearable
size="default"
>
<!-- 增加全部,默认传空字符串 -->
<el-option label="全部" value=" " />
<el-option v-for="item in policyFollowStatusList" :key="item.itemValue" :label="item.itemLabel" :value="item.itemValue" />
</el-select>
</div>
</el-col>
</el-row>
<!-- 第二行筛选条件 -->
<el-row :gutter="20" class="search-row">
<el-col :span="8">
<div class="form-item">
<label class="form-label">签单时间</label>
<el-date-picker
v-model="searchForm.signDateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
size="default"
/>
</div>
</el-col>
<el-col :span="8" class="search-buttons">
<el-button
type="primary"
@click="handleSearch"
size="default"
:icon="Search"
class="search-btn"
>
查询
</el-button>
<el-button
@click="resetForm"
size="default"
:icon="RefreshLeft"
>
重置
</el-button>
</el-col>
</el-row>
</el-card>
<!-- Excel导入区域 -->
<div class="import-area">
<el-card class="import-card">
<div class="import-content">
<div class="import-actions">
<el-upload
class="upload-excel"
:auto-upload="false"
:on-change="handleFileChange"
:show-file-list="false"
accept=".xlsx, .xls"
>
<el-button
type="success"
:icon="UploadFilled"
size="default"
>
上传Excel文件
</el-button>
</el-upload>
<el-button
text
@click="downloadTemplate"
size="default"
class="download-template-btn"
>
下载模板
</el-button>
</div>
<!-- 文件信息显示 -->
<div v-if="selectedFile" class="file-info">
<div class="file-info-content">
<el-icon><document /></el-icon>
<span class="file-name">{{ selectedFile.name }}</span>
<span class="file-size">({{ formatFileSize(selectedFile.size) }})</span>
</div>
<el-button
type="primary"
@click="handleImport"
size="default"
:loading="importLoading"
class="confirm-import-btn"
>
确认导入
</el-button>
</div>
</div>
</el-card>
</div>
<!-- 列表区域 -->
<el-card class="table-card">
<div class="table-actions">
<el-button type="primary" @click="handleUpdateToPolicyLib">更新至保单库</el-button>
<!-- <el-text class="mx-1" size="large">新单首期保费缴费完成后,勾选并点击更新至保单库,可前往保单中心查询</el-text> -->
</div>
<el-table
v-loading="tableLoading"
:data="tableData"
border
style="width: 100%"
height="350"
@selection-change="handleSelectionChange"
@sort-change="handleSortChange"
:row-class-name="tableRowClassName"
>
<el-table-column type="selection" width="55" align="center" />
<!-- 新单状态需要通过policyFollowStatusList和value匹配,显示label -->
<el-table-column prop="status" label="新单状态" min-width="100" align="center" sortable>
<template #default="scope">
<span>{{ convertStatusToDict(1,scope.row.status) }}</span>
</template>
</el-table-column>
<el-table-column prop="policyNo" label="保单号" min-width="100" align="center" sortable/>
<el-table-column prop="customerName" label="客户名称" min-width="100" align="center" sortable/>
<el-table-column prop="signDate" label="签单日期" min-width="100" align="center" sortable/>
<el-table-column prop="signer" label="签单人" min-width="100" align="center" sortable/>
<el-table-column prop="paymentTerm" label="供款年期" min-width="100" align="center" sortable/>
<el-table-column prop="productName" label="产品名称" min-width="100" align="center" sortable/>
<el-table-column prop="insurer" label="保险公司" min-width="100" align="center" sortable/>
<el-table-column prop="reconciliationCompany" label="对账公司" min-width="100" align="center" sortable/>
<el-table-column prop="policyHolder" label="保单持有人" min-width="100" align="center" sortable/>
<el-table-column prop="insured" label="受保人" min-width="100" align="center" sortable/>
<el-table-column prop="currency" label="币种" min-width="100" align="center" sortable/>
<el-table-column prop="initialPremium" label="首期保费" min-width="100" align="center" sortable>
<template #default="scope">
{{ numberWithCommas(scope.row.initialPremium) }}
</template>
</el-table-column>
<el-table-column
label="操作"
min-width="180"
align="center"
>
<template #default="scope">
<el-button
text
size="primary"
@click="handleView(scope.row)"
>
查看
</el-button>
<!-- <el-button
text
size="default"
type="primary"
@click="handleEdit(scope.row)"
>
编辑
</el-button> -->
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination">
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
<!-- 使用查看数据详情组件 -->
<PolicyDetailDialog
:visible="viewDialogVisible"
:detail-data="currentRow"
:policy-follow-status-list="policyFollowStatusList"
title="新单跟进详情"
:expected-commission-list="expectedCommissionList"
:policy-fortune-list="policyFortuneList"
@close="handleDetailClose"
/>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import { getPolicyFollowList, getExpectedCommissionList } from '@/api/sign/underwritingMain'
import { getToken } from "@/utils/auth"
import { listType } from '@/api/system/dict/type'
import date from '@/utils/date'
import PolicyDetailDialog from '@/components/PolicyDetailDialog/index.vue'
import { numberWithCommas } from '@/utils/index.js'
import { getPolicyFortuneList } from '@/api/financial/commission'
// 通过dictType=csf_policy_follow_status获取新单状态字典值,获取对象中的dictItemList
const policyFollowStatusList = ref([]);
const commissionStatusList = ref([]);
const fortuneStatusList = ref([]);
const getLists = ()=>{
listType({typeList: ['csf_policy_follow_status','csf_expected_commission_status','csf_fortune_status']}).then(res => {
if (res.code === 200 && res.data) {
const statusData = res.data.find(item => item.dictType === 'csf_policy_follow_status');
policyFollowStatusList.value = statusData?.dictItemList || [];
const commissionStatusData = res.data.find(item => item.dictType === 'csf_expected_commission_status');
commissionStatusList.value = commissionStatusData?.dictItemList || [];
const fortuneStatusData = res.data.find(item => item.dictType === 'csf_fortune_status');
fortuneStatusList.value = fortuneStatusData?.dictItemList || [];
} else {
policyFollowStatusList.value = [];
commissionStatusList.value = [];
fortuneStatusList.value = [];
}
}).catch(error => {
console.error('获取状态列表失败:', error);
policyFollowStatusList.value = [];
})
}
// 返回数据中状态需要转换为字典值
const convertStatusToDict = (type=1,status) => {
const arr = type === 1 ? policyFollowStatusList : type === 2 ? commissionStatusList : fortuneStatusList
const dictItem = arr.value.find(item => item.itemValue == status)
return dictItem?.itemLabel ?? status
}
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + '/csf/api/policy_follow/upload/excel')
// 搜索表单数据
const searchForm = reactive({
policyBizId: '', // 新单编号(对应接口的policyBizId)
policyNo: '', // 保单编号
customerName: '', // 客户姓名
customerBizId: '', // 客户编号(对应接口的customerBizId)
signDateRange: [], // 签单时间范围
status: '', // 新单状态(对应接口的status)
// 高级筛选字段
insurer : '', // 保险公司
productCode: '' // 产品代码(对应接口的productCode)
})
// 预计来佣列表
const expectedCommissionList = ref([])
const policyFortuneList = ref([])
const getPolicyFortuneLists = async (policyNo) => {
try {
const response = await getPolicyFortuneList({ policyNo })
if (response.code === 200 && response.data) {
policyFortuneList.value = response.data.records || []
policyFortuneList.value.forEach(item => {
item.fortuneStatusLabel = convertStatusToDict(3,item.status)
})
} else {
policyFortuneList.value = []
}
} catch (error) {
console.error('获取预计来佣列表失败:', error)
policyFortuneList.value = []
}
}
// 表格数据
const tableData = ref([])
const tableLoading = ref(false)
const selectedRows = ref([])
// 查看详情弹窗相关
const viewDialogVisible = ref(false)
const currentRow = ref({})
// 分页数据
const pagination = reactive({
currentPage: 1, // 当前页码(对应接口的pageNo)
pageSize: 10, // 每页条数(对应接口的pageSize)
total: 0, // 总条数
sortField: '', // 排序字段
sortOrder: '' // 排序方向
})
// 处理查看
const handleView = async (row) => {
console.log('查看详情:', row)
currentRow.value = { ...row}
viewDialogVisible.value = true
fetchExpectedCommissionList(row.policyNo)
getPolicyFortuneLists(row.policyNo)
}
// 处理详情弹窗关闭
const handleDetailClose = () => {
viewDialogVisible.value = false
console.log('详情弹窗已关闭')
}
// Excel导入相关
const selectedFile = ref(null)
const importLoading = ref(false)
// 页面加载时获取数据
onMounted(() => {
fetchTableData();
getLists();
})
// 获取列表数据
const fetchTableData = async () => {
tableLoading.value = true
try {
// 构造接口请求参数
const params = {
pageNo: pagination.currentPage, // 注意:如果后端是从0开始的页码,需要减1
pageSize: pagination.pageSize,
sortField: pagination.sortField,
sortOrder: pagination.sortOrder,
status: searchForm.status,
policyBizId: searchForm.policyBizId,
policyNo: searchForm.policyNo,
customerName: searchForm.customerName,
customerBizId: searchForm.customerBizId,
insurer : searchForm.insurer,
productCode: searchForm.productCode,
// 签单时间范围需要根据后端要求的参数名进行调整
// 例如:如果后端需要startSignDate和endSignDate
...(searchForm.signDateRange && searchForm.signDateRange.length === 2 && {
startSignDate: date.formatToDate(searchForm.signDateRange[0]) + ' 00:00:00',
endSignDate: date.formatToDate(searchForm.signDateRange[1]) + ' 23:59:59'
})
}
// 调用后台接口
const response = await getPolicyFollowList(params)
// 处理接口响应
if (response.code === 200) {
const result = response.data
// 将接口返回的数据映射到表格
tableData.value = result.records.map(record => ({
...record
}))
// console.log('tableData',tableData.value)
// 更新分页信息
pagination.total = result.total
pagination.currentPage = result.current || pagination.currentPage
pagination.pageSize = result.size || pagination.pageSize
} else {
// 接口返回错误信息
ElMessage.error(`获取数据失败: ${response.data.msg || '未知错误'}`)
tableData.value = []
pagination.total = 0
}
} catch (error) {
// 捕获网络或其他异常
console.error('请求失败:', error)
ElMessage.error('网络异常,请稍后重试')
tableData.value = []
pagination.total = 0
} finally {
tableLoading.value = false
}
}// 获取预计来佣列表
const fetchExpectedCommissionList = async (policyNo,pageNo=1,pageSize=100) => {
try {
// 构造接口请求参数
const params = {
pageNo: pageNo, // 注意:如果后端是从0开始的页码,需要减1
pageSize: pageSize,
policyNo: policyNo,
}
// 调用后台接口
const response = await getExpectedCommissionList(params)
// 处理接口响应
if (response.code === 200) {
const result = response.data
// 将接口返回的数据映射到表格
expectedCommissionList.value = result.records.map(record => ({
...record,
commissionStatusLabel: convertStatusToDict(2,record.status)
}))
} else {
// 接口返回错误信息
ElMessage.error(`获取数据失败: ${response.data.msg || '未知错误'}`)
expectedCommissionList.value = []
}
} catch (error) {
// 捕获网络或其他异常
console.error('请求失败:', error)
ElMessage.error('网络异常,请稍后重试')
expectedCommissionList.value = []
} finally {
}
}
// 增加通过表格排序,排序字段sortField,升降序sortOrder
const handleSortChange = (column) => {
pagination.sortField = column.prop
pagination.sortOrder = column.order === 'ascending' ? 'ascend' : 'descend'
fetchTableData()
}
// 处理查询
const handleSearch = () => {
pagination.currentPage = 1
fetchTableData()
ElMessage.success('查询成功')
}
// 重置表单
const resetForm = () => {
searchForm.name = ''
searchForm.status = ''
searchForm.policyBizId = ''
searchForm.policyNo = ''
searchForm.customerName = ''
searchForm.customerBizId = ''
searchForm.insurer = ''
searchForm.productCode = ''
searchForm.signDateRange = []
}
// 处理分页大小变化
const handleSizeChange = (val) => {
pagination.pageSize = val
fetchTableData()
}
// 处理分页页码变化
const handleCurrentChange = (val) => {
pagination.currentPage = val
fetchTableData()
}
// 处理表格选择变化
const handleSelectionChange = (rows) => {
selectedRows.value = rows
}
// 表格行样式
const tableRowClassName = ({ row }) => {
return row.status === 'inactive' ? 'row-inactive' : ''
}
// 处理文件选择
const handleFileChange = (file) => {
selectedFile.value = file.raw
}
// 处理导入 - 调整为multipart/form-data格式
const handleImport = async () => {
if (!selectedFile.value) {
ElMessage.warning('请先选择文件')
return
}
// 二次确认
ElMessageBox.confirm(
`确定要导入"${selectedFile.value.name}"吗?`,
'导入确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}
).then(async () => {
importLoading.value = true
try {
// 创建FormData对象,用于multipart/form-data格式
const formData = new FormData()
// 将文件添加到FormData,注意参数名要与后端保持一致
formData.append('file', selectedFile.value)
// 可以添加其他参数(如果后端需要)
// formData.append('otherParam', 'value')
// 调用上传接口
const response = await axios.post(
uploadUrl.value,
formData,
{
headers: {
'Content-Type': 'multipart/form-data', // 指定内容类型,
'Authorization': 'Bearer ' + getToken()
}
}
)
// 处理接口响应
if (response.data.code === 200) {
ElMessage.success('导入成功')
selectedFile.value = null
// 重新获取表格数据
fetchTableData()
} else {
ElMessage.error(`导入失败: ${response.data.msg || '未知错误'}`)
// 如果有错误详情,可以展示
if (response.data.msg) {
console.error('导入错误详情:', response.data.msg)
}
}
} catch (error) {
console.error('上传失败:', error)
ElMessage.error('文件上传失败,请稍后重试')
} finally {
importLoading.value = false
}
}).catch(() => {
// 用户取消导入
ElMessage.info('已取消导入')
})
}
// 下载模板
const downloadTemplate = () => {
// 下载地址
const templateUrl = 'https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/xlsx/2025/10/14/54ce715eabab4f1abd8652ba0fca0c51.xlsx'
// 修改下载文件名
const fileName = '保单导入模板.xlsx'
// 下载文件
window.open(templateUrl, '_blank', `download=${fileName}-${new Date().getTime()}`)
}
import { updateToPolicyLib } from '@/api/sign/underwritingMain'
// 处理更新至保单库
const handleUpdateToPolicyLib = () => {
ElMessageBox.confirm(
`确定要更新选中的 ${selectedRows.value.length} 条数据至保单库吗?`,
'更新确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
// 调用更新至保单库接口
updateToPolicyLib({
policyNoList: selectedRows.value.map(row => row.policyNo)
}).then(response => {
if (response.code === 200) {
ElMessage.success('更新成功')
} else {
ElMessage.error(`更新失败: ${response.msg || '未知错误'}`)
}
})
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消更新至保单库',
})
})
}
// 格式化文件大小
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
</script>
<style scoped>
.data-management-page {
padding: 20px;
/* max-width: 1600px; */
margin: 0 auto;
}
.page-header {
margin-bottom: 20px;
}
.search-card {
margin-bottom: 20px;
padding: 15px 20px;
}
.search-buttons {
display: flex;
gap: 10px;
}
.import-area {
margin-bottom: 20px;
}
.import-card {
padding: 10px;
background-color: #f6ffed;
border: 1px solid #b7eb8f;
}
.import-content {
display: flex;
flex-direction: column;
gap: 15px;
}
.import-actions {
display: flex;
align-items: center;
gap: 10px;
}
.upload-excel {
display: inline-block;
}
/* 文件信息显示样式 */
.file-info {
margin-top: 15px;
padding: 12px 16px;
background-color: #f5f7fa;
border-radius: 4px;
border: 1px solid #e4e7ed;
display: flex;
align-items: center;
justify-content: space-between;
}
.file-info-content {
display: flex;
align-items: center;
gap: 8px;
}
.file-info-content .el-icon {
color: #409eff;
font-size: 16px;
}
.file-name {
font-weight: 500;
color: #303133;
}
.file-size {
color: #909399;
font-size: 12px;
}
.confirm-import-btn {
margin-left: 12px;
}
/* 下载模板按钮样式 */
.download-template-btn {
color: #409eff;
font-size: 12px;
padding: 8px 12px;
}
.download-template-btn:hover {
background-color: #ecf5ff;
}
.table-card {
padding: 15px 20px;
}
.table-actions {
margin-bottom: 15px;
display: flex;
justify-content: space-between;
align-items: center;
}
.pagination {
margin-top: 15px;
text-align: right;
}
/* 禁用状态行样式 */
::v-deep .row-inactive {
background-color: #f5f5f5;
color: #9e9e9e;
}
.form-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.form-label {
font-size: 14px;
color: #4e5969;
font-weight: 500;
line-height: 1;
padding-left: 2px;
}
.search-row {
margin-bottom: 18px;
}
.search-row:last-child {
margin-bottom: 0;
}
.search-buttons {
display: flex;
gap: 10px;
justify-content: flex-start;
align-items: flex-end;
padding-bottom: 2px;
}
.advanced-search {
margin-top: 18px;
border-top: 1px dashed #e5e7eb;
padding-top: 18px;
}
.el-collapse-item__content {
padding-top: 15px !important;
}
/* 转介人详情样式 */
.broker-details {
max-height: 200px;
overflow-y: auto;
}
.broker-item {
padding: 8px 0;
}
.broker-item p {
margin: 4px 0;
font-size: 12px;
line-height: 1.4;
}
.broker-item strong {
color: #606266;
}
.no-broker {
color: #909399;
font-size: 12px;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.import-actions {
flex-wrap: wrap;
}
.download-template-btn {
margin-top: 10px;
}
.file-info {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.confirm-import-btn {
margin-left: 0;
align-self: flex-end;
}
}
@media (max-width: 992px) {
.search-card .el-row {
row-gap: 15px;
}
.search-card .el-col {
flex: 0 0 50%;
max-width: 50%;
}
.search-buttons {
justify-content: flex-start;
}
}
@media (max-width: 768px) {
.search-card .el-col {
flex: 0 0 100%;
max-width: 100%;
}
.table-actions {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.import-actions {
flex-direction: column;
align-items: flex-start;
}
.download-template-btn {
margin-top: 10px;
}
}
/* 防止表头换行 */
::v-deep .el-table .el-table__header-wrapper .el-table__cell {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 表头单元格内容不换行 */
::v-deep .el-table .el-table__header-wrapper .el-table__cell .cell {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
\ No newline at end of file
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