需求解析功能进一步完善

This commit is contained in:
2025-04-30 17:44:22 +08:00
parent 7fe6ecf765
commit 0a0beb3e91
32 changed files with 1073 additions and 618 deletions

View File

@@ -0,0 +1,42 @@
<template>
<div class="upload-input-container">
<a-upload
accept=".zip"
tip="只支持C/C++不支持FPGA代码上传后会自行录入圈复杂度等信息"
:limit="1"
:action="`/api/dut_upload/upload_file?dut_id=${form.id}`"
@success="uploadSuccessHandle"
@error="handleError"
></a-upload>
</div>
</template>
<script setup lang="ts">
import { Message } from "@arco-design/web-vue"
import { inject } from "vue"
// 表单数据
const form: any = inject("formModel")
// 组件props
const props = defineProps({
component: Object, // 组件配置信息
customField: { type: String, default: undefined } // 自定义字段名称,用于子表单
})
// 上传成功后回调-即时给前端反馈
const uploadSuccessHandle = (FileItem: any) => {
form.value.comment_lines = FileItem.response.data.comment_lines
form.value.effective_lines = FileItem.response.data.effective_lines
form.value.total_lines = FileItem.response.data.total_lines
Message.success("解析代码成功,圈复杂度、模块大小等均已录入")
}
// 上传失败
const handleError = () => {
Message.error("处理失败请检查是否是C/C++代码且zip文件")
}
defineOptions({
name: "UploadInput"
})
</script>
<style lang="less" scoped></style>

View File

