Compare commits

..

2 Commits

Author SHA1 Message Date
d1b5fdcdd3 修复说明reuse文件夹图片丢失问题 2026-04-16 17:29:40 +08:00
96bd1bdf9a 新增内容 2026-03-26 21:58:14 +08:00
12 changed files with 1430 additions and 6653 deletions

2264
cdTMP/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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"
} }
} }

View File

@@ -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"

View File

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

View File

@@ -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) => {

View File

@@ -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()
}) })
} }

View File

@@ -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 }
} }

View File

@@ -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

View File

@@ -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",

View File

@@ -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会找不到模块暂时取消分块