vue3-sfc-loader 加载远程.vue文件(sfc)案例
注意事项
- style标签如果增加了lang比如:lang=“scss”,需要提供scss-loader的处理器,这个暂时没研究,我的处理方式是将动态模版的css放在了全局
- 打包暂时还没有测试,后面测试了会同步更新
安装vue3-sfc-loader
npm install vue3-sfc-loader
后端vue模版 (sfc案例)
后端我用的是java加Velocity模版引擎(.vm模版)
vm模版引擎位置:
vm模版
<template>
<view class="cus-tab theme theme-page">
<uniForms ref="formRef" :modelValue="formData" validate-trigger="bind">
<table class="tableCla">
<colgroup>
<col style="width: 20px;">
<col style="width: 70px;">
<col style="width: 130px;">
<!-- <col style="width: 70px;"> -->
</colgroup>
<thead>
<tr>
<th class="slash-wrap" colspan="2">
<div class="oline">
<span class="span1">年级</span>
<span class="span2">姓名</span>
</div>
</th>
<th>高三二班</th>
<th>异议数据</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2">身高</td>
<td>身高</td>
<td>
<uniFormsItem name="sga">
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sga"
placeholder=""></uniEasyinput>
<text>cm</text>
</view>
</uniFormsItem>
</td>
</tr>
<tr>
<td colspan="2">体重</td>
<td>88888888</td>
<td>
<uniFormsItem name="tz">
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.tz"
placeholder=""></uniEasyinput>
<text>kg</text>
</view>
</uniFormsItem>
</td>
</tr>
<tr>
<td colspan="1" rowspan="3">眼科检查</td>
<td>裸眼视力</td>
<td>das</td>
<td>
<view class="theme-flex">
<view class="input-cla theme-flex">
<uniFormsItem name="lyz">
<view class="input-cla theme-flex">
<text>左</text>
<uniEasyinput :clearable="false" v-model="formData.lyz"
placeholder=""></uniEasyinput>
</view>
</uniFormsItem>
<text>/</text>
<uniFormsItem name="lyy">
<view class="input-cla theme-flex">
<text>右</text>
<uniEasyinput :clearable="false" v-model="formData.lyy"
placeholder=""></uniEasyinput>
</view>
</uniFormsItem>
</view>
</view>
</td>
</tr>
<tr>
<td>矫正视力</td>
<td>John</td>
<td>
<view class="theme-flex">
<view class="input-cla theme-flex">
<uniFormsItem name="jzz">
<view class="input-cla theme-flex">
<text>左</text>
<uniEasyinput :clearable="false" v-model="formData.jzz"
placeholder=""></uniEasyinput>
</view>
</uniFormsItem>
<text>/</text>
<uniFormsItem name="jzy">
<view class="input-cla theme-flex">
<text>右</text>
<uniEasyinput :clearable="false" v-model="formData.jzy"
placeholder=""></uniEasyinput>
</view>
</uniFormsItem>
</view>
</view>
</td>
</tr>
<tr>
<td>角膜炎</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<wdCheckbox v-model="formData.jmy" shape="square">有异议</wdCheckbox>
</view>
</td>
</tr>
<tr>
<td colspan="1" rowspan="4">内科检查</td>
<td>心脏</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<wdCheckbox v-model="formData.xz" shape="square">有异议</wdCheckbox>
</view>
</td>
</tr>
<tr>
<td>肺</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<wdCheckbox v-model="formData.fei" shape="square">有异议</wdCheckbox>
</view>
</td>
</tr>
<tr>
<td>肝</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<wdCheckbox v-model="formData.gan" shape="square">有异议</wdCheckbox>
</view>
</td>
</tr>
<tr>
<td>脾</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<wdCheckbox v-model="formData.pi" shape="square">有异议</wdCheckbox>
</view>
</td>
</tr>
<tr>
<td colspan="1" rowspan="8">外科检查</td>
<td>头部</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<wdCheckbox v-model="formData.tb" shape="square">有异议</wdCheckbox>
</view>
</td>
</tr>
<tr>
<td>颈部</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td>甲状腺</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td>胸部</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td>脊椎</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td>四肢</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td>皮肤</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td>淋巴结</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td colspan="1" rowspan="3">耳鼻喉科</td>
<td>耳</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td>鼻</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td>扁桃体</td>
<td>John</td>
<td>
<view class="input-cla theme-flex">
<uniEasyinput :clearable="false" v-model="formData.sg" placeholder=""></uniEasyinput>
</view>
</td>
</tr>
<tr>
<td colspan="4">
<view class="input-text-cla">
<uniFormsItem required name="fkyj" :rules="[{required: true,errorMessage: '请填写反馈意见',}]">
<view class="theme-flex">
<uniEasyinput type="textarea" autoHeight :clearable="false" v-model="formData.fkyj"
placeholder="请输入反馈意见"></uniEasyinput>
</view>
</uniFormsItem>
</view>
</td>
</tr>
</tbody>
</table>
<view>
<view class="tips-clas theme-flex">
<text>1. 请如实填写,填写完成后保存</text>
<text>2. 异议数据需机构审核,请耐心等待</text>
</view>
<view class="bot-box">
<wdButton @click="submit">提交审核</wdButton>
</view>
</view>
</uniForms>
</view>
</template>
<script setup name="Abnormal">
import { ref, reactive } from 'vue'
import uniForms from 'uniForms'
import uniFormsItem from 'uniFormsItem'
import uniEasyinput from 'uniEasyinput'
import wdCheckbox from 'wdCheckbox'
import wdButton from 'wdButton'
const formRef = ref();
const formData = reactive({
sga: '',
sg: '',
tz: '',
lyz: '',
lyy: '',
jzz: '',
jzy: '',
fkyj: '',
jmy: false,
xz: false,
fei: false,
gan: false,
pi: false,
tb: false,
});
const submit = () => {
formRef.value.validate().then((res) => {
console.log('表单数据信息:', res);
}).catch((err) => {
console.log('表单错误信息:', err);
})
};
</script>
<style lang="scss">
.oline {
border-top: 50px #c5c5c5 solid;
width: 0px;
height: 0px;
border-left: 90px #ffffff solid;
position: relative;
}
.oline::before {
position: absolute;
top: -40px;
left: -80px;
width: 80px;
height: 40px;
/* background: #fff url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPjxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjEwMCUiIHkyPSIxMDAlIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjEiLz48L3N2Zz4=) no-repeat 100% center; */
content: '';
}
.span1 {
font-style: normal;
display: block;
position: absolute;
top: -40px;
left: -40px;
width: 35px;
}
.span2 {
font-style: normal;
display: block;
position: absolute;
top: -25px;
left: -70px;
width: 55px;
}
.top {
padding: 10px;
border-bottom: 1px solid #e8e8e8;
}
.aColor>a {
color: rgba(0, 0, 0, 0.65);
}
.tableCla {
width: 100%;
border: 1px solid #e8e8e8;
border-collapse: collapse;
}
.theadCla>tr>th {
padding: 10px;
color: rgba(0, 0, 0, 0.85);
border: 1px solid #e8e8e8;
background: #fafafa;
font-weight: normal;
}
tbody>tr>td,
th {
text-align: center;
font-weight: normal;
color: rgba(0, 0, 0, 0.65);
/* padding: 15px 10px; */
border: 1px solid #e8e8e8;
font-size: 12px;
}
tr:nth-child(even) {
/* background: #effefd; */
}
td {
font-size: 15px;
}
.input-cla {
width: 100%;
// border: 1px solid #888888;
height: 30px;
padding: 10px;
box-sizing: border-box;
align-items: center;
text-align: center;
justify-content: center;
:deep(.uni-easyinput__content-input) {
height: 20px;
font-size: 14px;
width: 40px;
}
:deep(.uni-easyinput__content) {
height: 20px;
font-size: 14px;
width: 100%;
}
:deep(.uni-easyinput) {
height: 20px;
font-size: 14px;
flex: 0;
width: 40px !important;
}
:deep(.uni-input-input) {
font-size: 11px !important;
}
:deep(.uni-easyinput__content-input) {
height: 20px !important;
}
}
.cus-tab {
:deep(.uni-forms-item) {
margin-bottom: 0px !important;
}
}
.input-text-cla {
padding: 10px 5px;
padding-bottom: 20px;
box-sizing: border-box;
}
.tips-clas {
flex-direction: column;
color: red;
align-items: flex-start;
gap: 5px;
font-size: 14px;
padding: 10px;
box-sizing: border-box;
}
.bot-box {
width: 100%;
padding: 10px;
padding-top: 5px;
box-sizing: border-box;
:deep(.wd-button) {
width: 90%;
display: block;
margin: 0 auto;
}
}
</style>
后端处理模版数据
之前写过类似的,就直接拿来用了,传送门
前端案例
前端使用的uniapp+vue3+ts+vite
moduleCache中需要存放你的vm模版中的组件及api差不类似运行时的东西,不然标签是不会被解析的,类似下面中的uni-ui的组件,如果不引入就不会被解析,注意:引入的模版key要与vm模板中的名称一致
<template>
<!-- <div style=" margin: 0; padding: 0;overflow-y: scroll;"> -->
<component :is="dynamicComponent" v-if="templateStr"></component>
<wd-status-tip image="comment" tip="暂无报告数据~" v-else/>
<Fab ref="fabRef" @selectAr="selectAr" />
<!-- </div> -->
</template>
<script setup lang="ts" name="CalendarYear">
import { onLoad } from '@dcloudio/uni-app'
import uniForms from '@dcloudio/uni-ui/lib/uni-forms/uni-forms.vue'
import uniFormsItem from '@dcloudio/uni-ui/lib/uni-forms-item/uni-forms-item.vue'
import uniDataCheckbox from '@dcloudio/uni-ui/lib/uni-data-checkbox/uni-data-checkbox.vue'
import uniEasyinput from '@dcloudio/uni-ui/lib/uni-easyinput/uni-easyinput.vue'
import { abnormalReport } from '@/api/speExamineResService'
import { loadModule } from "vue3-sfc-loader/dist/vue3-sfc-loader"
import wdCheckbox from 'wot-design-uni/components/wd-checkbox/wd-checkbox.vue'
import wdButton from 'wot-design-uni/components/wd-button/wd-button.vue'
import * as Vue from 'vue'
import Fab from '@/components/Fab/index.vue'
import { compile } from 'sass';
import useUserStore from "@/store/user";
const fabRef = ref()
const proxy = getCurrentInstance()?.proxy
const dynamicComponent = shallowRef();
const templateStr = ref('')
const archiveInfo = computed(() => {
return useUserStore().getSelectArchive()
})
const reportFun = (val : any) => {
if (!val.archivesId) {
proxy.$pop.toast('请选择学生')
nextTick(() => {
fabRef.value.openAr()
})
return;
}
console.log('uniForms', uniForms)
abnormalReport(val.archivesId).then(async (res : any) => {
console.log(res)
templateStr.value = res.data
const options = {
moduleCache: {
vue: Vue,
'uniForms': uniForms,
'uniFormsItem': uniFormsItem,
'uniEasyinput': uniEasyinput,
'wdCheckbox': wdCheckbox,
'wdButton': wdButton,
'scss': (source) => {
console.log('======>source',source)
return Object.assign(compile(source), { deps: () => [] })
}
},
async getFile() {
return res.data
},
addStyle(textContent) {
console.log('textContent,', textContent)
const style = Object.assign(document.createElement('style'), { textContent })
console.log('style,', style)
const ref = document.head.getElementsByTagName('style')[0] || null
document.head.insertBefore(style, ref)
},
loader: {
}
};
dynamicComponent.value = defineAsyncComponent(() => loadModule(res.fileName || "loader.vue", options))
})
}
const selectAr = async (val : any) => {
reportFun(val)
}
onLoad(() => {
reportFun(archiveInfo.value)
})
</script>
<style lang="scss">
</style>