@@ -57,7 +57,7 @@ const props = defineProps({
type: [String, Array],
// 如果要取消粘贴只粘贴文本需要用户加格式请加上pastetext
default:
"undo redo aligncenter alignleft indent styleselect formatselect fontselect fontsizeselect removeformat"
"code undo redo aligncenter alignleft indent styleselect formatselect fontselect fontsizeselect removeformat"
// 下面是备份配置:
// default:"code undo redo restoredraft | paste | bold | aligncenter alignleft alignjustify indent | \

View File

@@ -3,60 +3,65 @@
- @Link XXX
-->
<template>
<div>
<ma-image-upload v-if="props.type === 'image'" v-model="file" />
<ma-file-upload v-if="props.type === 'file'" v-model="file" />
<ma-chunk-upload v-if="props.type === 'chunk'" v-model="file" />
</div>
<div>
<ma-image-upload v-if="props.type === 'image'" v-model="file" />
<ma-file-upload v-if="props.type === 'file'" v-model="file" />
<ma-chunk-upload v-if="props.type === 'chunk'" v-model="file" />
</div>
</template>
<script setup>
import { ref, watch, provide } from 'vue'
import { Message } from '@arco-design/web-vue'
import { ref, watch, provide } from "vue"
import { Message } from "@arco-design/web-vue"
import uploadConfig from '@/config/upload'
import MaImageUpload from './components/image-upload.vue'
import MaFileUpload from './components/file-upload.vue'
import MaChunkUpload from './components/chunk-upload.vue'
import uploadConfig from "@/config/upload"
import MaImageUpload from "./components/image-upload.vue"
import MaFileUpload from "./components/file-upload.vue"
import MaChunkUpload from "./components/chunk-upload.vue"
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(["update:modelValue"])
const file = ref()
const props = defineProps({
modelValue: { type: [ String, Number, Array ], default: () => {} },
title: { type: String, default: 'buttonText', },
icon: { type: String, default: 'icon-plus'},
rounded: { type: Boolean, default: false },
multiple: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
draggable: { type: Boolean, default: false },
size: { type: Number, default: 4 * 1024 * 1024 },
chunk: { type: Boolean, default: false },
chunkSize: { type: Number, default: 1 * 1024 * 1024 },
limit: { type: Number, default: 0 },
tip: { type: String, default: undefined },
type: { type: String, default: 'image' },
accept: { type: String, default: '*' },
returnType: { type: String, default: 'hash' },
fileType: { type: String, default: 'button' },
showList: { type: Boolean, default: true },
requestData: { type: Object, default: {} },
modelValue: { type: [String, Number, Array], default: () => {} },
title: { type: String, default: "buttonText" },
icon: { type: String, default: "icon-plus" },
rounded: { type: Boolean, default: false },
multiple: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
draggable: { type: Boolean, default: false },
size: { type: Number, default: 4 * 1024 * 1024 },
chunk: { type: Boolean, default: false },
chunkSize: { type: Number, default: 1 * 1024 * 1024 },
limit: { type: Number, default: 0 },
tip: { type: String, default: undefined },
type: { type: String, default: "image" },
accept: { type: String, default: "*" },
returnType: { type: String, default: "hash" },
fileType: { type: String, default: "button" },
showList: { type: Boolean, default: true },
requestData: { type: Object, default: {} }
})
if (! ['id', 'url', 'hash'].includes(props.returnType)) {
Message.error('MaUpload组件props的returnType只能为id, url, hash 其中一个')
console.error('MaUpload组件props的returnType只能为id, url, hash 其中一个')
if (!["id", "url", "hash"].includes(props.returnType)) {
Message.error("MaUpload组件props的returnType只能为id, url, hash 其中一个")
console.error("MaUpload组件props的returnType只能为id, url, hash 其中一个")
}
watch(() => props.modelValue, (val) => {
file.value = val
}, {
deep: true, immediate: true
})
watch(
() => props.modelValue,
(val) => {
file.value = val
},
{
deep: true,
immediate: true
}
)
provide('storageMode', uploadConfig.storageMode)
provide('config', props)
provide("storageMode", uploadConfig.storageMode)
provide("config", props)
watch(
() => file.value,
vl => emit('update:modelValue', vl)
() => file.value,
(vl) => emit("update:modelValue", vl)
)
</script>

View File

@@ -0,0 +1,250 @@
<template>
<div
ref="canvasContainerRef"
:class="$props.class"
aria-hidden="true"
>
<canvas ref="canvasRef"></canvas>
</div>
</template>
<script setup lang="ts">
import { useMouse, useDevicePixelRatio } from '@vueuse/core';
import { ref, onMounted, onBeforeUnmount, watch, computed, reactive } from 'vue';
type Circle = {
x: number;
y: number;
translateX: number;
translateY: number;
size: number;
alpha: number;
targetAlpha: number;
dx: number;
dy: number;
magnetism: number;
};
type Props = {
color?: string;
quantity?: number;
staticity?: number;
ease?: number;
class?: string;
};
const props = withDefaults(defineProps<Props>(), {
color: '#FFF',
quantity: 100,
staticity: 50,
ease: 50,
class: '',
});
const canvasRef = ref<HTMLCanvasElement | null>(null);
const canvasContainerRef = ref<HTMLDivElement | null>(null);
const context = ref<CanvasRenderingContext2D | null>(null);
const circles = ref<Circle[]>([]);
const mouse = reactive<{ x: number; y: number }>({ x: 0, y: 0 });
const canvasSize = reactive<{ w: number; h: number }>({ w: 0, h: 0 });
const { x: mouseX, y: mouseY } = useMouse();
const { pixelRatio } = useDevicePixelRatio();
const color = computed(() => {
// Remove the leading '#' if it's present
let hex = props.color.replace(/^#/, '');
// If the hex code is 3 characters, expand it to 6 characters
if (hex.length === 3) {
hex = hex
.split('')
.map((char) => char + char)
.join('');
}
// Parse the r, g, b values from the hex string
const bigint = parseInt(hex, 16);
const r = (bigint >> 16) & 255; // Extract the red component
const g = (bigint >> 8) & 255; // Extract the green component
const b = bigint & 255; // Extract the blue component
// Return the RGB values as a string separated by spaces
return `${r} ${g} ${b}`;
});
onMounted(() => {
if (canvasRef.value) {
context.value = canvasRef.value.getContext('2d');
}
initCanvas();
animate();
window.addEventListener('resize', initCanvas);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', initCanvas);
});
watch([mouseX, mouseY], () => {
onMouseMove();
});
function initCanvas() {
resizeCanvas();
drawParticles();
}
function onMouseMove() {
if (canvasRef.value) {
const rect = canvasRef.value.getBoundingClientRect();
const { w, h } = canvasSize;
const x = mouseX.value - rect.left - w / 2;
const y = mouseY.value - rect.top - h / 2;
const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2;
if (inside) {
mouse.x = x;
mouse.y = y;
}
}
}
function resizeCanvas() {
if (canvasContainerRef.value && canvasRef.value && context.value) {
circles.value.length = 0;
canvasSize.w = canvasContainerRef.value.offsetWidth;
canvasSize.h = canvasContainerRef.value.offsetHeight;
canvasRef.value.width = canvasSize.w * pixelRatio.value;
canvasRef.value.height = canvasSize.h * pixelRatio.value;
canvasRef.value.style.width = canvasSize.w + 'px';
canvasRef.value.style.height = canvasSize.h + 'px';
context.value.scale(pixelRatio.value, pixelRatio.value);
}
}
function circleParams(): Circle {
const x = Math.floor(Math.random() * canvasSize.w);
const y = Math.floor(Math.random() * canvasSize.h);
const translateX = 0;
const translateY = 0;
const size = Math.floor(Math.random() * 2) + 1;
const alpha = 0;
const targetAlpha = parseFloat((Math.random() * 0.6 + 0.1).toFixed(1));
const dx = (Math.random() - 0.5) * 0.2;
const dy = (Math.random() - 0.5) * 0.2;
const magnetism = 0.1 + Math.random() * 4;
return {
x,
y,
translateX,
translateY,
size,
alpha,
targetAlpha,
dx,
dy,
magnetism,
};
}
function drawCircle(circle: Circle, update = false) {
if (context.value) {
const { x, y, translateX, translateY, size, alpha } = circle;
context.value.translate(translateX, translateY);
context.value.beginPath();
context.value.arc(x, y, size, 0, 2 * Math.PI);
context.value.fillStyle = `rgba(${color.value.split(' ').join(', ')}, ${alpha})`;
context.value.fill();
context.value.setTransform(pixelRatio.value, 0, 0, pixelRatio.value, 0, 0);
if (!update) {
circles.value.push(circle);
}
}
}
function clearContext() {
if (context.value) {
context.value.clearRect(0, 0, canvasSize.w, canvasSize.h);
}
}
function drawParticles() {
clearContext();
const particleCount = props.quantity;
for (let i = 0; i < particleCount; i++) {
const circle = circleParams();
drawCircle(circle);
}
}
function remapValue(
value: number,
start1: number,
end1: number,
start2: number,
end2: number,
): number {
const remapped = ((value - start1) * (end2 - start2)) / (end1 - start1) + start2;
return remapped > 0 ? remapped : 0;
}
function animate() {
clearContext();
circles.value.forEach((circle, i) => {
// Handle the alpha value
const edge = [
circle.x + circle.translateX - circle.size, // distance from left edge
canvasSize.w - circle.x - circle.translateX - circle.size, // distance from right edge
circle.y + circle.translateY - circle.size, // distance from top edge
canvasSize.h - circle.y - circle.translateY - circle.size, // distance from bottom edge
];
const closestEdge = edge.reduce((a, b) => Math.min(a, b));
const remapClosestEdge = parseFloat(remapValue(closestEdge, 0, 20, 0, 1).toFixed(2));
if (remapClosestEdge > 1) {
circle.alpha += 0.02;
if (circle.alpha > circle.targetAlpha) circle.alpha = circle.targetAlpha;
} else {
circle.alpha = circle.targetAlpha * remapClosestEdge;
}
circle.x += circle.dx;
circle.y += circle.dy;
circle.translateX +=
(mouse.x / (props.staticity / circle.magnetism) - circle.translateX) / props.ease;
circle.translateY +=
(mouse.y / (props.staticity / circle.magnetism) - circle.translateY) / props.ease;
// circle gets out of the canvas
if (
circle.x < -circle.size ||
circle.x > canvasSize.w + circle.size ||
circle.y < -circle.size ||
circle.y > canvasSize.h + circle.size
) {
// remove the circle from the array
circles.value.splice(i, 1);
// create a new circle
const newCircle = circleParams();
drawCircle(newCircle);
// update the circle position
} else {
drawCircle(
{
...circle,
x: circle.x,
y: circle.y,
translateX: circle.translateX,
translateY: circle.translateY,
alpha: circle.alpha,
},
true,
);
}
});
window.requestAnimationFrame(animate);
}
</script>

View File

@@ -0,0 +1 @@
export { default as ParticlesBg } from './ParticlesBg.vue';