Compare commits
2 Commits
089b8e3943
...
d1b5fdcdd3
| Author | SHA1 | Date | |
|---|---|---|---|
| d1b5fdcdd3 | |||
| 96bd1bdf9a |
2264
cdTMP/package-lock.json
generated
2264
cdTMP/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "testplant",
|
"name": "testplant",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.1",
|
"version": "1.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -13,53 +13,53 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@arco-design/color": "^0.4.0",
|
"@arco-design/color": "^0.4.0",
|
||||||
"@arco-design/web-vue": "^2.57.0",
|
"@arco-design/web-vue": "^2.57.0",
|
||||||
"@tanstack/vue-query": "^5.92.9",
|
"@tanstack/vue-query": "^5.99.0",
|
||||||
"@tinymce/tinymce-vue": "^6.3.0",
|
"@tinymce/tinymce-vue": "^6.3.0",
|
||||||
"@vueuse/core": "^14.2.1",
|
"@vueuse/core": "^14.2.1",
|
||||||
"axios": "^1.13.5",
|
"axios": "^1.15.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.20",
|
||||||
"file2md5": "^1.3.0",
|
"file2md5": "^1.3.0",
|
||||||
"lodash-es": "^4.17.23",
|
"lodash-es": "^4.18.1",
|
||||||
"mammoth": "^1.11.0",
|
"mammoth": "^1.12.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
"pinyin-match": "^1.2.10",
|
"pinyin-match": "^1.2.10",
|
||||||
"postcss-import": "^16.1.1",
|
"postcss-import": "^16.1.1",
|
||||||
"qs": "^6.14.2",
|
"qs": "^6.15.1",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tinymce": "^7.9.1",
|
"tinymce": "^7.9.1",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"vue": "^3.5.28",
|
"vue": "^3.5.32",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
"vue-color-kit": "^1.0.6",
|
"vue-color-kit": "^1.0.6",
|
||||||
"vue-data-ui": "^3.14.10",
|
"vue-data-ui": "^3.17.13",
|
||||||
"vue-router": "^5.0.2",
|
"vue-router": "^5.0.4",
|
||||||
"vuedraggable": "^2.24.3"
|
"vuedraggable": "^2.24.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4.1.18",
|
"@tailwindcss/postcss": "^4.2.2",
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
"@tailwindcss/vite": "^4.2.2",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^25.2.3",
|
"@types/node": "^25.6.0",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@types/qs": "^6.14.0",
|
"@types/qs": "^6.15.0",
|
||||||
"@vitejs/plugin-vue": "^6.0.4",
|
"@vitejs/plugin-vue": "^6.0.6",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.1.4",
|
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
||||||
"@vue/babel-plugin-jsx": "^2.0.1",
|
"@vue/babel-plugin-jsx": "^2.0.1",
|
||||||
"browserslist": "^4.28.1",
|
"browserslist": "^4.28.2",
|
||||||
"eslint": "^10.0.0",
|
"eslint": "^10.2.0",
|
||||||
"eslint-plugin-vue": "^10.7.0",
|
"eslint-plugin-vue": "^10.8.0",
|
||||||
"less": "^4.5.1",
|
"less": "^4.6.4",
|
||||||
"less-loader": "^12.3.1",
|
"less-loader": "^12.3.2",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.10",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.3",
|
||||||
"rollup-plugin-visualizer": "^6.0.5",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"tailwindcss": "^4.1.18",
|
"tailwindcss": "^4.2.2",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^6.0.2",
|
||||||
"vite": "^7.3.1",
|
"vite": "^8.0.8",
|
||||||
"vue-eslint-parser": "^10.2.0"
|
"vue-eslint-parser": "^10.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,81 +1,81 @@
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
button {
|
button {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
|
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
border: 1px solid rgb(61, 106, 255);
|
border: 1px solid rgb(61, 106, 255);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: rgb(61, 106, 255);
|
color: rgb(61, 106, 255);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 0 0 0 transparent;
|
box-shadow: 0 0 0 0 transparent;
|
||||||
-webkit-transition: all 0.2s ease-in;
|
-webkit-transition: all 0.2s ease-in;
|
||||||
-moz-transition: all 0.2s ease-in;
|
-moz-transition: all 0.2s ease-in;
|
||||||
transition: all 0.2s ease-in;
|
transition: all 0.2s ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
background: rgb(61, 106, 255);
|
background: rgb(61, 106, 255);
|
||||||
box-shadow: 0 0 30px 5px rgba(0, 142, 236, 0.815);
|
box-shadow: 0 0 30px 5px rgba(0, 142, 236, 0.815);
|
||||||
-webkit-transition: all 0.2s ease-out;
|
-webkit-transition: all 0.2s ease-out;
|
||||||
-moz-transition: all 0.2s ease-out;
|
-moz-transition: all 0.2s ease-out;
|
||||||
transition: all 0.2s ease-out;
|
transition: all 0.2s ease-out;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover::before {
|
button:hover::before {
|
||||||
-webkit-animation: sh02 0.5s 0s linear;
|
-webkit-animation: sh02 0.5s 0s linear;
|
||||||
-moz-animation: sh02 0.5s 0s linear;
|
-moz-animation: sh02 0.5s 0s linear;
|
||||||
animation: sh02 0.5s 0s linear;
|
animation: sh02 0.5s 0s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
button::before {
|
button::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
width: 0px;
|
width: 0px;
|
||||||
height: 86%;
|
height: 86%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 7%;
|
top: 7%;
|
||||||
left: 0%;
|
left: 0%;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 0 50px 30px #fff;
|
box-shadow: 0 0 50px 30px #fff;
|
||||||
-webkit-transform: skewX(-20deg);
|
-webkit-transform: skewX(-20deg);
|
||||||
-moz-transform: skewX(-20deg);
|
-moz-transform: skewX(-20deg);
|
||||||
-ms-transform: skewX(-20deg);
|
-ms-transform: skewX(-20deg);
|
||||||
-o-transform: skewX(-20deg);
|
-o-transform: skewX(-20deg);
|
||||||
transform: skewX(-20deg);
|
transform: skewX(-20deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes sh02 {
|
@keyframes sh02 {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
left: 0%;
|
left: 0%;
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button:active {
|
button:active {
|
||||||
box-shadow: 0 0 0 0 transparent;
|
box-shadow: 0 0 0 0 transparent;
|
||||||
-webkit-transition: box-shadow 0.2s ease-in;
|
-webkit-transition: box-shadow 0.2s ease-in;
|
||||||
-moz-transition: box-shadow 0.2s ease-in;
|
-moz-transition: box-shadow 0.2s ease-in;
|
||||||
transition: box-shadow 0.2s ease-in;
|
transition: box-shadow 0.2s ease-in;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button>AI生成测试项</button>
|
<button>AI生成测试项</button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -8,9 +8,8 @@
|
|||||||
import { reactive, ref, watch, computed } from "vue"
|
import { reactive, ref, watch, computed } from "vue"
|
||||||
import { useAppStore } from "@/store"
|
import { useAppStore } from "@/store"
|
||||||
|
|
||||||
import Editor from "@tinymce/tinymce-vue"
|
|
||||||
import tinymce from "tinymce/tinymce.min.js"
|
import tinymce from "tinymce/tinymce.min.js"
|
||||||
|
import Editor from "@tinymce/tinymce-vue"
|
||||||
import "tinymce/icons/default/icons.min.js"
|
import "tinymce/icons/default/icons.min.js"
|
||||||
import "tinymce/models/dom/model.min.js"
|
import "tinymce/models/dom/model.min.js"
|
||||||
import "tinymce/themes/silver/theme.min.js"
|
import "tinymce/themes/silver/theme.min.js"
|
||||||
|
|||||||
@@ -1,250 +1,233 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div ref="canvasContainerRef" :class="$props.class" aria-hidden="true">
|
||||||
ref="canvasContainerRef"
|
<canvas ref="canvasRef"></canvas>
|
||||||
:class="$props.class"
|
</div>
|
||||||
aria-hidden="true"
|
</template>
|
||||||
>
|
|
||||||
<canvas ref="canvasRef"></canvas>
|
<script setup lang="ts">
|
||||||
</div>
|
import { useMouse, useDevicePixelRatio } from "@vueuse/core"
|
||||||
</template>
|
import { ref, onMounted, onBeforeUnmount, watch, computed, reactive } from "vue"
|
||||||
|
|
||||||
<script setup lang="ts">
|
type Circle = {
|
||||||
import { useMouse, useDevicePixelRatio } from '@vueuse/core';
|
x: number
|
||||||
import { ref, onMounted, onBeforeUnmount, watch, computed, reactive } from 'vue';
|
y: number
|
||||||
|
translateX: number
|
||||||
type Circle = {
|
translateY: number
|
||||||
x: number;
|
size: number
|
||||||
y: number;
|
alpha: number
|
||||||
translateX: number;
|
targetAlpha: number
|
||||||
translateY: number;
|
dx: number
|
||||||
size: number;
|
dy: number
|
||||||
alpha: number;
|
magnetism: number
|
||||||
targetAlpha: number;
|
}
|
||||||
dx: number;
|
|
||||||
dy: number;
|
type Props = {
|
||||||
magnetism: number;
|
color?: string
|
||||||
};
|
quantity?: number
|
||||||
|
staticity?: number
|
||||||
type Props = {
|
ease?: number
|
||||||
color?: string;
|
class?: string
|
||||||
quantity?: number;
|
}
|
||||||
staticity?: number;
|
|
||||||
ease?: number;
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
class?: string;
|
color: "#FFF",
|
||||||
};
|
quantity: 100,
|
||||||
|
staticity: 50,
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
ease: 50,
|
||||||
color: '#FFF',
|
class: ""
|
||||||
quantity: 100,
|
})
|
||||||
staticity: 50,
|
|
||||||
ease: 50,
|
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||||
class: '',
|
const canvasContainerRef = ref<HTMLDivElement | null>(null)
|
||||||
});
|
const context = ref<CanvasRenderingContext2D | null>(null)
|
||||||
|
const circles = ref<Circle[]>([])
|
||||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
const mouse = reactive<{ x: number; y: number }>({ x: 0, y: 0 })
|
||||||
const canvasContainerRef = ref<HTMLDivElement | null>(null);
|
const canvasSize = reactive<{ w: number; h: number }>({ w: 0, h: 0 })
|
||||||
const context = ref<CanvasRenderingContext2D | null>(null);
|
const { x: mouseX, y: mouseY } = useMouse()
|
||||||
const circles = ref<Circle[]>([]);
|
const { pixelRatio } = useDevicePixelRatio()
|
||||||
const mouse = reactive<{ x: number; y: number }>({ x: 0, y: 0 });
|
|
||||||
const canvasSize = reactive<{ w: number; h: number }>({ w: 0, h: 0 });
|
const color = computed(() => {
|
||||||
const { x: mouseX, y: mouseY } = useMouse();
|
// Remove the leading '#' if it's present
|
||||||
const { pixelRatio } = useDevicePixelRatio();
|
let hex = props.color.replace(/^#/, "")
|
||||||
|
|
||||||
const color = computed(() => {
|
// If the hex code is 3 characters, expand it to 6 characters
|
||||||
// Remove the leading '#' if it's present
|
if (hex.length === 3) {
|
||||||
let hex = props.color.replace(/^#/, '');
|
hex = hex
|
||||||
|
.split("")
|
||||||
// If the hex code is 3 characters, expand it to 6 characters
|
.map((char) => char + char)
|
||||||
if (hex.length === 3) {
|
.join("")
|
||||||
hex = hex
|
}
|
||||||
.split('')
|
|
||||||
.map((char) => char + char)
|
// Parse the r, g, b values from the hex string
|
||||||
.join('');
|
const bigint = parseInt(hex, 16)
|
||||||
}
|
const r = (bigint >> 16) & 255 // Extract the red component
|
||||||
|
const g = (bigint >> 8) & 255 // Extract the green component
|
||||||
// Parse the r, g, b values from the hex string
|
const b = bigint & 255 // Extract the blue component
|
||||||
const bigint = parseInt(hex, 16);
|
|
||||||
const r = (bigint >> 16) & 255; // Extract the red component
|
// Return the RGB values as a string separated by spaces
|
||||||
const g = (bigint >> 8) & 255; // Extract the green component
|
return `${r} ${g} ${b}`
|
||||||
const b = bigint & 255; // Extract the blue component
|
})
|
||||||
|
|
||||||
// Return the RGB values as a string separated by spaces
|
onMounted(() => {
|
||||||
return `${r} ${g} ${b}`;
|
if (canvasRef.value) {
|
||||||
});
|
context.value = canvasRef.value.getContext("2d")
|
||||||
|
}
|
||||||
onMounted(() => {
|
|
||||||
if (canvasRef.value) {
|
initCanvas()
|
||||||
context.value = canvasRef.value.getContext('2d');
|
animate()
|
||||||
}
|
window.addEventListener("resize", initCanvas)
|
||||||
|
})
|
||||||
initCanvas();
|
|
||||||
animate();
|
onBeforeUnmount(() => {
|
||||||
window.addEventListener('resize', initCanvas);
|
window.removeEventListener("resize", initCanvas)
|
||||||
});
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
watch([mouseX, mouseY], () => {
|
||||||
window.removeEventListener('resize', initCanvas);
|
onMouseMove()
|
||||||
});
|
})
|
||||||
|
|
||||||
watch([mouseX, mouseY], () => {
|
function initCanvas() {
|
||||||
onMouseMove();
|
resizeCanvas()
|
||||||
});
|
drawParticles()
|
||||||
|
}
|
||||||
function initCanvas() {
|
|
||||||
resizeCanvas();
|
function onMouseMove() {
|
||||||
drawParticles();
|
if (canvasRef.value) {
|
||||||
}
|
const rect = canvasRef.value.getBoundingClientRect()
|
||||||
|
const { w, h } = canvasSize
|
||||||
function onMouseMove() {
|
const x = mouseX.value - rect.left - w / 2
|
||||||
if (canvasRef.value) {
|
const y = mouseY.value - rect.top - h / 2
|
||||||
const rect = canvasRef.value.getBoundingClientRect();
|
|
||||||
const { w, h } = canvasSize;
|
const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2
|
||||||
const x = mouseX.value - rect.left - w / 2;
|
if (inside) {
|
||||||
const y = mouseY.value - rect.top - h / 2;
|
mouse.x = x
|
||||||
|
mouse.y = y
|
||||||
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
|
||||||
function resizeCanvas() {
|
canvasSize.h = canvasContainerRef.value.offsetHeight
|
||||||
if (canvasContainerRef.value && canvasRef.value && context.value) {
|
canvasRef.value.width = canvasSize.w * pixelRatio.value
|
||||||
circles.value.length = 0;
|
canvasRef.value.height = canvasSize.h * pixelRatio.value
|
||||||
canvasSize.w = canvasContainerRef.value.offsetWidth;
|
canvasRef.value.style.width = canvasSize.w + "px"
|
||||||
canvasSize.h = canvasContainerRef.value.offsetHeight;
|
canvasRef.value.style.height = canvasSize.h + "px"
|
||||||
canvasRef.value.width = canvasSize.w * pixelRatio.value;
|
context.value.scale(pixelRatio.value, 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
|
||||||
function circleParams(): Circle {
|
const translateY = 0
|
||||||
const x = Math.floor(Math.random() * canvasSize.w);
|
const size = Math.floor(Math.random() * 2) + 1
|
||||||
const y = Math.floor(Math.random() * canvasSize.h);
|
const alpha = 0
|
||||||
const translateX = 0;
|
const targetAlpha = parseFloat((Math.random() * 0.6 + 0.1).toFixed(1))
|
||||||
const translateY = 0;
|
const dx = (Math.random() - 0.5) * 0.2
|
||||||
const size = Math.floor(Math.random() * 2) + 1;
|
const dy = (Math.random() - 0.5) * 0.2
|
||||||
const alpha = 0;
|
const magnetism = 0.1 + Math.random() * 4
|
||||||
const targetAlpha = parseFloat((Math.random() * 0.6 + 0.1).toFixed(1));
|
return {
|
||||||
const dx = (Math.random() - 0.5) * 0.2;
|
x,
|
||||||
const dy = (Math.random() - 0.5) * 0.2;
|
y,
|
||||||
const magnetism = 0.1 + Math.random() * 4;
|
translateX,
|
||||||
return {
|
translateY,
|
||||||
x,
|
size,
|
||||||
y,
|
alpha,
|
||||||
translateX,
|
targetAlpha,
|
||||||
translateY,
|
dx,
|
||||||
size,
|
dy,
|
||||||
alpha,
|
magnetism
|
||||||
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)
|
||||||
function drawCircle(circle: Circle, update = false) {
|
context.value.beginPath()
|
||||||
if (context.value) {
|
context.value.arc(x, y, size, 0, 2 * Math.PI)
|
||||||
const { x, y, translateX, translateY, size, alpha } = circle;
|
context.value.fillStyle = `rgba(${color.value.split(" ").join(", ")}, ${alpha})`
|
||||||
context.value.translate(translateX, translateY);
|
context.value.fill()
|
||||||
context.value.beginPath();
|
context.value.setTransform(pixelRatio.value, 0, 0, pixelRatio.value, 0, 0)
|
||||||
context.value.arc(x, y, size, 0, 2 * Math.PI);
|
|
||||||
context.value.fillStyle = `rgba(${color.value.split(' ').join(', ')}, ${alpha})`;
|
if (!update) {
|
||||||
context.value.fill();
|
circles.value.push(circle)
|
||||||
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 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++) {
|
||||||
function drawParticles() {
|
const circle = circleParams()
|
||||||
clearContext();
|
drawCircle(circle)
|
||||||
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 remapValue(
|
|
||||||
value: number,
|
function animate() {
|
||||||
start1: number,
|
clearContext()
|
||||||
end1: number,
|
circles.value.forEach((circle, i) => {
|
||||||
start2: number,
|
// Handle the alpha value
|
||||||
end2: number,
|
const edge = [
|
||||||
): number {
|
circle.x + circle.translateX - circle.size, // distance from left edge
|
||||||
const remapped = ((value - start1) * (end2 - start2)) / (end1 - start1) + start2;
|
canvasSize.w - circle.x - circle.translateX - circle.size, // distance from right edge
|
||||||
return remapped > 0 ? remapped : 0;
|
circle.y + circle.translateY - circle.size, // distance from top edge
|
||||||
}
|
canvasSize.h - circle.y - circle.translateY - circle.size // distance from bottom edge
|
||||||
|
]
|
||||||
function animate() {
|
|
||||||
clearContext();
|
const closestEdge = edge.reduce((a, b) => Math.min(a, b))
|
||||||
circles.value.forEach((circle, i) => {
|
const remapClosestEdge = parseFloat(remapValue(closestEdge, 0, 20, 0, 1).toFixed(2))
|
||||||
// Handle the alpha value
|
|
||||||
const edge = [
|
if (remapClosestEdge > 1) {
|
||||||
circle.x + circle.translateX - circle.size, // distance from left edge
|
circle.alpha += 0.02
|
||||||
canvasSize.w - circle.x - circle.translateX - circle.size, // distance from right edge
|
if (circle.alpha > circle.targetAlpha) circle.alpha = circle.targetAlpha
|
||||||
circle.y + circle.translateY - circle.size, // distance from top edge
|
} else {
|
||||||
canvasSize.h - circle.y - circle.translateY - circle.size, // distance from bottom edge
|
circle.alpha = circle.targetAlpha * remapClosestEdge
|
||||||
];
|
}
|
||||||
|
|
||||||
const closestEdge = edge.reduce((a, b) => Math.min(a, b));
|
circle.x += circle.dx
|
||||||
const remapClosestEdge = parseFloat(remapValue(closestEdge, 0, 20, 0, 1).toFixed(2));
|
circle.y += circle.dy
|
||||||
|
circle.translateX += (mouse.x / (props.staticity / circle.magnetism) - circle.translateX) / props.ease
|
||||||
if (remapClosestEdge > 1) {
|
circle.translateY += (mouse.y / (props.staticity / circle.magnetism) - circle.translateY) / props.ease
|
||||||
circle.alpha += 0.02;
|
|
||||||
if (circle.alpha > circle.targetAlpha) circle.alpha = circle.targetAlpha;
|
// circle gets out of the canvas
|
||||||
} else {
|
if (circle.x < -circle.size || circle.x > canvasSize.w + circle.size || circle.y < -circle.size || circle.y > canvasSize.h + circle.size) {
|
||||||
circle.alpha = circle.targetAlpha * remapClosestEdge;
|
// remove the circle from the array
|
||||||
}
|
circles.value.splice(i, 1)
|
||||||
|
// create a new circle
|
||||||
circle.x += circle.dx;
|
const newCircle = circleParams()
|
||||||
circle.y += circle.dy;
|
drawCircle(newCircle)
|
||||||
circle.translateX +=
|
// update the circle position
|
||||||
(mouse.x / (props.staticity / circle.magnetism) - circle.translateX) / props.ease;
|
} else {
|
||||||
circle.translateY +=
|
drawCircle(
|
||||||
(mouse.y / (props.staticity / circle.magnetism) - circle.translateY) / props.ease;
|
{
|
||||||
|
...circle,
|
||||||
// circle gets out of the canvas
|
x: circle.x,
|
||||||
if (
|
y: circle.y,
|
||||||
circle.x < -circle.size ||
|
translateX: circle.translateX,
|
||||||
circle.x > canvasSize.w + circle.size ||
|
translateY: circle.translateY,
|
||||||
circle.y < -circle.size ||
|
alpha: circle.alpha
|
||||||
circle.y > canvasSize.h + circle.size
|
},
|
||||||
) {
|
true
|
||||||
// remove the circle from the array
|
)
|
||||||
circles.value.splice(i, 1);
|
}
|
||||||
// create a new circle
|
})
|
||||||
const newCircle = circleParams();
|
window.requestAnimationFrame(animate)
|
||||||
drawCircle(newCircle);
|
}
|
||||||
// update the circle position
|
</script>
|
||||||
} else {
|
|
||||||
drawCircle(
|
|
||||||
{
|
|
||||||
...circle,
|
|
||||||
x: circle.x,
|
|
||||||
y: circle.y,
|
|
||||||
translateX: circle.translateX,
|
|
||||||
translateY: circle.translateY,
|
|
||||||
alpha: circle.alpha,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.requestAnimationFrame(animate);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import setupPermissionGuard from "@/router/guard/permisstion"
|
|||||||
import { setRouteTitle } from "@/utils/title"
|
import { setRouteTitle } from "@/utils/title"
|
||||||
// 为了已登录用户直接进入login
|
// 为了已登录用户直接进入login
|
||||||
import { useUserStore } from "@/store"
|
import { useUserStore } from "@/store"
|
||||||
|
import NProgress from "nprogress" // progress bar
|
||||||
|
import "nprogress/nprogress.css"
|
||||||
|
|
||||||
function setupPageGuard(router) {
|
function setupPageGuard(router) {
|
||||||
router.beforeEach(async (to) => {
|
router.beforeEach(async (to) => {
|
||||||
@@ -14,6 +16,7 @@ function setupPageGuard(router) {
|
|||||||
// 设置站点document.title
|
// 设置站点document.title
|
||||||
router.afterEach((to, from) => {
|
router.afterEach((to, from) => {
|
||||||
setRouteTitle(to.meta.title)
|
setRouteTitle(to.meta.title)
|
||||||
|
NProgress.done()
|
||||||
})
|
})
|
||||||
// 设置如果已登录用户进入login页面则直接进入工作台
|
// 设置如果已登录用户进入login页面则直接进入工作台
|
||||||
router.beforeEach((to) => {
|
router.beforeEach((to) => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { appRoutes } from "../routes"
|
|||||||
import { WHITE_LIST, NOT_FOUND } from "../constants"
|
import { WHITE_LIST, NOT_FOUND } from "../constants"
|
||||||
// 权限守卫
|
// 权限守卫
|
||||||
export default function setupPermissionGuard(router) {
|
export default function setupPermissionGuard(router) {
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from) => {
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const Permission = usePermission()
|
const Permission = usePermission()
|
||||||
@@ -34,16 +34,18 @@ export default function setupPermissionGuard(router) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (exist && permissionsAllow) {
|
if (exist && permissionsAllow) {
|
||||||
next()
|
return true
|
||||||
} else next(NOT_FOUND)
|
} else {
|
||||||
|
return NOT_FOUND
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line no-lonely-if
|
// eslint-disable-next-line no-lonely-if
|
||||||
if (permissionsAllow) next()
|
if (permissionsAllow) {
|
||||||
else {
|
return true
|
||||||
|
} else {
|
||||||
const destination = Permission.findFirstPermissionRoute(appRoutes, userStore.role) || NOT_FOUND
|
const destination = Permission.findFirstPermissionRoute(appRoutes, userStore.role) || NOT_FOUND
|
||||||
next(destination)
|
return destination
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NProgress.done()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import { Ref, computed } from "vue"
|
|||||||
import { storeToRefs } from "pinia"
|
import { storeToRefs } from "pinia"
|
||||||
import { useAppStore } from "@/store"
|
import { useAppStore } from "@/store"
|
||||||
|
|
||||||
// 单个每月项目数量对象格式
|
|
||||||
interface IData {
|
interface IData {
|
||||||
mouth: string
|
mouth: string
|
||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
// 响应.data的数据格式
|
|
||||||
interface ResData {
|
interface ResData {
|
||||||
data: IData[]
|
data: IData[]
|
||||||
}
|
}
|
||||||
@@ -15,7 +13,8 @@ interface ResData {
|
|||||||
function useVueDataUI(data: Ref<ResData>) {
|
function useVueDataUI(data: Ref<ResData>) {
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const { theme } = storeToRefs(appStore)
|
const { theme } = storeToRefs(appStore)
|
||||||
// 结构pinia储存的主体响应式变量
|
|
||||||
|
// 基础数据集
|
||||||
const initialData = [
|
const initialData = [
|
||||||
{
|
{
|
||||||
name: "项目数量",
|
name: "项目数量",
|
||||||
@@ -27,36 +26,38 @@ function useVueDataUI(data: Ref<ResData>) {
|
|||||||
smooth: true
|
smooth: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
const chartData = computed<any[]>(() => {
|
|
||||||
|
const chartData = computed(() => {
|
||||||
if (data.value) {
|
if (data.value) {
|
||||||
const countData = data.value.data.map((it) => it.count)
|
const countData = data.value.data.map((it) => it.count)
|
||||||
initialData[0].series = countData
|
// 注意:这里直接修改了 initialData[0].series,为避免副作用,建议每次返回新对象
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
...initialData[0],
|
||||||
|
series: countData
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
return initialData
|
return initialData
|
||||||
})
|
})
|
||||||
// 暗黑模式识别(这是存在pinia的)
|
|
||||||
const darkMode = document.body.getAttribute("arco-theme")
|
// 基础配置(不包含颜色相关部分)
|
||||||
const initialConfig = {
|
const baseConfig = {
|
||||||
theme: darkMode === "dark" ? "celebrationNight" : "",
|
|
||||||
responsive: false,
|
responsive: false,
|
||||||
customPalette: [],
|
customPalette: [],
|
||||||
downsample: { threshold: 500 },
|
downsample: { threshold: 500 },
|
||||||
chart: {
|
chart: {
|
||||||
fontFamily: "inherit",
|
fontFamily: "inherit",
|
||||||
backgroundColor: "#FFFFFFff",
|
|
||||||
color: "#1A1A1Aff",
|
|
||||||
height: 300,
|
height: 300,
|
||||||
width: 1200,
|
width: 1200,
|
||||||
padding: { top: 36, right: 24, bottom: 48, left: 48 },
|
padding: { top: 36, right: 24, bottom: 48, left: 48 },
|
||||||
highlighter: { color: "#1A1A1Aff", opacity: 5, useLine: false, lineDasharray: 2, lineWidth: 1 },
|
highlighter: { opacity: 5, useLine: false, lineDasharray: 2, lineWidth: 1 },
|
||||||
grid: {
|
grid: {
|
||||||
stroke: "#e1e5e8ff",
|
|
||||||
showVerticalLines: false,
|
showVerticalLines: false,
|
||||||
showHorizontalLines: false,
|
showHorizontalLines: false,
|
||||||
position: "middle",
|
position: "middle",
|
||||||
frame: {
|
frame: {
|
||||||
show: false,
|
show: false,
|
||||||
stroke: "#E1E5E8ff",
|
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
strokeLinecap: "round",
|
strokeLinecap: "round",
|
||||||
strokeLinejoin: "round",
|
strokeLinejoin: "round",
|
||||||
@@ -64,7 +65,6 @@ function useVueDataUI(data: Ref<ResData>) {
|
|||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
show: true,
|
show: true,
|
||||||
color: "#1A1A1Aff",
|
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
axis: { yLabel: "", yLabelOffsetX: 0, xLabel: "", xLabelOffsetY: 14, fontSize: 12 },
|
axis: { yLabel: "", yLabelOffsetX: 0, xLabel: "", xLabelOffsetY: 14, fontSize: 12 },
|
||||||
zeroLine: { show: true },
|
zeroLine: { show: true },
|
||||||
@@ -81,22 +81,8 @@ function useVueDataUI(data: Ref<ResData>) {
|
|||||||
scaleMax: 20
|
scaleMax: 20
|
||||||
},
|
},
|
||||||
xAxisLabels: {
|
xAxisLabels: {
|
||||||
color: "#1A1A1Aff",
|
|
||||||
show: true,
|
show: true,
|
||||||
values: [
|
values: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
|
||||||
"一月",
|
|
||||||
"二月",
|
|
||||||
"三月",
|
|
||||||
"四月",
|
|
||||||
"五月",
|
|
||||||
"六月",
|
|
||||||
"七月",
|
|
||||||
"八月",
|
|
||||||
"九月",
|
|
||||||
"十月",
|
|
||||||
"十一月",
|
|
||||||
"十二月"
|
|
||||||
],
|
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
showOnlyFirstAndLast: false,
|
showOnlyFirstAndLast: false,
|
||||||
showOnlyAtModulo: false,
|
showOnlyAtModulo: false,
|
||||||
@@ -108,26 +94,22 @@ function useVueDataUI(data: Ref<ResData>) {
|
|||||||
},
|
},
|
||||||
comments: { show: true, showInTooltip: true, width: 200, offsetX: 0, offsetY: 0 },
|
comments: { show: true, showInTooltip: true, width: 200, offsetX: 0, offsetY: 0 },
|
||||||
labels: { fontSize: 10, prefix: "", suffix: "" },
|
labels: { fontSize: 10, prefix: "", suffix: "" },
|
||||||
legend: { color: "#1A1A1Aff", show: false, fontSize: 16 },
|
legend: { show: false, fontSize: 16 },
|
||||||
title: {
|
title: {
|
||||||
text: "项目每月统计",
|
text: "项目每月统计",
|
||||||
color: "#1A1A1Aff",
|
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
bold: true,
|
bold: true,
|
||||||
textAlign: "left",
|
textAlign: "left",
|
||||||
paddingLeft: 5,
|
paddingLeft: 5,
|
||||||
paddingRight: 0,
|
paddingRight: 0,
|
||||||
subtitle: { color: "#CCCCCCff", text: "", fontSize: 16, bold: false },
|
subtitle: { text: "", fontSize: 16, bold: false },
|
||||||
show: true
|
show: true
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: true,
|
show: true,
|
||||||
color: "#1A1A1Aff",
|
|
||||||
backgroundColor: "#FFFFFFff",
|
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
customFormat: null,
|
customFormat: null,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
borderColor: "#e1e5e8",
|
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
backgroundOpacity: 30,
|
backgroundOpacity: 30,
|
||||||
position: "center",
|
position: "center",
|
||||||
@@ -170,16 +152,73 @@ function useVueDataUI(data: Ref<ResData>) {
|
|||||||
},
|
},
|
||||||
showTable: false
|
showTable: false
|
||||||
}
|
}
|
||||||
|
|
||||||
const chartConfig = computed(() => {
|
const chartConfig = computed(() => {
|
||||||
|
const isDark = theme.value === "dark"
|
||||||
|
|
||||||
|
// 计算 Y 轴最大值
|
||||||
|
let scaleMax = 10
|
||||||
if (data.value) {
|
if (data.value) {
|
||||||
const countData = data.value.data.map((it) => it.count)
|
const countData = data.value.data.map((it) => it.count)
|
||||||
initialConfig.chart.grid.labels.yAxis.scaleMax = Math.max(...countData) ? Math.max(...countData) : 10
|
scaleMax = Math.max(...countData) || 10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 动态生成颜色相关配置
|
||||||
return {
|
return {
|
||||||
...initialConfig,
|
...baseConfig,
|
||||||
theme: theme.value === "dark" ? "celebrationNight" : ""
|
// 不设置 theme,完全自定义
|
||||||
|
chart: {
|
||||||
|
...baseConfig.chart,
|
||||||
|
// 背景色
|
||||||
|
backgroundColor: isDark ? "#1A1A1Aff" : "#FFFFFFff",
|
||||||
|
// 全局文字颜色
|
||||||
|
color: isDark ? "#FFFFFFFF" : "#1A1A1Aff",
|
||||||
|
highlighter: {
|
||||||
|
...baseConfig.chart.highlighter,
|
||||||
|
color: isDark ? "#FFFFFFFF" : "#1A1A1Aff"
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
...baseConfig.chart.grid,
|
||||||
|
stroke: isDark ? "#444444ff" : "#e1e5e8ff",
|
||||||
|
frame: {
|
||||||
|
...baseConfig.chart.grid.frame,
|
||||||
|
stroke: isDark ? "#444444ff" : "#E1E5E8ff"
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
...baseConfig.chart.grid.labels,
|
||||||
|
color: isDark ? "#FFFFFFFF" : "#1A1A1Aff",
|
||||||
|
yAxis: {
|
||||||
|
...baseConfig.chart.grid.labels.yAxis,
|
||||||
|
scaleMax
|
||||||
|
},
|
||||||
|
xAxisLabels: {
|
||||||
|
...baseConfig.chart.grid.labels.xAxisLabels,
|
||||||
|
color: isDark ? "#FFFFFFFF" : "#1A1A1Aff"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
...baseConfig.chart.legend,
|
||||||
|
color: isDark ? "#FFFFFFFF" : "#1A1A1Aff"
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
...baseConfig.chart.title,
|
||||||
|
color: isDark ? "#FFFFFFFF" : "#1A1A1Aff",
|
||||||
|
subtitle: {
|
||||||
|
...baseConfig.chart.title.subtitle,
|
||||||
|
color: isDark ? "#AAAAAAff" : "#CCCCCCff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
...baseConfig.chart.tooltip,
|
||||||
|
color: isDark ? "#FFFFFFFF" : "#1A1A1Aff",
|
||||||
|
backgroundColor: isDark ? "#333333ff" : "#FFFFFFff",
|
||||||
|
borderColor: isDark ? "#555555ff" : "#e1e5e8"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return { chartData, chartConfig }
|
return { chartData, chartConfig }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<a-spin class="chartContainer" :loading="isPending" tip="图标数据加载中...">
|
<a-spin class="chartContainer" :loading="isPending" tip="图标数据加载中...">
|
||||||
<div class="flex justify-center items-center" v-if="!isPending">
|
<div class="flex justify-center items-center" v-if="!isPending">
|
||||||
<template v-if="isError">
|
<template v-if="isError">
|
||||||
<img class="w-[200px] h-[300px]" src="@/assets/img/ErrorLoad.svg" alt="" />
|
<img class="w-50 h-75" src="@/assets/img/ErrorLoad.svg" alt="" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<VueUiXy :dataset="chartData" :config="chartConfig" :style="{ padding: '10px' }" />
|
<VueUiXy :dataset="chartData" :config="chartConfig" :style="{ padding: '10px' }" />
|
||||||
@@ -28,6 +28,8 @@ const { isPending, data, isError } = useQuery({
|
|||||||
|
|
||||||
// vue-data-ui图表
|
// vue-data-ui图表
|
||||||
const { chartData, chartConfig } = useVueDataUI(data)
|
const { chartData, chartConfig } = useVueDataUI(data)
|
||||||
|
|
||||||
|
// 暗黑模式配置
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -16,7 +16,7 @@ export default ({ mode }) => {
|
|||||||
vueJsx(),
|
vueJsx(),
|
||||||
visualizer({
|
visualizer({
|
||||||
open: true,
|
open: true,
|
||||||
filename: "visualizer.html" // 分析图生成的文件名
|
filename: "./dist/visualizer.html" // 分析图生成的文件名
|
||||||
}),
|
}),
|
||||||
tailwindcss()
|
tailwindcss()
|
||||||
],
|
],
|
||||||
@@ -33,8 +33,25 @@ export default ({ mode }) => {
|
|||||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false
|
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
chunkSizeWarningLimit: 3000,
|
chunkSizeWarningLimit: 3000
|
||||||
// assetsPublicPath: "./"
|
// assetsPublicPath: "./"
|
||||||
|
// v8版本又报tinymce is not defined,只有遗憾业务js大的问题
|
||||||
|
/**
|
||||||
|
rolldownOptions: {
|
||||||
|
output: {
|
||||||
|
codeSplitting: {
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
test: (id) => id.includes("tinymce"),
|
||||||
|
name: "tinymce",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// vite v7版本配置:V8已经移除
|
||||||
|
/**
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
manualChunks: (id) => {
|
manualChunks: (id) => {
|
||||||
@@ -43,6 +60,7 @@ export default ({ mode }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
host: "0.0.0.0",
|
host: "0.0.0.0",
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
1. tinymce 7.9.1
|
1. tinymce 7.9.1
|
||||||
2. vue-router 4.6.4
|
2. vite 7.3.1 -> 8.0.0使用分块打包tinymce会找不到模块,暂时取消分块
|
||||||
Reference in New Issue
Block a user