parent
8e63fe4b83
commit
7e291e6722
14 changed files with 1039 additions and 19 deletions
@ -0,0 +1,7 @@ |
||||
import axios from '@/utils/request'; |
||||
// 保险
|
||||
export const saveFund = async (data: Record<string, any>): Promise<any> => (await axios.post('/product/fundProducts/save', data)).data; |
||||
export const batchDeletion = async (data: number[]): Promise<any> => (await axios.post(`/product/fundProducts/batchDeletion`, data)).data; |
||||
export const fundProductList = async (data: Record<string, any>): Promise<any> => (await axios.post(`/product/fundProducts/fundProductList`, data)).data; |
||||
export const findById = async (id: number): Promise<any> => (await axios.post(`/product/fundProducts/findById?id=${id}`)).data; |
||||
export const getAListOfAShares = async (keyWord: string): Promise<any> => (await axios.post(`/product/aShares/data/getAListOfAShares?keyWord=${keyWord}`)).data; |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,596 @@ |
||||
<template> |
||||
<div> |
||||
<el-tabs v-model="curTab"> |
||||
<el-tab-pane :label="id ? '产品要素' : '新增产品'" |
||||
name="tab1"> |
||||
<el-form label-width="100px" |
||||
label-suffix=":" |
||||
class="form" |
||||
status-icon> |
||||
<el-form-item label="基金名称" |
||||
prop="fundName"> |
||||
<el-input placeholder="取个有吸引力的产品名,限20字。" |
||||
maxlength="20" |
||||
v-model="form.fundName"></el-input> |
||||
</el-form-item> |
||||
<el-form-item label="基金类型" |
||||
prop="fundType"> |
||||
<el-select v-model="form.fundType" |
||||
placeholder="请选择"> |
||||
<el-option v-for="item in config[1]?.subject?.itemList" |
||||
:key="item" |
||||
:label="item.options" |
||||
:value="item.itemId" /> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item label="募集规模"> |
||||
<div> |
||||
<el-input placeholder="请输入" |
||||
v-model="form.fundraisingScale"> |
||||
<template #append>万元</template> |
||||
</el-input> |
||||
</div> |
||||
</el-form-item> |
||||
<el-form-item label="运作方式" |
||||
prop="modeOfOperation"> |
||||
<el-select v-model="form.modeOfOperation" |
||||
placeholder="请选择"> |
||||
<el-option v-for="item in config[3]?.subject?.itemList" |
||||
:key="item" |
||||
:label="item.options" |
||||
:value="item.itemId" /> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item label="持股配置"> |
||||
<div class="flex-1"> |
||||
<p class="">输入心仪的A股代码或名称,组建属于你的股票基金,比重合计必须为100%,最多配置10支股票。</p> |
||||
<div class="flex"> |
||||
<p class="field-name w-[300px] mr-32">股票</p> |
||||
<p class="field-name w-[100px] mr-32">比重(%)</p> |
||||
<div class="field-name"> |
||||
<el-icon class="cursor-pointer" |
||||
:size="16" |
||||
color="#333" |
||||
@click="addStock"> |
||||
<Plus /> |
||||
</el-icon> |
||||
</div> |
||||
</div> |
||||
<div v-for="(item, i) in form.shareholdingAllocationsList" |
||||
:key="i" |
||||
class="flex items-center mb-2"> |
||||
<div class="w-[300px] mr-32"> |
||||
<el-select-v2 class="w-full" |
||||
v-model="item.stockCode" |
||||
filterable |
||||
remote |
||||
:remote-method="val => remoteMethod(val, item)" |
||||
clearable |
||||
:options="item.stocks" |
||||
:loading="loading" |
||||
placeholder="请输入A股代码或者名称" /> |
||||
</div> |
||||
<div class="w-[100px] mr-32"> |
||||
<el-input placeholder="请输入" |
||||
v-model="item.proportion"></el-input> |
||||
</div> |
||||
<div> |
||||
<el-icon class="cursor-pointer" |
||||
:size="16" |
||||
color="#333" |
||||
@click="delStock(i)"> |
||||
<Minus /> |
||||
</el-icon> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</el-form-item> |
||||
<el-form-item label="费率配置"> |
||||
<div class="flex-1"> |
||||
<p class="mt-1 mb-2 text-base">确认份额时长。</p> |
||||
<!-- 份额时长 --> |
||||
<div class="flex items-center mb-3"> |
||||
客户T时买入,T+ |
||||
<el-input class="w-[100px] mx-2" |
||||
placeholder="请输入" |
||||
v-model="form.buyingDuration"></el-input> |
||||
<el-select class="w-[100px] mr-2" |
||||
v-model="form.buyingUnit" |
||||
placeholder="请选择"> |
||||
<el-option v-for="item in config[5]?.recordChildren[0]?.recordChildren[1]?.subject?.itemList" |
||||
:key="item" |
||||
:value="item.options" /> |
||||
</el-select> |
||||
确认申购份额期间,资金进入冻结账户。 |
||||
</div> |
||||
<div class="flex items-center"> |
||||
客户T时卖出,T+ |
||||
<el-input class="w-[100px] mx-2" |
||||
placeholder="请输入" |
||||
v-model="form.sellingTime"></el-input> |
||||
<el-select class="w-[100px] mr-2" |
||||
v-model="form.soldUnit" |
||||
placeholder="请选择"> |
||||
<el-option v-for="item in config[5]?.recordChildren[0]?.recordChildren[1]?.subject?.itemList" |
||||
:key="item" |
||||
:value="item.options" /> |
||||
</el-select> |
||||
确认赎回金额期间,资金进入冻结账户。 |
||||
</div> |
||||
|
||||
<!-- 买入费率 --> |
||||
<p class="mt-10 mb-2 text-xs text-[#ef3838]">买入费率(单笔费率应小于5.00%,5000元)</p> |
||||
<div class="flex"> |
||||
<p class="field-name w-[400px] mr-32">金额</p> |
||||
<p class="field-name w-[100px] mr-32">费率</p> |
||||
<div class="field-name"> |
||||
<el-icon class="cursor-pointer" |
||||
:size="16" |
||||
color="#333" |
||||
@click="addRatio(0)"> |
||||
<Plus /> |
||||
</el-icon> |
||||
</div> |
||||
</div> |
||||
<div v-for="(item, i) in form.buySellRatioList0" |
||||
:key="i" |
||||
class="flex items-center mb-2"> |
||||
<div class="w-[400px] mr-10"> |
||||
<el-input v-if="i" |
||||
class="w-[100px] mr-2" |
||||
placeholder="请输入" |
||||
disabled |
||||
v-model="form.buySellRatioList0[i - 1].input2"></el-input> |
||||
<el-input v-else |
||||
class="w-[100px] mr-2" |
||||
placeholder="请输入" |
||||
disabled |
||||
v-model="item.input1"></el-input> |
||||
{{ i ? '万' : ' ' }} |
||||
<span class="mx-2"> |
||||
<= 买入金额</span> |
||||
<template v-if="i !== form.buySellRatioList0.length - 1"> |
||||
< |
||||
<el-input class="w-[100px] mx-2" |
||||
placeholder="请输入" |
||||
v-model="item.input2"></el-input> |
||||
万 |
||||
</template> |
||||
</div> |
||||
|
||||
<div class="mr-32"> |
||||
<el-input class="w-[100px] mr-2" |
||||
placeholder="请输入" |
||||
v-model="item.rate"></el-input> |
||||
<el-select class="w-[80px]" |
||||
v-model="item.rateUnit" |
||||
placeholder="请选择"> |
||||
<el-option v-for="item in units" |
||||
:key="item" |
||||
:value="item" /> |
||||
</el-select> |
||||
</div> |
||||
|
||||
<div v-if="i"> |
||||
<el-icon class="cursor-pointer" |
||||
:size="16" |
||||
color="#333" |
||||
@click="delRatio(0, i)"> |
||||
<Minus /> |
||||
</el-icon> |
||||
</div> |
||||
</div> |
||||
<div class="mt-5 text-xs text-[#7a7a7a]"> |
||||
<p>申购计算:</p> |
||||
<p class="my-2">净申购金额 = 申购金额 / (1 + 申购费用)</p> |
||||
<p>申购费用 = 申购金额 - 净申购金额</p> |
||||
<p class="mt-2">申购份额 = 净申购金额 / T时基金份额净值</p> |
||||
</div> |
||||
|
||||
<!-- 运作费率 --> |
||||
<p class="mt-10 mb-2 text-xs text-[#ef3838]">运作费率(单笔费率应小于5.00%)</p> |
||||
<div class="flex"> |
||||
<span class="w-[140px]">管理费</span> |
||||
<el-input class="w-[100px] mr-2" |
||||
placeholder="请输入" |
||||
v-model="form.operationManagementRate"></el-input>% |
||||
</div> |
||||
<div class="flex my-2"> |
||||
<span class="w-[140px]">托管费</span> |
||||
<el-input class="w-[100px] mr-2" |
||||
placeholder="请输入" |
||||
v-model="form.operatingEscrowRate"></el-input>% |
||||
</div> |
||||
<div class="flex"> |
||||
<span class="w-[140px]">销售服务费</span> |
||||
<el-input class="w-[100px] mr-2" |
||||
placeholder="请输入" |
||||
v-model="form.operatingSalesServiceRates"></el-input>% |
||||
</div> |
||||
<div class="mt-5 text-xs text-[#7a7a7a]"> |
||||
<p>运作费计算:</p> |
||||
<p class="my-2">管理费 = (前一日的基金资产净值 x 管理费率)/ 365</p> |
||||
<p>托管费 = (前一日的基金资产净值 x 管理费率)/ 365</p> |
||||
<p class="mt-2">销售服务费 = (前一日的基金资产净值 x 管理费率)/ 365</p> |
||||
</div> |
||||
|
||||
<!-- 赎回费率 --> |
||||
<p class="mt-10 mb-2 text-xs text-[#ef3838]">赎回费率(单笔费率应小于5.00%,5000元)</p> |
||||
<div class="flex"> |
||||
<p class="field-name w-[600px] mr-32">持有时长</p> |
||||
<p class="field-name w-[100px] mr-32">费率</p> |
||||
<div class="field-name"> |
||||
<el-icon class="cursor-pointer" |
||||
:size="16" |
||||
color="#333" |
||||
@click="addRatio(1)"> |
||||
<Plus /> |
||||
</el-icon> |
||||
</div> |
||||
</div> |
||||
<div v-for="(item, i) in form.buySellRatioList1" |
||||
:key="i" |
||||
class="flex items-center mb-2"> |
||||
<div class="w-[600px] mr-10"> |
||||
<el-input v-if="i" |
||||
class="w-[100px] mr-2" |
||||
placeholder="请输入" |
||||
disabled |
||||
v-model="form.buySellRatioList1[i - 1].input2"></el-input> |
||||
<el-input v-else |
||||
class="w-[100px] mr-2" |
||||
placeholder="请输入" |
||||
disabled |
||||
v-model="item.input1"></el-input> |
||||
|
||||
<el-select class="w-[100px]" |
||||
v-model="item.soldUnit1" |
||||
placeholder="请选择"> |
||||
<el-option v-for="item in times" |
||||
:key="item" |
||||
:value="item" /> |
||||
</el-select> |
||||
|
||||
<span class="mx-2"> |
||||
<= 持有时长</span> |
||||
<template v-if="i !== form.buySellRatioList1.length - 1"> |
||||
< |
||||
<el-input class="w-[100px] mx-2" |
||||
placeholder="请输入" |
||||
v-model="item.input2"></el-input> |
||||
|
||||
<el-select class="w-[100px]" |
||||
v-model="item.soldUnit2" |
||||
placeholder="请选择"> |
||||
<el-option v-for="item in times" |
||||
:key="item" |
||||
:value="item" /> |
||||
</el-select> |
||||
</template> |
||||
</div> |
||||
|
||||
<div class="mr-32"> |
||||
<el-input class="w-[100px] mr-2" |
||||
placeholder="请输入" |
||||
v-model="item.rate"></el-input> |
||||
<el-select class="w-[80px]" |
||||
v-model="item.rateUnit" |
||||
placeholder="请选择"> |
||||
<el-option v-for="item in units" |
||||
:key="item" |
||||
:value="item" /> |
||||
</el-select> |
||||
</div> |
||||
|
||||
<div> |
||||
<el-icon class="cursor-pointer" |
||||
:size="16" |
||||
color="#333" |
||||
@click="delRatio(1, i)"> |
||||
<Minus /> |
||||
</el-icon> |
||||
</div> |
||||
</div> |
||||
<div class="mt-5 text-xs text-[#7a7a7a]"> |
||||
<p>赎回计算:</p> |
||||
<p class="my-2">赎回费用 = 赎回总额 x 赎回费率</p> |
||||
<p>赎回金额 = 赎回总额 - 赎回费用</p> |
||||
</div> |
||||
</div> |
||||
</el-form-item> |
||||
<div class="flex justify-end"> |
||||
<div class="submit" |
||||
@click="submit">完成</div> |
||||
</div> |
||||
</el-form> |
||||
</el-tab-pane> |
||||
</el-tabs> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { ref, reactive, computed, watch, onMounted, defineEmits } from 'vue'; |
||||
import { ElMessage } from 'element-plus'; |
||||
import { Plus, Minus } from '@element-plus/icons-vue'; |
||||
import { insuranceProductDetails, addInsuranceProducts } from '@/api/insurance'; |
||||
import { getAListOfAShares, saveFund } from '@/api/fund'; |
||||
import { getProcessInformationBasedOnRoles, addOperation } from '@/api/judgment'; |
||||
import { useRouter, useRoute } from 'vue-router'; |
||||
import { handleId } from '@/utils/common'; |
||||
import { getStatus } from '@/store/useProduct'; |
||||
import Info from './Info.vue'; |
||||
import Cookies from 'js-cookie'; |
||||
|
||||
const emit = defineEmits(['getList']); |
||||
|
||||
const router = useRouter(); |
||||
const route = useRoute(); |
||||
const id = computed(() => route.query.id); |
||||
const projectId = +Cookies.get('sand-projectId'); |
||||
const levelId = +Cookies.get('sand-level'); |
||||
const curTab = ref<string>('tab1'); |
||||
const config = ref<any[]>([]); |
||||
const info = ref<Record<string, any>>(null); |
||||
const units = ref<string[]>(['%', '元']); |
||||
const times = ref<string[]>(['分钟', '天', '小时', '月']); |
||||
const loading = ref<boolean>(false); |
||||
const form = reactive({ |
||||
checkPointId: levelId, |
||||
projectId, |
||||
buyingDuration: '', |
||||
buyingUnit: '天', |
||||
fundName: '', |
||||
fundType: '', |
||||
fundraisingScale: '', |
||||
modeOfOperation: '', |
||||
operatingEscrowRate: '', |
||||
operatingSalesServiceRates: '', |
||||
operationManagementRate: '', |
||||
sellingTime: '', |
||||
soldUnit: '天', |
||||
buySellRatioList0: [ |
||||
{ |
||||
input1: '0', |
||||
input2: '', |
||||
rate: '', |
||||
rateUnit: '%', |
||||
type: 0, |
||||
}, |
||||
{ |
||||
input1: '', |
||||
input2: '', |
||||
rate: '', |
||||
rateUnit: '%', |
||||
type: 0, |
||||
}, |
||||
], |
||||
buySellRatioList1: [ |
||||
{ |
||||
input1: '0', |
||||
input2: '', |
||||
rate: '', |
||||
rateUnit: '%', |
||||
soldUnit1: '分钟', |
||||
soldUnit2: '分钟', |
||||
type: 1, |
||||
}, |
||||
{ |
||||
input1: '', |
||||
input2: '', |
||||
rate: '', |
||||
rateUnit: '%', |
||||
soldUnit1: '分钟', |
||||
soldUnit2: '分钟', |
||||
type: 1, |
||||
}, |
||||
], |
||||
shareholdingAllocationsList: [ |
||||
{ |
||||
latestPrice: '', |
||||
proportion: '', |
||||
stockCode: '', |
||||
stockName: '', |
||||
stocks: [], |
||||
}, |
||||
], |
||||
}); |
||||
|
||||
// 配置项 |
||||
const getConfig = async () => { |
||||
const { process } = await getProcessInformationBasedOnRoles(1161); |
||||
config.value = process; |
||||
}; |
||||
// 详情 |
||||
const getDetail = async () => { |
||||
if (id.value) { |
||||
try { |
||||
const { data } = await insuranceProductDetails(id.value); |
||||
info.value = data; |
||||
} finally { |
||||
} |
||||
} |
||||
}; |
||||
// 新增持股 |
||||
const addStock = () => { |
||||
form.shareholdingAllocationsList.length < 10 |
||||
? form.shareholdingAllocationsList.push({ |
||||
latestPrice: '', |
||||
proportion: '', |
||||
stockCode: '', |
||||
stockName: '', |
||||
stocks: [], |
||||
}) |
||||
: ElMessage.error('持股最多不能超过10只!'); |
||||
}; |
||||
// 删除持股 |
||||
const delStock = (i: number) => { |
||||
form.shareholdingAllocationsList.length > 1 && form.shareholdingAllocationsList.splice(i, 1); |
||||
}; |
||||
// 查询股票 |
||||
const remoteMethod = (query: string, item: Record<string, any>) => { |
||||
if (query !== '') { |
||||
loading.value = true; |
||||
setTimeout(async () => { |
||||
loading.value = false; |
||||
const { aShares } = await getAListOfAShares(query); |
||||
aShares.forEach((e) => { |
||||
e.label = e.f14; |
||||
e.value = e.f12; |
||||
}); |
||||
item.stocks = aShares; |
||||
}, 200); |
||||
} else { |
||||
item.stocks = []; |
||||
} |
||||
}; |
||||
|
||||
// 新增一行费率 |
||||
const addRatio = (type: number) => { |
||||
if (form['buySellRatioList' + type].length === 5) { |
||||
ElMessage.error('规则最多只能添加5条!'); |
||||
} else { |
||||
const temp = { |
||||
input1: '', |
||||
input2: '', |
||||
rate: '', |
||||
rateUnit: '%', |
||||
type, |
||||
}; |
||||
if (type) { |
||||
temp.soldUnit1 = '分钟'; |
||||
temp.soldUnit2 = '分钟'; |
||||
} |
||||
form['buySellRatioList' + type].push(temp); |
||||
} |
||||
}; |
||||
// 删除一行费率 |
||||
const delRatio = (type: number, i: number) => { |
||||
form['buySellRatioList' + type].length > 1 && form['buySellRatioList' + type].splice(i, 1); |
||||
}; |
||||
|
||||
watch( |
||||
() => route.query, |
||||
() => { |
||||
getDetail(); |
||||
}, |
||||
{ |
||||
immediate: true, |
||||
}, |
||||
); |
||||
|
||||
const getId = (str: string): string | number => { |
||||
const result = config.value[5]?.recordChildren[0]?.recordChildren[1]?.subject?.itemList?.find((e) => e.options === str); |
||||
return result?.itemId || ''; |
||||
}; |
||||
// 新增判分记录 |
||||
const addRecord = async (data: Record<string, any>) => { |
||||
const preIds = `1,${levelId},1161`; // 1,关卡id,角色(这个页面是基金产品经理新增产品) |
||||
const rule = [ |
||||
handleId(1162, 308, data.fundName, preIds + ',1162', 3), |
||||
handleId(1163, 309, 764, preIds + ',1163', 1), |
||||
handleId(1164, 310, data.fundraisingScale, preIds + ',1164', 3), |
||||
handleId(1165, 311, 765, preIds + ',1165', 1), |
||||
]; |
||||
|
||||
const stockId = 1167; |
||||
data.shareholdingAllocationsList.forEach((e, i) => { |
||||
rule.push(handleId(1177, 312, e.stockName, `${preIds},1166,${stockId + i},1177`, 3)); |
||||
rule.push(handleId(1178, 313, e.proportion + '%', `${preIds},1166,${stockId + i},1178`, 3)); |
||||
}); |
||||
rule.push( |
||||
handleId(1181, 314, data.buyingDuration, preIds + ',1179,1180,1181', 3), |
||||
handleId(1182, 315, getId(data.buyingUnit), preIds + ',1179,1180,1182', 3), |
||||
handleId(1184, 314, data.sellingTime, preIds + ',1179,1183,1184', 3), |
||||
handleId(1185, 315, getId(data.soldUnit), preIds + ',1179,1183,1185', 3), |
||||
); |
||||
|
||||
// 处理买入费率 |
||||
const buyId = 1187; |
||||
data.buySellRatioList0.forEach((e, i) => { |
||||
e.amountOrDuration && rule.push(handleId(1192, 316, e.amountOrDuration, `${preIds},1179,1186,${buyId + i},1192`, 3)); |
||||
e.rate && rule.push(handleId(1193, 318, e.rate + e.rateUnit, `${preIds},1179,1186,${buyId + i},1193`, 3)); |
||||
}); |
||||
|
||||
// 处理赎回费率 |
||||
const sellId = 1200; |
||||
data.buySellRatioList1.forEach((e, i) => { |
||||
e.amountOrDuration && rule.push(handleId(1205, 314, e.amountOrDuration, `${preIds},1179,1199,${sellId + i},1205`, 3)); |
||||
e.rate && rule.push(handleId(1206, 318, e.rate + e.rateUnit, `${preIds},1179,1199,${sellId + i},1206`, 3)); |
||||
}); |
||||
|
||||
rule.push( |
||||
handleId(1196, 319, data.operationManagementRate + '%', preIds + ',1179,1195,1196', 3), |
||||
handleId(1197, 320, data.operatingEscrowRate + '%', preIds + ',1179,1195,1197', 3), |
||||
handleId(1198, 321, data.operatingSalesServiceRates + '%', preIds + ',1179,1195,1198', 3), |
||||
); |
||||
|
||||
await addOperation({ |
||||
checkpointId: levelId, |
||||
parentId: preIds, |
||||
lcJudgmentRuleReq: rule, |
||||
projectId, |
||||
}); |
||||
}; |
||||
// 提交 |
||||
const submit = async () => { |
||||
try { |
||||
const param = JSON.parse(JSON.stringify(form)); |
||||
|
||||
if (!param.fundName) return ElMessage.error('请输入保险名称'); |
||||
if (!param.fundType) return ElMessage.error('请选择基金类型'); |
||||
if (!param.fundraisingScale) return ElMessage.error('请输入募集规模'); |
||||
if (!param.modeOfOperation) return ElMessage.error('请选择运作方式'); |
||||
if (!param.shareholdingAllocationsList.length || !param.shareholdingAllocationsList.filter((e) => e.stockCode).length) return ElMessage.error('请配置持股'); |
||||
|
||||
// 计算持股权重是否等于100 |
||||
let sum = 0; |
||||
param.shareholdingAllocationsList.forEach((e) => { |
||||
if (e.stockCode && e.proportion) { |
||||
sum += +e.proportion; |
||||
} |
||||
}); |
||||
if (sum !== 100) return ElMessage.error('持股权重总和需等于100,请重新配置'); |
||||
if (!param.buyingDuration || !param.sellingTime) return ElMessage.error('请输入份额时长'); |
||||
if (!param.operationManagementRate) return ElMessage.error('请输入管理费'); |
||||
if (!param.operatingEscrowRate) return ElMessage.error('请输入托管费'); |
||||
if (!param.operatingSalesServiceRates) return ElMessage.error('请输入销售服务费'); |
||||
|
||||
// 持股配置处理 |
||||
param.shareholdingAllocationsList.forEach((e) => { |
||||
if (e.stockCode) { |
||||
const item = e.stocks.find((n) => n.f12 === e.stockCode); |
||||
e.stockName = item.f14; |
||||
e.latestPrice = item.f18; |
||||
delete e.stocks; |
||||
} |
||||
}); |
||||
|
||||
// 处理费率 |
||||
param.buySellRatioList0.forEach((e, i) => { |
||||
e.amountOrDuration = i ? param.buySellRatioList0[i - 1].input2 : e.input1; |
||||
if (i !== param.buySellRatioList0.length - 1) e.amountOrDuration += `-${e.input2}万`; |
||||
}); |
||||
param.buySellRatioList1.forEach((e, i) => { |
||||
e.amountOrDuration = i ? param.buySellRatioList1[i - 1].input2 + param.buySellRatioList1[i - 1].soldUnit2 : e.input1; |
||||
if (i !== param.buySellRatioList1.length - 1) e.amountOrDuration += `-${e.input2}${e.soldUnit2}`; |
||||
}); |
||||
param.buySellRatioList = [...param.buySellRatioList0, ...param.buySellRatioList1]; |
||||
|
||||
const recordParam = JSON.parse(JSON.stringify(param)); |
||||
delete param.buySellRatioList0; |
||||
delete param.buySellRatioList1; |
||||
await saveFund(param); |
||||
addRecord(recordParam); |
||||
ElMessage.success('提交成功!'); |
||||
emit('getList', 1); |
||||
} finally { |
||||
} |
||||
}; |
||||
onMounted(() => { |
||||
getConfig(); |
||||
}); |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import url(../../../styles/form.scss); |
||||
</style> |
@ -0,0 +1,137 @@ |
||||
<template> |
||||
<div class="block flex" |
||||
style="padding-top: 0"> |
||||
<div class="left w-[241px] min-w-[241px] pr-5 py-4"> |
||||
<div class="flex justify-center items-center py-2 mb-3 text-sm text-[#006BFF] bg-[rgba(0,107,255,0.1)] border border-solid border-[#006BFF] rounded-[10px] cursor-pointer" |
||||
@click="toAdd"> |
||||
<el-icon class="mr-1" |
||||
:size="16" |
||||
color="#006BFF"> |
||||
<Plus /> |
||||
</el-icon> |
||||
新增产品 |
||||
</div> |
||||
<div class="flex justify-end mb-4"> |
||||
<img src="@/assets/images/fold.png" |
||||
alt="" |
||||
class="cursor-pointer" |
||||
@click="toList" /> |
||||
</div> |
||||
<ul class="products"> |
||||
<li v-for="(item, i) in list" |
||||
:key="i" |
||||
:class="{ active: item.id === id }" |
||||
@click="switchProduct(item.id)"> |
||||
<el-popconfirm title="您确定删除吗?" |
||||
@confirm="handleDelete(item.id)"> |
||||
<template #reference> |
||||
<img src="@/assets/images/trash.png" |
||||
alt="" |
||||
class="del" /> |
||||
</template> |
||||
</el-popconfirm> |
||||
|
||||
<h6>{{ item.fundName }}</h6> |
||||
<p class="type">{{ item.fundraisingScale }}万募集规模</p> |
||||
<p class="type">买入费率{{ item.buying }},赎回费率{{ item.sale }}</p> |
||||
<p class="date">创建日期:{{ item.createTime.split(' ')[0] }}</p> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
<div class="right flex-1 px-5 pt-2"> |
||||
<detail v-if="action === 'detail'"></detail> |
||||
<add v-else-if="action === 'add'" |
||||
@getList="getList"></add> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { computed, onMounted, ref, watch } from 'vue'; |
||||
import { ElMessage } from 'element-plus'; |
||||
import { Plus } from '@element-plus/icons-vue'; |
||||
import { fundProductList, batchDeletion } from '@/api/fund'; |
||||
import { useRouter, useRoute } from 'vue-router'; |
||||
import Detail from './Detail.vue'; |
||||
import Add from './Add.vue'; |
||||
import Cookies from 'js-cookie'; |
||||
|
||||
const router = useRouter(); |
||||
const route = useRoute(); |
||||
const action = ref<any>(''); |
||||
const projectId = +Cookies.get('sand-projectId'); |
||||
const levelId = +Cookies.get('sand-level'); |
||||
|
||||
const list = ref<Array<any>>([]); |
||||
const loading = ref<boolean>(false); |
||||
const id = computed(() => +route.query.id); |
||||
// 列表 |
||||
const getList = async (first: any) => { |
||||
loading.value = true; |
||||
try { |
||||
const { data } = await fundProductList({ pageNum: 1, pageSize: 1000, checkPointId: levelId, projectId }); |
||||
list.value = data.data.records; |
||||
first && list.value.length && switchProduct(list.value[0].id); |
||||
} finally { |
||||
loading.value = false; |
||||
} |
||||
}; |
||||
onMounted(() => { |
||||
getList(); |
||||
}); |
||||
|
||||
watch( |
||||
route, |
||||
(route: any) => { |
||||
action.value = route.params.action; |
||||
}, |
||||
{ |
||||
immediate: true, |
||||
}, |
||||
); |
||||
// 产品切换 |
||||
const switchProduct = (id: number) => { |
||||
router.push(`/product/fund/detail?id=` + id); |
||||
}; |
||||
// 新增 |
||||
const toAdd = () => { |
||||
router.push(`/product/fund/add`); |
||||
}; |
||||
// 返回列表 |
||||
const toList = () => { |
||||
router.push(`/product/fund`); |
||||
}; |
||||
const handleDelete = async (id: number) => { |
||||
await batchDeletion([id]); |
||||
getList(1); |
||||
ElMessage.success('删除成功!'); |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.left { |
||||
border-right: 1px solid #e9eff2; |
||||
} |
||||
.products { |
||||
@apply max-h-[calc(100vh-205px)] pr-1 overflow-auto; |
||||
li { |
||||
@apply relative p-5 pt-7 mb-5 rounded-[10px] cursor-pointer border border-solid border-[transparent] bg-[url('@/assets/images/10.png')] bg-[length:100%_100%] bg-no-repeat; |
||||
&.active { |
||||
@apply border-[#CAE0FF]; |
||||
} |
||||
} |
||||
.del { |
||||
@apply absolute top-0 right-0; |
||||
} |
||||
h6 { |
||||
@apply text-[#14436b]; |
||||
} |
||||
.type, |
||||
.status { |
||||
@apply my-[15px] text-sm text-[#333]; |
||||
} |
||||
.date { |
||||
@apply text-sm text-[#8798a9]; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,21 @@ |
||||
<template> |
||||
<div> |
||||
<el-tabs v-model="curTab"> |
||||
<el-tab-pane label="产品详情" |
||||
name="tab1"> |
||||
<info /> |
||||
</el-tab-pane> |
||||
</el-tabs> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { ref } from 'vue'; |
||||
import Info from './Info.vue'; |
||||
|
||||
const curTab = ref<string>('tab1'); |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
@import url(../../../styles/form.scss); |
||||
</style> |
@ -0,0 +1,105 @@ |
||||
<template> |
||||
<div class="info mt-2"> |
||||
<h2 class="text-center text-lg">{{ info.fundName }}</h2> |
||||
<p class="text">基金类型:{{ info.fundType }}</p> |
||||
<p class="text">基金规模:{{ info.fundraisingScale }}万元</p> |
||||
<p class="text">运作方式:{{ info.modeOfOperation }}</p> |
||||
<p class="text mt-10">持股配置:</p> |
||||
<div class="text"> |
||||
<div class="flex"> |
||||
<p class="w-[130px] mr-32">股票</p> |
||||
<p class="">比重(%)</p> |
||||
</div> |
||||
<div v-for="(item, i) in info.shareholdingAllocationsList" |
||||
:key="item" |
||||
class="flex"> |
||||
<p class="w-[130px] mr-32">{{ item.stockName }}({{ item.stockCode }})</p> |
||||
<p class="">{{ item.proportion }}</p> |
||||
</div> |
||||
</div> |
||||
<p class="text mt-10">费率配置:</p> |
||||
<p class="text">客户T时买入,T+{{ info.buyingDuration }}{{ info.buyingUnit }}确认申购份额期间,资金进入冻结账户。</p> |
||||
<p class="text">客户T时卖出,T+{{ info.sellingTime }}{{ info.soldUnit }}确认赎回金额期间,资金进入冻结账户。</p> |
||||
|
||||
<p class="text mt-5">买入费率</p> |
||||
<div class="text"> |
||||
<div class="flex"> |
||||
<p class="w-[130px] mr-32">金额</p> |
||||
<p class="">费率</p> |
||||
</div> |
||||
<div v-for="(item, i) in info.purchaseRateList" |
||||
:key="item" |
||||
class="flex"> |
||||
<p class="w-[130px] mr-32">{{ item.amountOrDuration }}</p> |
||||
<p class="">{{ item.rate }}{{ item.rateUnit |
||||
}}</p> |
||||
</div> |
||||
</div> |
||||
|
||||
<p class="text mt-5">管理费{{ info.operationManagementRate }}%</p> |
||||
<p class="text">托管费{{ info.operatingEscrowRate }}%</p> |
||||
<p class="text">销售服务费{{ info.operatingSalesServiceRates }}%</p> |
||||
|
||||
<p class="text mt-5">赎回费率</p> |
||||
<div class="text"> |
||||
<div class="flex"> |
||||
<p class="w-[130px] mr-32">持有时长</p> |
||||
<p class="">费率</p> |
||||
</div> |
||||
<div v-for="(item, i) in info.sellingRateList" |
||||
:key="item" |
||||
class="flex"> |
||||
<p class="w-[130px] mr-32">{{ item.amountOrDuration }}</p> |
||||
<p class="">{{ item.rate }}{{ item.rateUnit }}</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { ref, computed, watch, onMounted } from 'vue'; |
||||
import { findById } from '@/api/fund'; |
||||
import { useRouter, useRoute } from 'vue-router'; |
||||
|
||||
const router = useRouter(); |
||||
const route = useRoute(); |
||||
const id = computed(() => +route.query.id); |
||||
const info = ref<Record<string, any>>({}); |
||||
|
||||
// 详情 |
||||
const getDetail = async () => { |
||||
if (id.value) { |
||||
try { |
||||
const { data } = await findById(id.value); |
||||
info.value = data; |
||||
} finally { |
||||
} |
||||
} |
||||
}; |
||||
watch( |
||||
() => route.query, |
||||
() => { |
||||
getDetail(); |
||||
}, |
||||
{ |
||||
immediate: true, |
||||
}, |
||||
); |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.info { |
||||
.step-name { |
||||
@apply mb-3 text-sm font-semibold text-[#006bff]; |
||||
} |
||||
.line { |
||||
@apply flex mb-2; |
||||
} |
||||
.label { |
||||
@apply mr-1 text-sm font-semibold text-[#333] leading-[32px]; |
||||
} |
||||
.text { |
||||
@apply text-sm text-[#333] leading-[32px]; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,155 @@ |
||||
<template> |
||||
<div class="block"> |
||||
<div class="flex justify-between items-center mb-5"> |
||||
<search v-model="params.fundName" |
||||
@change="initList"></search> |
||||
<div class="filter"> |
||||
<div class="add-btn" |
||||
@click="toAdd"> |
||||
<img src="@/assets/images/plus.png" |
||||
alt="" |
||||
class="icon" /> |
||||
新增产品 |
||||
</div> |
||||
<img src="@/assets/images/9.png" |
||||
alt="" |
||||
class="ml-4 cursor-pointer" |
||||
@click="toCardList" /> |
||||
</div> |
||||
</div> |
||||
<el-table ref="table" |
||||
v-loading="loading" |
||||
:data="list" |
||||
@sort-change="handleSort"> |
||||
<el-table-column prop="fundName" |
||||
label="基金名称" |
||||
min-width="150"></el-table-column> |
||||
<el-table-column prop="fundCode" |
||||
label="基金代码" |
||||
min-width="100"></el-table-column> |
||||
<el-table-column prop="fundType" |
||||
label="基金类型" |
||||
min-width="100"></el-table-column> |
||||
<el-table-column prop="fundraisingScale" |
||||
label="募集规模" |
||||
min-width="100"></el-table-column> |
||||
<el-table-column prop="modeOfOperation" |
||||
label="运作方式" |
||||
min-width="100"></el-table-column> |
||||
<el-table-column prop="buying" |
||||
label="买入费率" |
||||
min-width="120"></el-table-column> |
||||
<el-table-column prop="sale" |
||||
label="赎回费率" |
||||
min-width="120"></el-table-column> |
||||
<el-table-column prop="createTime" |
||||
label="创建日期" |
||||
width="180" |
||||
sortable="custom"></el-table-column> |
||||
<el-table-column label="操作" |
||||
width="140"> |
||||
<template #default="{ row }"> |
||||
<el-popconfirm title="您确定删除吗?" |
||||
@confirm.stop="handleDelete(row.id)"> |
||||
<template #reference> |
||||
<el-button type="text" |
||||
size="small">删除</el-button> |
||||
</template> |
||||
</el-popconfirm> |
||||
<el-button type="text" |
||||
@click="toDetail(`/product/fund/detail`, row.id)" |
||||
size="small">产品详情</el-button> |
||||
</template></el-table-column> |
||||
</el-table> |
||||
<el-pagination v-model:currentPage="currentPage" |
||||
v-model:pageSize="pageSize" |
||||
:total="total" |
||||
:page-sizes="pageSizes" |
||||
:layout="pageLayout" |
||||
@size-change="getList()" |
||||
@current-change="getList()" |
||||
small |
||||
background |
||||
class="px-3 py-2 justify-end"></el-pagination> |
||||
</div> |
||||
</template> |
||||
|
||||
<script setup lang="ts"> |
||||
import { computed, onMounted, ref, reactive, watch } from 'vue'; |
||||
import { ElMessage } from 'element-plus'; |
||||
import { pageSizes, pageLayout, toParams, resetParams } from '@/utils/common'; |
||||
import { fundProductList, batchDeletion } from '@/api/fund'; |
||||
import Search from '@/components/Search.vue'; |
||||
import { useRouter, useRoute } from 'vue-router'; |
||||
import Cookies from 'js-cookie'; |
||||
import { productState, getExpertStatus, getStatus } from '@/store/useProduct'; |
||||
|
||||
const router = useRouter(); |
||||
const route = useRoute(); |
||||
const projectId = +Cookies.get('sand-projectId'); |
||||
const levelId = +Cookies.get('sand-level'); |
||||
const params = reactive({ |
||||
createDateSort: '', |
||||
fundName: '', |
||||
checkPointId: levelId, |
||||
projectId, |
||||
}); |
||||
const currentPage = ref<number>(1); |
||||
const pageSize = ref<number>(10); |
||||
const total = ref<number>(0); |
||||
const table = ref<any>(); |
||||
|
||||
const list = ref<Array<any>>([]); |
||||
const loading = ref<boolean>(false); |
||||
const stop = () => {}; |
||||
// 列表 |
||||
const getList = async () => { |
||||
loading.value = true; |
||||
try { |
||||
const { data } = await fundProductList({ pageNum: currentPage.value, pageSize: pageSize.value, ...toParams(params) }); |
||||
list.value = data.data.records; |
||||
total.value = data.data.total; |
||||
} finally { |
||||
loading.value = false; |
||||
} |
||||
}; |
||||
// 重置列表 |
||||
const initList = async () => { |
||||
currentPage.value = 1; |
||||
getList(); |
||||
}; |
||||
onMounted(() => { |
||||
getList(); |
||||
}); |
||||
|
||||
watch([params, () => route.query], initList); |
||||
const handleSort = ({ column, prop, order }: { column: any; prop: string; order: string }) => { |
||||
params.createDateSort = order === 'descending' ? 'desc' : order === 'ascending' ? 'asc' : ''; |
||||
getList(); |
||||
}; |
||||
// 新增 |
||||
const toAdd = () => { |
||||
router.push({ |
||||
path: `/product/fund/add`, |
||||
}); |
||||
}; |
||||
// 产品详情 |
||||
const toDetail = async (path: string, id: number) => { |
||||
router.push({ |
||||
path, |
||||
query: { |
||||
id, |
||||
}, |
||||
}); |
||||
}; |
||||
// 卡片模式 |
||||
const toCardList = () => { |
||||
router.push('/product/fund/detail'); |
||||
}; |
||||
|
||||
const handleDelete = async (id: number) => { |
||||
await batchDeletion([id]); |
||||
getList(); |
||||
ElMessage.success('删除成功!'); |
||||
}; |
||||
</script> |
Loading…
Reference in new issue