注意事项
- 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()?.proxyconst 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.dataconst 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] || nulldocument.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>