el-upload limit=1时 无法上传多次
<el-upload :limit="1" :action="importUrl" :before-upload="beforeUpload" :on-success="handleSuccess" :on-error="handleError" :show-file-list="false" accept=".xlsx" ref="uploadRef">
<el-button type="primary" icon="Upload" style="margin-left: 10px" :loading="uploadLoading">导入算法</el-button>
</el-upload>
在成功的钩子函数中使用clearFiles(),清空已上传文件
handleSuccess(response) {
if (response.code === 0) {
ElMessage.success("导入成功");
this.$refs.uploadRef.clearFiles();
this.getDataList();
} else {
this.handleError(response);
}
this.uploadLoading = false;
},
nginx部署时,有两个前端时的打包配置
非主前端的项目,打包需要加上nginx的路径,例如/point
vite.config.js
export default defineConfig({
base: "/point",
build: {
outDir: 'point' //输出目录
},
plugins: [
vue(),
// commonjs()
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
打包后的静态文件
方案二:在nginx配置文件中配置两个server
server {
listen 8080; # 第一个端口
server_name localhost;
location / {
root html/dist;
try_files $uri $uri/ /index.html;
}
}
server {
listen 8081; # 第二个端口
server_name localhost;
location / {
root html/3dview;
try_files $uri $uri/ /index.html;
}
}
打开弹窗组件的处理思路 拒绝用emit
弹窗组件init方法:
先调用表单的resetFields方法(去除校验,恢复默认值)
再清空dataForm
如果是编辑再给dataForm赋值
弹窗常用属性:close-on-click-modal=“false” 禁止点击空白关闭
const init = (row: IObject) => {
visible.value = true;
// 重置表单数据
if (dataFormRef.value) {
dataFormRef.value.resetFields();
}
for (const key in dataForm) {
dataForm[key] = "";
}
if (row) {
Object.assign(dataForm, row);
}
};
根据坐标点,转换为图像
<!--
CAD解析结果预览图片
* @Description
* @ReservedFields
-->
<template>
<el-dialog :title="title" v-model="open" width="40%" append-to-body @close="clearSvg">
<div class="svgContainer">
<svg id="mySvg"></svg>
</div>
</el-dialog>
</template>
<script setup>
import * as d3 from 'd3'
import { onMounted, ref, getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
const data = ref([])
const open = ref(false)
const title = ref('')
const width = 480
const offset = 10 // 画布的偏移量
const init = () => {
open.value = true
proxy.$nextTick(() => {
drawSvg()
})
}
const drawSvg = () => {
// 找到x坐标的最小值和最大值
const xMin = d3.min(data.value, (d) => d.x)
const xMax = d3.max(data.value, (d) => d.x)
// 找到y坐标的最小值和最大值
const yMin = d3.min(data.value, (d) => d.y)
const yMax = d3.max(data.value, (d) => d.y)
// 计算x和y方向的缩放比例
const xScale = width / (xMax - xMin)
const yScale = width / (yMax - yMin)
// 对坐标点进行缩放和偏移适配到合适范围内
const scaledData = data.value.map((d) => {
return {
x: (d.x - xMin) * xScale + offset,
y: width - (d.y - yMin) * yScale + offset
}
})
const svg = d3.select('#mySvg')
const polygon = svg
.append('polygon')
.attr('points', function () {
return scaledData.map((d) => `${d.x},${d.y}`).join(' ')
})
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('stroke-width', 3)
}
const clearSvg = () => {
const svg = d3.select('#mySvg')
svg.selectAll('*').remove()
}
defineExpose({
init,
data,
title
})
onMounted(() => {})
</script>
<style lang="scss" scoped>
.svgContainer {
width: 100%;
height: 100%;
text-align: center;
svg {
width: 500px;
height: 500px;
}
}
</style>
沿svg轨迹运动
使用animateMotion定义沿路径的动画
<animateMotion dur="30" repeatCount="1" href="#plane" rotate="auto">
<mpath xlink:href="#myPath" />
</animateMotion>
让物体中心在轨迹上
<g transform=“translate(-长的一半,-宽的一半)”> 物体的path</g>
使用gsap动画库
<script setup>
import { onMounted, ref } from 'vue'
import { defineComponent, reactive, toRefs } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
let tl = gsap.timeline({ paused: true })
const pathLength = ref(143.16) // 轨迹实际总长为143km
const speed = ref(0) // 每秒传入的速度(km/s)
const currentMileage = ref(0) // 当前小车的里程(km)
const fnArrayString = route.query.fns
const fns = fnArrayString?.split(',')
route.query.length && (pathLength.value = route.query.length)
function toggleAnimation() {
if (tl.paused()) {
tl.play()
} else {
tl.pause()
}
}
function updateProgress() {
if (speed.value === 0) {
tl.paused()
console.log('小车停止')
return
}
// 结束动画
if (currentMileage.value > pathLength.value) {
currentMileage.value = pathLength.value
tl.paused()
}
const progress = currentMileage.value / pathLength.value
tl.progress(progress) //更新进度
}
onMounted(() => {
const path = document.getElementById('myPath')
const car = document.getElementById('plane')
const length = path.getTotalLength()
tl.add('start')
tl.to('#plane', {
motionPath: {
path: '#myPath', //
align: '#myPath', // 对齐的参照物
autoRotate: true, //自动旋转角度
alignOrigin: [0.5, 0.5] // 水平和垂直方向都位于元素的中心
},
yoyo: false
})
// 初始进度
tl.progress(0.00001)
window.ue = {
...window.ue,
interface: {
...window.ue.interface
}
}
// 更新速度
window.ue.interface[fns[0]] = (speed) => {
speed.value = speed
}
//更新里程
window.ue.interface[fns[1]] = (mileage) => {
currentMileage.value = mileage
updateProgress()
}
})
</script>
el-input 内容换行
boundData.map((item) => `x:${item.x}, y:${item.y}`).join('\n') // 分隔符用\n
批量处理表单
比如要在表格的每一行对表单进行校验
<el-form
:model="row"
label-width="90px"
label-position="left"
:rules="rules"
:ref="
(el) => {
formRefs[row.id] = el
}
"
>
// js
const formRefs = reactive({})
const formRef = formRefs[row.id]
// 分别对每个表单进行表单校验...
iframe通信总结
<iframe :src="src" frameborder="0" width="100%" height="100%" ref="iframeRef" @load="iframeLoad" @error="handleIframeError"></iframe>
父窗口向嵌入发送信息时,务必等待iframe加载完毕
父传子窗口:dom.contentWindow.postMessage()
let loadFinish = false
const loading = ref(true)
const iframeLoad = () => {
loadFinish = true
const iframe = iframeRef.value
if (iframe && loadFinish) {
const dataToSend = {
id: props.task.id,
trainType: props.task.trainType,
train: props.task.train,
url: props.url
}
// 此处延迟发送,经测试立马发送子窗口可能收不到
setTimeout(() => {
iframe.contentWindow.postMessage(dataToSend, '*')
loading.value = false
}, 500)
}
}
子窗口:window.addEventListener(‘message’,function(e){})
onMounted(() => {
// 挂载后初始化视图
initViewer()
window.addEventListener('message', function (event) {
const receivedData = event.data
if (receivedData.id) {
id.value = receivedData.id
trainType.value = receivedData.trainType
train.value = receivedData.train
url.value = receivedData.url
queryAutoConvert()
}
})
})
相邻兄弟选择器(+)的使用场景
例如 一个列表,除最后一个元素以外,每个元素距离底部20px 作用的是后面一个child
.child + .child {
margin-top:20px;
}