feat: 🚀 生产入库
This commit is contained in:
@@ -85,7 +85,7 @@ export const COLUMNS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: "left",
|
align: "left",
|
||||||
label: "已出库数据",
|
label: "已出库数量",
|
||||||
prop: "realityQty",
|
prop: "realityQty",
|
||||||
width: "120"
|
width: "120"
|
||||||
},
|
},
|
||||||
|
|||||||
132
src/views/login/index2.vue
Normal file
132
src/views/login/index2.vue
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<el-main></el-main>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
//useRouter
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { useMsg } from "@/hooks/useMsg";
|
||||||
|
//登录请求接口
|
||||||
|
import { loginApi } from "@/api/modules/login";
|
||||||
|
//全局状态
|
||||||
|
|
||||||
|
import { getGlobalStatusApi } from "@/api/modules/global";
|
||||||
|
//全局仓库
|
||||||
|
import { getStockApi } from "@/api/modules/global";
|
||||||
|
//用户信息存储
|
||||||
|
import { useUserStore } from "@/stores/modules/user";
|
||||||
|
import { usePathUrl } from "@/hooks/usePathUrl";
|
||||||
|
//全局状态
|
||||||
|
import { useStatusStore } from "@/stores/modules/status";
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const statusStore = useStatusStore();
|
||||||
|
// 路由
|
||||||
|
const $route = useRoute();
|
||||||
|
const $router = useRouter();
|
||||||
|
|
||||||
|
const getGlobalStatus = async () => {
|
||||||
|
const result = await getGlobalStatusApi();
|
||||||
|
if (result.status === 200) {
|
||||||
|
const { data } = result;
|
||||||
|
//设置全局状态
|
||||||
|
statusStore.setGlobalStatus(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getStock = async () => {
|
||||||
|
const result = await getStockApi();
|
||||||
|
if (result.status === 200) {
|
||||||
|
const { data } = result;
|
||||||
|
data.forEach((item: any) => {
|
||||||
|
item.code = item.code + "_" + "$" + item.erpOrgCode;
|
||||||
|
});
|
||||||
|
//设置全局仓库
|
||||||
|
userStore.setWarehouse(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getUcOnline = (phpToken: any, refreshToken: any) => {
|
||||||
|
let httpUrl = import.meta.env.VITE_SINGLE_URL + "uc/online";
|
||||||
|
fetch(httpUrl, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
keepalive: true,
|
||||||
|
headers: {
|
||||||
|
Authorization: phpToken,
|
||||||
|
"Refresh-Authorization": refreshToken
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUcOffline = (redirectUrl: any) => {
|
||||||
|
let httpUrl = import.meta.env.VITE_SINGLE_URL + "uc/offline";
|
||||||
|
fetch(httpUrl, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
keepalive: true,
|
||||||
|
headers: {
|
||||||
|
Authorization: userStore.phpToken,
|
||||||
|
"Refresh-Authorization": userStore.refreshToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((res: any) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
useMsg("success", "退出登录成功 !");
|
||||||
|
setTimeout(() => {
|
||||||
|
location.href = redirectUrl;
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
useMsg("error", "退出登录失败,请稍后再试 !");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置用户数据
|
||||||
|
const setUserData = (data: any) => {
|
||||||
|
// 组装token
|
||||||
|
let newUserToken = data.tokenInfo.tokenType + " " + data.tokenInfo.token;
|
||||||
|
let RefreshToken = data.tokenInfo.tokenType + " " + data.tokenInfo.refreshToken;
|
||||||
|
let phpToken = data.tokenInfo.tokenType + " " + data.tokenInfo.phpToken;
|
||||||
|
// 设置token
|
||||||
|
userStore.setToken(newUserToken);
|
||||||
|
userStore.setRefreshToken(RefreshToken);
|
||||||
|
userStore.setPhpToken(phpToken);
|
||||||
|
// 设置用户信息
|
||||||
|
userStore.setUserInfo(data.userInfo);
|
||||||
|
// 设置组织id
|
||||||
|
userStore.setOrgId(data.userInfo.orgId);
|
||||||
|
getUcOnline(phpToken, RefreshToken);
|
||||||
|
getGlobalStatus();
|
||||||
|
getStock();
|
||||||
|
//跳转
|
||||||
|
setTimeout(() => {
|
||||||
|
$router.push({ path: "/" });
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
const loginHttp = async (code: any) => {
|
||||||
|
const result: Record<string, any> = await loginApi({ code: code });
|
||||||
|
if (result.status === 200) {
|
||||||
|
setUserData(result.data);
|
||||||
|
} else {
|
||||||
|
getUcOffline(usePathUrl());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 登录前的判断
|
||||||
|
const login = () => {
|
||||||
|
const { code } = $route.query;
|
||||||
|
// 没有code直接跳转到登录页
|
||||||
|
if (!code) {
|
||||||
|
getUcOffline(usePathUrl());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 有code就登录请求
|
||||||
|
loginHttp(code);
|
||||||
|
};
|
||||||
|
login();
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
.el-main {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
411
src/views/login/index3.vue
Normal file
411
src/views/login/index3.vue
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
<template>
|
||||||
|
<div class="curve-container relative w-full h-full overflow-hidden">
|
||||||
|
<svg :width="svgWidth" :height="svgHeight">
|
||||||
|
<!-- 绘制曲线 -->
|
||||||
|
<path
|
||||||
|
v-for="(segment, index) in curveSegments"
|
||||||
|
:key="index"
|
||||||
|
:d="generateCurvePath(segment.startNode, segment.endNode)"
|
||||||
|
:stroke="segment.color"
|
||||||
|
stroke-width="14"
|
||||||
|
stroke-linecap="round"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
<!-- 绘制节点 -->
|
||||||
|
<circle
|
||||||
|
v-for="(node, index) in nodes"
|
||||||
|
:key="index"
|
||||||
|
:cx="node.x"
|
||||||
|
:cy="node.y"
|
||||||
|
:r="isHovered[index] ? hoverRadius : nodeRadius"
|
||||||
|
:fill="isHovered[index] ? hoverFill : nodeFill"
|
||||||
|
:stroke="isHovered[index] ? hoverStroke : nodeStroke"
|
||||||
|
:stroke-width="isHovered[index] ? hoverStrokeWidth : nodeStrokeWidth"
|
||||||
|
:filter="isHovered[index] ? 'drop-shadow(0 0 5px rgba(0,0,0,0.3))' : 'none'"
|
||||||
|
@mouseenter="handleNodeMouseEnter(index)"
|
||||||
|
@mouseleave="handleNodeMouseLeave(index)"
|
||||||
|
@mousedown="startDragging(index, $event)"
|
||||||
|
cursor="pointer"
|
||||||
|
/>
|
||||||
|
<!-- 绘制需求池背景 -->
|
||||||
|
<rect
|
||||||
|
x="20"
|
||||||
|
y="80"
|
||||||
|
width="140"
|
||||||
|
height="220"
|
||||||
|
rx="12"
|
||||||
|
ry="12"
|
||||||
|
fill="#f0f7ff"
|
||||||
|
stroke="#aac5e8"
|
||||||
|
stroke-width="4"
|
||||||
|
opacity="0.9"
|
||||||
|
filter="drop-shadow(0 4px 6px rgba(0,0,0,0.1))"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="700"
|
||||||
|
y="80"
|
||||||
|
width="120"
|
||||||
|
height="220"
|
||||||
|
rx="12"
|
||||||
|
ry="12"
|
||||||
|
fill="#e6f5ef"
|
||||||
|
stroke="#98d7c2"
|
||||||
|
stroke-width="2"
|
||||||
|
opacity="0.9"
|
||||||
|
filter="drop-shadow(0 4px 6px rgba(0,0,0,0.1))"
|
||||||
|
/>
|
||||||
|
<!-- 绘制需求池标签 -->
|
||||||
|
<text x="80" y="65" text-anchor="middle" font-size="14" fill="#2c6aa0" font-weight="bold">待处理</text>
|
||||||
|
<text x="760" y="65" text-anchor="middle" font-size="14" fill="#278865" font-weight="bold">已完成</text>
|
||||||
|
<!-- 绘制需求池中的小节点 -->
|
||||||
|
<circle
|
||||||
|
v-for="(poolNode, index) in poolNodes"
|
||||||
|
:key="index"
|
||||||
|
:cx="poolNode.isJumping ? poolNode.jumpPosition.x : constrainedPoolNodePosition[index].x"
|
||||||
|
:cy="poolNode.isJumping ? poolNode.jumpPosition.y : constrainedPoolNodePosition[index].y + getBounceOffset(index)"
|
||||||
|
:r="poolNodeRadius"
|
||||||
|
:fill="poolNode.fill"
|
||||||
|
:stroke="poolNode.stroke"
|
||||||
|
:stroke-width="1.5"
|
||||||
|
:filter="
|
||||||
|
poolNode.isJumping
|
||||||
|
? 'drop-shadow(0 0 8px rgba(0,0,0,0.5))'
|
||||||
|
: poolNode.isHovered
|
||||||
|
? 'drop-shadow(0 0 6px rgba(0,0,0,0.35))'
|
||||||
|
: 'none'
|
||||||
|
"
|
||||||
|
@mouseenter="handlePoolNodeMouseEnter(index)"
|
||||||
|
@mouseleave="handlePoolNodeMouseLeave(index)"
|
||||||
|
cursor="pointer"
|
||||||
|
/>
|
||||||
|
<!-- 需求池装饰元素 -->
|
||||||
|
<g v-if="!isMobileView">
|
||||||
|
<path d="M30,80 L30,300" stroke="#aac5e8" stroke-width="1" stroke-dasharray="5,5" opacity="0.5" />
|
||||||
|
<path d="M110,80 L110,300" stroke="#aac5e8" stroke-width="1" stroke-dasharray="5,5" opacity="0.5" />
|
||||||
|
<path d="M710,80 L710,300" stroke="#98d7c2" stroke-width="1" stroke-dasharray="5,5" opacity="0.5" />
|
||||||
|
<path d="M790,80 L790,300" stroke="#98d7c2" stroke-width="1" stroke-dasharray="5,5" opacity="0.5" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted, onUnmounted } from "vue";
|
||||||
|
|
||||||
|
const svgWidth = ref(800);
|
||||||
|
const svgHeight = ref(400);
|
||||||
|
|
||||||
|
// 节点样式配置
|
||||||
|
const nodeRadius = ref(8);
|
||||||
|
const hoverRadius = ref(12);
|
||||||
|
const nodeFill = ref("#fff");
|
||||||
|
const hoverFill = ref("#e8f4ff");
|
||||||
|
const nodeStroke = ref("#3498db");
|
||||||
|
const hoverStroke = ref("#2980b9");
|
||||||
|
const nodeStrokeWidth = ref(2.5);
|
||||||
|
const hoverStrokeWidth = ref(3.5);
|
||||||
|
|
||||||
|
// 需求池节点样式配置
|
||||||
|
const poolNodeRadius = ref(6);
|
||||||
|
|
||||||
|
// 节点数据
|
||||||
|
const nodes = ref([
|
||||||
|
{ x: 150, y: 220 },
|
||||||
|
{ x: 350, y: 180 },
|
||||||
|
{ x: 550, y: 250 },
|
||||||
|
{ x: 750, y: 220 }
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 曲线分段信息
|
||||||
|
const curveSegments = ref([
|
||||||
|
{ startNode: 0, endNode: 1, color: "#3498db" },
|
||||||
|
{ startNode: 1, endNode: 2, color: "#2ecc71" },
|
||||||
|
{ startNode: 2, endNode: 3, color: "#3498db" }
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 需求池节点数据
|
||||||
|
const poolNodes = ref([
|
||||||
|
{ poolIndex: 0, fill: "#3498db", stroke: "#2980b9", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 0, fill: "#e74c3c", stroke: "#c0392b", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 0, fill: "#9b59b6", stroke: "#8e44ad", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 0, fill: "#e67e22", stroke: "#d35400", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 0, fill: "#f1c40f", stroke: "#f39c12", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 0, fill: "#1abc9c", stroke: "#16a085", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 0, fill: "#3498db", stroke: "#2980b9", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 0, fill: "#e74c3c", stroke: "#c0392b", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 1, fill: "#1abc9c", stroke: "#16a085", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 1, fill: "#3498db", stroke: "#2980b9", isHovered: false, isJumping: false },
|
||||||
|
{ poolIndex: 1, fill: "#95a5a6", stroke: "#7f8c8d", isHovered: false, isJumping: false }
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 跟踪鼠标悬停状态
|
||||||
|
const isHovered = reactive(nodes.value.map(() => false));
|
||||||
|
|
||||||
|
// 响应式视图标志
|
||||||
|
const isMobileView = ref(false);
|
||||||
|
|
||||||
|
// 动画相关
|
||||||
|
const bounceOffsets = ref(poolNodes.value.map(() => 0));
|
||||||
|
let animationFrameId = null;
|
||||||
|
let lastTime = 0;
|
||||||
|
let jumpTimer = null;
|
||||||
|
|
||||||
|
// 存储约束后的节点位置(避免超出需求池)
|
||||||
|
const constrainedPoolNodePosition = ref(poolNodes.value.map(() => ({ x: 0, y: 0 })));
|
||||||
|
|
||||||
|
// 获取需求池节点位置(带边界约束)
|
||||||
|
const getPoolNodePosition = (poolIndex, nodeIndex) => {
|
||||||
|
const poolX = poolIndex === 0 ? 80 : 760;
|
||||||
|
const poolY = 100;
|
||||||
|
const nodesPerColumn = 4;
|
||||||
|
const column = Math.floor(nodeIndex / nodesPerColumn);
|
||||||
|
const row = nodeIndex % nodesPerColumn;
|
||||||
|
const spacingX = 25;
|
||||||
|
const spacingY = 25;
|
||||||
|
|
||||||
|
// 基础位置计算
|
||||||
|
let baseX = poolX + (column - 1) * spacingX;
|
||||||
|
let baseY = poolY + row * spacingY;
|
||||||
|
|
||||||
|
// 需求池边界(考虑节点半径)
|
||||||
|
const nodeRadius = poolNodeRadius.value;
|
||||||
|
const poolLeft = poolIndex === 0 ? 20 + nodeRadius : 700 + nodeRadius;
|
||||||
|
const poolRight = poolIndex === 0 ? 20 + 120 - nodeRadius : 700 + 120 - nodeRadius;
|
||||||
|
const poolTop = 80 + nodeRadius;
|
||||||
|
const poolBottom = 80 + 220 - nodeRadius;
|
||||||
|
|
||||||
|
// 边界约束逻辑
|
||||||
|
const constrainedX = Math.max(poolLeft, Math.min(baseX, poolRight));
|
||||||
|
const constrainedY = Math.max(poolTop, Math.min(baseY, poolBottom));
|
||||||
|
|
||||||
|
return { x: constrainedX, y: constrainedY };
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取节点跳动偏移量
|
||||||
|
const getBounceOffset = index => {
|
||||||
|
return bounceOffsets.value[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 动态生成曲线路径
|
||||||
|
const generateCurvePath = (startNodeIndex, endNodeIndex) => {
|
||||||
|
const start = nodes.value[startNodeIndex];
|
||||||
|
const end = nodes.value[endNodeIndex];
|
||||||
|
|
||||||
|
const dx = end.x - start.x;
|
||||||
|
const dy = end.y - start.y;
|
||||||
|
const controlDistance = Math.max(Math.abs(dx), Math.abs(dy)) * 0.3;
|
||||||
|
|
||||||
|
const cp1x = start.x + dx * 0.3;
|
||||||
|
const cp1y = start.y + dy * 0.2 - controlDistance;
|
||||||
|
const cp2x = start.x + dx * 0.7;
|
||||||
|
const cp2y = start.y + dy * 0.8 + controlDistance;
|
||||||
|
|
||||||
|
return `M${start.x},${start.y} C${cp1x},${cp1y} ${cp2x},${cp2y} ${end.x},${end.y}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理节点鼠标事件
|
||||||
|
const handleNodeMouseEnter = index => {
|
||||||
|
isHovered[index] = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNodeMouseLeave = index => {
|
||||||
|
isHovered[index] = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理需求池节点鼠标事件
|
||||||
|
const handlePoolNodeMouseEnter = index => {
|
||||||
|
poolNodes.value[index].isHovered = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePoolNodeMouseLeave = index => {
|
||||||
|
poolNodes.value[index].isHovered = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 开始节点自动跳入曲线
|
||||||
|
const startAutoJumping = () => {
|
||||||
|
const minInterval = 3000;
|
||||||
|
const maxInterval = 5000;
|
||||||
|
|
||||||
|
const jumpRandomNode = () => {
|
||||||
|
const leftPoolNodes = poolNodes.value.filter(node => node.poolIndex === 0 && !node.isJumping);
|
||||||
|
|
||||||
|
if (leftPoolNodes.length > 0) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * leftPoolNodes.length);
|
||||||
|
const nodeToJump = leftPoolNodes[randomIndex];
|
||||||
|
const poolNodeIndex = poolNodes.value.indexOf(nodeToJump);
|
||||||
|
|
||||||
|
jumpNodeToCurve(poolNodeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
jumpTimer = setTimeout(jumpRandomNode, Math.random() * (maxInterval - minInterval) + minInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
jumpTimer = setTimeout(jumpRandomNode, Math.random() * (maxInterval - minInterval) + minInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 节点跳入曲线动画
|
||||||
|
const jumpNodeToCurve = poolNodeIndex => {
|
||||||
|
const poolNode = poolNodes.value[poolNodeIndex];
|
||||||
|
if (poolNode.isJumping) return;
|
||||||
|
|
||||||
|
const targetNodeIndex = Math.floor(Math.random() * nodes.value.length);
|
||||||
|
const targetNode = nodes.value[targetNodeIndex];
|
||||||
|
|
||||||
|
const startPos = getPoolNodePosition(poolNode.poolIndex, poolNodeIndex);
|
||||||
|
startPos.y += getBounceOffset(poolNodeIndex);
|
||||||
|
|
||||||
|
poolNode.isJumping = true;
|
||||||
|
poolNode.jumpPosition = { ...startPos };
|
||||||
|
poolNode.jumpProgress = 0;
|
||||||
|
poolNode.targetNodeIndex = targetNodeIndex;
|
||||||
|
|
||||||
|
const startTime = Date.now();
|
||||||
|
const duration = 1500;
|
||||||
|
|
||||||
|
const animateJump = () => {
|
||||||
|
if (!poolNode.isJumping) return;
|
||||||
|
|
||||||
|
const elapsed = Date.now() - startTime;
|
||||||
|
const progress = Math.min(elapsed / duration, 1);
|
||||||
|
|
||||||
|
const easeOutCubic = t => 1 - Math.pow(1 - t, 3);
|
||||||
|
const easedProgress = easeOutCubic(progress);
|
||||||
|
|
||||||
|
poolNode.jumpPosition.x = startPos.x + (targetNode.x - startPos.x) * easedProgress;
|
||||||
|
poolNode.jumpPosition.y = startPos.y + (targetNode.y - startPos.y) * easedProgress;
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
requestAnimationFrame(animateJump);
|
||||||
|
} else {
|
||||||
|
completeNodeJump(poolNodeIndex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
requestAnimationFrame(animateJump);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 完成节点跳跃并添加到曲线上
|
||||||
|
const completeNodeJump = poolNodeIndex => {
|
||||||
|
const poolNode = poolNodes.value[poolNodeIndex];
|
||||||
|
|
||||||
|
const newNode = { ...poolNode.jumpPosition };
|
||||||
|
nodes.value.push(newNode);
|
||||||
|
const newNodeIndex = nodes.value.length - 1;
|
||||||
|
|
||||||
|
const targetNodeIndex = poolNode.targetNodeIndex;
|
||||||
|
const segmentIndex = findSegmentContainingNode(targetNodeIndex);
|
||||||
|
|
||||||
|
if (segmentIndex !== -1) {
|
||||||
|
const segment = curveSegments.value[segmentIndex];
|
||||||
|
if (segment.startNode === targetNodeIndex) {
|
||||||
|
curveSegments.value.push({
|
||||||
|
startNode: newNodeIndex,
|
||||||
|
endNode: segment.endNode,
|
||||||
|
color: segment.color
|
||||||
|
});
|
||||||
|
segment.endNode = newNodeIndex;
|
||||||
|
} else if (segment.endNode === targetNodeIndex) {
|
||||||
|
curveSegments.value.push({
|
||||||
|
startNode: segment.startNode,
|
||||||
|
endNode: newNodeIndex,
|
||||||
|
color: segment.color
|
||||||
|
});
|
||||||
|
segment.startNode = newNodeIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
poolNodes.value.splice(poolNodeIndex, 1);
|
||||||
|
bounceOffsets.value = poolNodes.value.map(() => 0);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
addNewPoolNode();
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加新的需求池节点
|
||||||
|
const addNewPoolNode = () => {
|
||||||
|
const colors = [
|
||||||
|
{ fill: "#3498db", stroke: "#2980b9" },
|
||||||
|
{ fill: "#e74c3c", stroke: "#c0392b" },
|
||||||
|
{ fill: "#9b59b6", stroke: "#8e44ad" },
|
||||||
|
{ fill: "#e67e22", stroke: "#d35400" },
|
||||||
|
{ fill: "#f1c40f", stroke: "#f39c12" },
|
||||||
|
{ fill: "#1abc9c", stroke: "#16a085" },
|
||||||
|
{ fill: "#95a5a6", stroke: "#7f8c8d" },
|
||||||
|
{ fill: "#3498db", stroke: "#2980b9" },
|
||||||
|
{ fill: "#e74c3c", stroke: "#c0392b" },
|
||||||
|
{ fill: "#9b59b6", stroke: "#8e44ad" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const randomColor = colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
|
||||||
|
poolNodes.value.push({
|
||||||
|
poolIndex: 0,
|
||||||
|
...randomColor,
|
||||||
|
isHovered: false,
|
||||||
|
isJumping: false
|
||||||
|
});
|
||||||
|
|
||||||
|
updateConstrainedPositions(); // 新增节点后更新约束位置
|
||||||
|
};
|
||||||
|
|
||||||
|
// 找到包含指定节点的线段
|
||||||
|
const findSegmentContainingNode = nodeIndex => {
|
||||||
|
return curveSegments.value.findIndex(segment => segment.startNode === nodeIndex || segment.endNode === nodeIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新所有需求池节点的约束位置
|
||||||
|
const updateConstrainedPositions = () => {
|
||||||
|
constrainedPoolNodePosition.value = poolNodes.value.map((node, index) => getPoolNodePosition(node.poolIndex, index));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 动画函数
|
||||||
|
const animate = time => {
|
||||||
|
if (!lastTime) lastTime = time;
|
||||||
|
|
||||||
|
poolNodes.value.forEach((_, index) => {
|
||||||
|
if (!poolNodes.value[index].isJumping) {
|
||||||
|
const frequency = 0.0025 * (index + 1);
|
||||||
|
const amplitude = 2.5;
|
||||||
|
bounceOffsets.value[index] = amplitude * Math.sin(time * frequency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
lastTime = time;
|
||||||
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生命周期钩子
|
||||||
|
onMounted(() => {
|
||||||
|
updateConstrainedPositions(); // 初始化约束位置
|
||||||
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
|
startAutoJumping();
|
||||||
|
|
||||||
|
const handleResize = () => {
|
||||||
|
isMobileView.value = window.innerWidth < 768;
|
||||||
|
updateConstrainedPositions(); // 窗口大小变化时更新约束位置
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
handleResize();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (animationFrameId) {
|
||||||
|
cancelAnimationFrame(animationFrameId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jumpTimer) {
|
||||||
|
clearTimeout(jumpTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.removeEventListener("resize", handleResize);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.curve-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -27,11 +27,8 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- successSync -->
|
<!-- successSync -->
|
||||||
<template #successSync="scope">
|
<template #successSync="scope">
|
||||||
<a
|
<a @click="handleSuccessSync(scope.row)" :class="scope.row.successSync == '失败' ? 'break-word to-detail1' : ''">
|
||||||
@click="handleSuccessSync(scope.row)"
|
{{ scope.row.successSync }}
|
||||||
:class="scope.row.successSync == '失败' && scope.row.type == '采购入库' ? 'break-word to-detail1' : ''"
|
|
||||||
>
|
|
||||||
{{ scope.row.type == "采购入库" ? scope.row.successSync : "--" }}
|
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
</ProTable>
|
</ProTable>
|
||||||
|
|||||||
Reference in New Issue
Block a user