首先贴一下生成代码方法, 这里其实踩了很多坑, 最开始使用了自己解析模板, 但是出现各种兼容问题, 最后还是使用了vue的模板编译的功能实现的, 自己写的话太坑了
import * as Vue from 'vue'
import {compile} from '@vue/compiler-dom'
const handleVueTemplate = (internalValue, cb) => {
// 提取模板字符串中的各部分
const templateMatch = internalValue.match(/<template>([\s\S]*?)<\/template>/i);
const scriptMatch = internalValue.match(/<script>([\s\S]*?)<\/script>/i) ||
internalValue.match(/<script setup>([\s\S]*?)<\/script>/i);
const styleMatch = internalValue.match(/<style[^>]*>([\s\S]*?)<\/style>/i);
let template = templateMatch ? templateMatch[1] : '';
let script = scriptMatch ? scriptMatch[1] : '';
let style = styleMatch ? styleMatch[1] : '';
// 创建预览HTML
let previewContent = '';
// 添加样式
if (style) {
previewContent += `<style>${style}</style>`;
}
// 如果有脚本,提取数据并应用到预览中
let dataObj = {};
if (script) {
try {
// 提取data部分 - 支持两种格式:data() { return {...} } 和 data: {...}
let dataMatch = script.match(/data\s*\(\)\s*{\s*return\s*({[\s\S]*?});?\s*}/i);
if (!dataMatch) {
dataMatch = script.match(/data\s*:\s*({[\s\S]*?})[,\n]/i);
}
if (dataMatch && dataMatch[1]) {
// 处理单引号和双引号的问题
let dataStr = dataMatch[1];
// 处理特殊情况:将单引号替换为双引号,但要避免替换已经转义的单引号
dataStr = dataStr.replace(/(?<!\\)'/g, '"');
// 处理数组中的逗号后面的空格
dataStr = dataStr.replace(/,\s*([\]}])/g, ',$1');
if (cb) {
dataStr = cb(dataStr);
}
try {
// 尝试解析data对象
dataObj = new Function(`return ${dataStr}`)();
} catch (e) {
console.error('解析data对象失败:', e);
console.error('尝试解析的数据字符串:', dataStr);
}
}
} catch (e) {
console.error('处理脚本失败:', e);
}
}
// 处理模板内容
if (template) {
// 使用 @vue/compiler-dom 编译模板为渲染函数
const { code } = compile(template, { mode: 'function', filename: 'user-template.vue' });
const renderFn = new Function('Vue', `${code}; return render`)(Vue);
const app = Vue.createApp({
data() {
return dataObj;
},
render: renderFn
});
const container = document.createElement('div');
app.mount(container);
previewContent += container.innerHTML;
}
// 设置预览内容
return previewContent;
}
下面是测试这个方法
const str = "<template>
<div class="financial-form">
<h2 class="financial-title">海通证券财富管理中⼼固定收益证券现券交易申请表</h2>
<table class="form-table">
<!-- 申请信息 -->
<tr>
<td class="label form-item">申请人</td>
<td class="data form-item">{{ requestUser }}</td>
<td class="label form-item">联系电话</td>
<td class="data form-item" colspan="3">--</td>
<td class="label form-item">申请⽇期</td>
<td class="data form-item">{{ dataList[0].requestDate }}</td>
</tr>
<tr>
<td class="label form-item" colspan="2">产品名称</td>
<td class="data form-item" colspan="6">{{ dataList[0].agencyAccountName }}</td>
</tr>
<!-- 交易内容 -->
<tr>
<td class="label">序号</td>
<td class="label">代码</td>
<td class="label">简称</td>
<td class="label">交易方向</td>
<td class="label">交易量(万)</td>
<td class="label">交易净价/收益率</td>
<td class="label">清算速度</td>
<td class="label">对手方</td>
</tr>
<tr v-for="(item, index) in dataList" :key="item.bizInstNo">
<td class="data">{{index + 1}}</td>
<td class="data">{{ item.underlyingAssetDetails[0].firstAssetKey }}</td>
<td class="data">{{ item.underlyingAssetDetails[0].firstAssetName }}</td>
<td class="data">{{ item.underlyingAssetDetails[0].dealSide === '1' ? "买入" : "卖出" }}</td>
<td class="data">{{ item.underlyingAssetDetails[0].firstTradeAmt }}</td>
<td class="data">
<div>{{
item.underlyingAssetDetails?.[0].calMode === 0
? "净价"
: item.underlyingAssetDetails[0].calMode === 1
? "到期收益率"
: "行权收益率"
}}</div>
<div>{{
item.underlyingAssetDetails[0].calMode === 0
? item.underlyingAssetDetails[0].firstCleanPrice
: item.underlyingAssetDetails[0].calMode === 1
? item.underlyingAssetDetails[0].firstMatYield
: item.underlyingAssetDetails[0].firstOptionYield
}}</div>
</td>
<td class="data">{{
item.underlyingAssetDetails[0].settleType === 1 ? "T+0"
: item.underlyingAssetDetails[0].settleType === 2 ? "T+1"
: item.underlyingAssetDetails[0].settleType === 3 ? "T+2"
: item.underlyingAssetDetails[0].settleType === 4 ? "T+3"
: item.underlyingAssetDetails[0].settleType === 5? "T+4"
: "T+5"
}}</td>
<td class="data">{{ item.counterPartyName }}</td>
</tr>
<!-- 审核区域 -->
<tr v-for="(approve, index) in approvedList" :key="index">
<td colspan="2">{{approve.nodeName}}</td>
<td colspan="6">{{approve.userNames}}</td>
</tr>
</table>
</div>
</template>
<script>
export default {
data() {
return {
"requestUser": "Admin",
"approvedList": [
{
"nodeName": "审批节点1",
"userNames": "王朔铖",
"approveTime": "2025-05-19 14:04:47",
"nodeId": null
},
{
"nodeName": "审批节点2",
"userNames": "王朔铖",
"approveTime": "2025-05-19 14:04:56",
"nodeId": null
},
{
"nodeName": "审批节点2",
"userNames": "王朔铖",
"approveTime": "2025-05-19 14:04:56",
"nodeId": null
}
],
"dataList": [
{
"marginSecurityDetails": [
],
"settleState": null,
"bizInstDesc": null,
"pledgeMortgageArrangement": null,
"cashAssignDetails": null,
"bizInstState": 12,
"bondRepoFlag": false,
"expectTermDay": null,
"realCounterPartyName": "",
"outRepoType": 1,
"bizInstSummary": "买入,21西藏债专项(八期)IB(Z21120852.IB),净价:100.0000元,交易面额:1,000.00万元(固收自营组合[银行间交易性]/1,000.00万元),到期收益率:0.0000%,T+0,12年第6期私银混合精选,结算金额:10,000,000.00元",
"counterTraderName": "",
"marketPriceFlag": false,
"requestUserId": "Admin",
"agencyAccountNo": "Account-20250403-00002",
"executeDate": null,
"dataVal2": null,
"requestDate": "2025-05-19",
"selfTraderName": "",
"dataVal1": null,
"dataVal0": null,
"originalExecId": null,
"deliveryDate": "2025-05-19",
"dataVal4": null,
"dataVal3": null,
"numVal4": null,
"numVal3": null,
"tradeRateType": null,
"numVal6": null,
"exposureAtDefault": null,
"numVal5": null,
"numVal0": null,
"securityCode": null,
"numVal2": null,
"counterPartyName": "12年第6期私银混合精选",
"numVal1": 1,
"instFeeDetails": [
{
"channelType": "null",
"channelCode": null,
"feeType": "",
"feeRate": 0,
"feeAmt": 0,
"dataVal0": null,
"numVal0": null,
"numVal1": null,
"strVal0": null,
"strVal1": null
}
],
"sedSettleAmt": null,
"sedDeliveryType": null,
"settleType": 1,
"operateNo": null,
"marginAmt": null,
"bizInstNo": "BIZ-20250519-00201",
"agencyAccountName": null,
"disputeSolution": null,
"dealSide": 1,
"channelType": null,
"fieldValueMap": null,
"realCounterPartyCode": "",
"sedDealType": null,
"relatedExecId": null,
"sedDeliveryDate": null,
"tradePlatform": null,
"interestAmt": 0,
"openCloseFlag": null,
"operateType": null,
"apiContactDetail": {
"quoteMode": "0",
"quoteNo": "",
"quoteValidTime": null,
"initiator": "",
"sendParty": "1",
"investNo": "",
"saleChannel": "",
"tailDiffFlag": null,
"redeemMode": "",
"allRedeemFlag": null,
"largeRedeemMode": "",
"settledFlag": null,
"swapType": null,
"counterPartyType": "",
"counterAccountNo": "",
"counterSeatNo": "",
"counterCompanyCode": "",
"counterMemberCode": "",
"dealerCode": "",
"tradeCompanyCode": "",
"promiseNo": "",
"bonsMode": "",
"dataVal0": null,
"numVal0": null,
"numVal1": null,
"strVal0": null,
"strVal1": null,
"strVal2": null,
"strVal3": null
},
"dealType": 223,
"cashGap": null,
"timeVal2": null,
"timeVal0": null,
"timeVal1": null,
"tradeRate": null,
"endTradeDate": "2025-05-19",
"fstSettleAmt": 10000000,
"performReward": null,
"cashAccountNo": "900610000012001",
"securityName": null,
"penaltyInterest": null,
"strVal5": null,
"strVal4": null,
"requestUserName": null,
"settleDate": "2025-05-19",
"strVal6": null,
"relatedTradeIndexes": null,
"strVal1": null,
"counterPartyCode": "402401",
"strVal0": null,
"strVal3": null,
"strVal2": null,
"counterTraderCode": "",
"profitPercent": null,
"marketCode": "IB",
"benchMark": null,
"groupNo": null,
"categorize": 0,
"channelCode": null,
"underlyingAssetDetails": [
{
"uniqueNo": "BIZ-20250519-00201",
"firstAssignAmt": 10000000,
"firstAssetKey": "Z21120852.IB",
"firstOuterKey": null,
"firstAssetName": "21西藏债专项(八期)IB",
"firstAssetType": "BOND",
"firstCleanPrice": 100,
"firstAccruedInterest": 0,
"firstDirtyPrice": 100,
"firstMatYield": 0.00000000000007289964042575258,
"firstOptionYield": 0,
"firstTradeAmt": 10000000,
"firstInterestAmt": 0,
"firstSettleAmt": 10000000,
"firstIssueSize": null,
"calMode": 0,
"changeField": null,
"firstCustodyAgency": null,
"firstCustAccNo": "665544",
"firstAssignDetails": [
{
"assignAmt": 10000000,
"occupyAssignAmt": null,
"portfolioNo": "P-20250308-00001",
"operateType": null,
"portfolioName": "固收自营组合[银行间交易性]",
"assetKey": "Z21120852.IB",
"outerKey": null,
"assetName": "21西藏债专项(八期)IB",
"assetType": "BOND",
"custodyAccountNo": "665544",
"useType": 1,
"convertRate": 1,
"principalAmt": null,
"interestAmt": null,
"feeAmt": null,
"dataVal0": null,
"numVal0": null,
"numVal1": null,
"strVal0": null,
"strVal1": null,
"underlyingSecurity": true,
"marginSecurity": false,
"remainAssignAmt": 10000000
}
],
"secondAssignAmt": null,
"secondAssetKey": null,
"secondOuterKey": null,
"secondAssetName": null,
"secondAssetType": null,
"secondCleanPrice": null,
"secondAccruedInterest": null,
"secondDirtyPrice": null,
"secondMatYield": null,
"secondOptionYield": null,
"secondTradeAmt": null,
"secondInterestAmt": null,
"secondSettleAmt": null,
"secondCustAccNo": null,
"secondAssignDetails": null,
"innerCreditRate": null,
"convertRate": 1,
"rateInvalidDate": null,
"currencyType": "CNY",
"currencyRate": null,
"bidRate": null,
"dataVal1": null,
"dataVal2": null,
"dataVal3": null,
"numVal1": null,
"numVal2": null,
"numVal3": null,
"strVal1": null,
"strVal2": null,
"strVal3": null,
"strVal4": null,
"firstOccupyAssignAmt": 0,
"secondOccupyAssignAmt": 0,
"remainFirstAssignAmt": 10000000,
"remainSecondAssignAmt": 0
}
],
"requestType": 223,
"sedSettleDate": null,
"deliveryType": 0,
"occupyTrustFlag": null,
"thirdPartyRenewalFlag": null,
"tradeDate": "2025-05-19",
"requestNo": "REQ-20250519-00301",
"needCalFstSettleAmt": null,
"selfTraderCode": "",
"rateSpread": null,
"actualTermDay": null,
"clearingMode": 13,
"totalAssignAmt": 10000000,
"requestState": 2,
"warehouseState": null,
"firstOccupySettleAmt": 0,
"class": "com.bocloud.ficc.pms.provider.api.core.dto.BizInstUsualIndexDTO",
"remainFirstSettleAmt": 0,
"transactionState": 0
}
]
};
}
};
</script>
<style scoped>
.financial-form {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 20px auto;
}
.financial-title {
font-size: 20px;
font-weight: bold;
text-align: center;
margin-bottom: 6px;
}
.form-table {
width: 100%;
border-collapse: collapse;
border: 2px solid #e1e1e1;
}
td {
padding: 12px;
border: 1px solid #e1e1e1;
}
td.form-item {
padding: 6px 12px;
}
.label {
width: 12.5%;
text-align: center;
}
.data {
width: 12.5%;
text-align: center;
}
.section-header {
font-size: 18px;
font-weight: bold;
text-align: center;
padding: 15px;
}
.approval-section {
min-height: 60px;
padding: 10px;
}
</style>
"
const html = handleVueTemplate(str)
console.log(html)
生成的html代码
<style>
.financial-form {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 20px auto;
}
.financial-title {
font-size: 20px;
font-weight: bold;
text-align: center;
margin-bottom: 6px;
}
.form-table {
width: 100%;
border-collapse: collapse;
border: 2px solid #e1e1e1;
}
td {
padding: 12px;
border: 1px solid #e1e1e1;
}
td.form-item {
padding: 6px 12px;
}
.label {
width: 12.5%;
text-align: center;
}
.data {
width: 12.5%;
text-align: center;
}
.section-header {
font-size: 18px;
font-weight: bold;
text-align: center;
padding: 15px;
}
.approval-section {
min-height: 60px;
padding: 10px;
}
</style><div class="financial-form"><h2 class="financial-title">海通证券财富管理中⼼固定收益证券现券交易申请表</h2><table class="form-table"><!-- 申请信息 --><tr><td class="label form-item">申请人</td><td class="data form-item">Admin</td><td class="label form-item">联系电话</td><td class="data form-item" colspan="3">--</td><td class="label form-item">申请⽇期</td><td class="data form-item">2025-05-19</td></tr><tr><td class="label form-item" colspan="2">产品名称</td><td class="data form-item" colspan="6"></td></tr><!-- 交易内容 --><tr><td class="label">序号</td><td class="label">代码</td><td class="label">简称</td><td class="label">交易方向</td><td class="label">交易量(万)</td><td class="label">交易净价/收益率</td><td class="label">清算速度</td><td class="label">对手方</td></tr><tr><td class="data">1</td><td class="data">Z21120852.IB</td><td class="data">21西藏债专项(八期)IB</td><td class="data">卖出</td><td class="data">10000000</td><td class="data"><div>净价</div><div>100</div></td><td class="data">T+5</td><td class="data">12年第6期私银混合精选</td></tr><!-- 审核区域 --><tr><td colspan="2">审批节点1</td><td colspan="6">王朔铖</td></tr><tr><td colspan="2">审批节点2</td><td colspan="6">王朔铖</td></tr><tr><td colspan="2">审批节点2</td><td colspan="6">王朔铖</td></tr></table></div>
放到html文件运行没问题, 证明方法没问题
这里说明下: 产品有个需求是用户要在页面添加一个编辑器功能, 自己手写vue代码, 然后生成新的页面的需求