1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300 |
- <template>
- <div class="maturity-game-page">
- <head-component :headinfo="headinfo"></head-component>
- <div class="page-content">
- <div class="pyramid-container">
- <div class="lbline l1"></div>
- <div class="lbline l2"></div>
- <div class="lbline l3"></div>
- <div class="lbline l4"></div>
- <div class="lbline l5"></div>
- <div class="lbline l6"></div>
- <div class="lbline l7"></div>
- <div class="lbline l8"></div>
- <div class="lbline l9"></div>
- <div class="lbline l10"></div>
- <div class="lbline l11"></div>
- <div class="lbline l12"></div>
- <div class="lbline l13"></div>
- <div class="lbline l14"></div>
- <!-- 长期目标层 -->
- <div class="pyramid-layer layer-1">
- <div
- class="goal-box"
- :class="longTermGoal ? 'filled' : 'empty'"
- @dragover.prevent
- @drop="onDrop('longTerm', $event)"
- :data-drop-target="'longTerm'"
- >
- {{ longTermGoal || "长期目标" }}
- </div>
- </div>
- <!-- 中期目标层 -->
- <div class="pyramid-layer layer-2">
- <div
- v-for="(goal, index) in midTermGoals"
- :key="'mid-' + index"
- class="goal-box"
- :class="goal ? 'filled' : 'empty'"
- @dragover.prevent
- @drop="onDrop('midTerm' + (index + 1), $event)"
- :data-drop-target="'midTerm' + (index + 1)"
- >
- {{ goal || `中期目标${index + 1}` }}
- </div>
- </div>
- <!-- 短期目标层 -->
- <div class="pyramid-layer layer-3">
- <div
- v-for="(goal, index) in shortTermGoals"
- :key="'short-' + index"
- class="goal-box"
- :class="goal ? 'filled' : 'empty'"
- @dragover.prevent
- @drop="onDrop('shortTerm' + (index + 1), $event)"
- :data-drop-target="'shortTerm' + (index + 1)"
- >
- {{ goal || `短期目标${index + 1}` }}
- </div>
- </div>
- <!-- 小目标层 -->
- <div class="pyramid-layer layer-4">
- <div
- v-for="(goal, index) in smallGoals"
- :key="'small-' + index"
- class="goal-box"
- :class="goal ? 'filled' : 'empty'"
- @dragover.prevent
- @drop="onDrop('small' + (index + 1), $event)"
- :data-drop-target="'small' + (index + 1)"
- >
- {{ goal || `小目标${index + 1}` }}
- </div>
- </div>
- </div>
- <div
- style="
- width: 100%;
- height: 8px;
- background: rgb(173 173 173);
- border-top: 3px solid #fff;
- "
- ></div>
- <div class="action-row">
- <el-button type="success">返回上一级</el-button>
- <p class="instruction">
- 请选择一项你想实现的长期目标,并将其拖拽到目标金字塔中相应位置。
- </p>
- </div>
- <div class="goal-options">
- <div
- v-for="(option, index) in goalOptions"
- :key="'goal-option-' + index"
- class="goal-btn"
- :class="{
- dragging: isDragging && currentDragItem === option.content,
- }"
- draggable="true"
- @dragstart="onDragStart($event, option.content)"
- @touchstart="onTouchStart($event, option.content, option)"
- @touchmove="onTouchMove($event)"
- @touchend="onTouchEnd($event, option)"
- >
- {{ option.content }}
- </div>
- </div>
- <!-- 拖拽时的虚拟图 -->
- <div
- v-if="isDragging"
- class="drag-ghost"
- :style="ghostStyle"
- ref="dragGhost"
- >
- {{ currentDragItem }}
- </div>
- <drag_component></drag_component>
- <el-dialog
- v-model="centerDialogVisible2"
- title=""
- width="30%"
- align-center
- style="padding: 40px; top: 300px; border-radius: 20px"
- class="custom-dialog"
- :close-on-click-modal="false"
- :close-on-press-escape="false"
- :show-close="false"
- >
- <span
- >请根据SMART原则将各级有效目标依次拖拽到目标金字塔中的相应位置。</span
- >
- <template #footer>
- <span class="dialog-footer" style="">
- <el-button
- style="margin-right: 0px !important"
- class="button1"
- @click="handleTypeError"
- >
- 我知道了
- </el-button>
- </span>
- </template> </el-dialog
- ><el-dialog
- v-model="centerDialogVisible1"
- title=""
- width="30%"
- align-center
- style="padding: 40px; top: 300px; border-radius: 20px"
- class="custom-dialog"
- :close-on-click-modal="false"
- :close-on-press-escape="false"
- :show-close="false"
- >
- <span
- >与上级目标无关,不符合“SMART原则”(一致性R)。请重新进行选择与拖拽!</span
- >
- <template #footer>
- <span class="dialog-footer" style="">
- <el-button
- style="margin-right: 0px !important"
- class="button1"
- @click="handleTypeError"
- >
- 我知道了
- </el-button>
- </span>
- </template> </el-dialog
- ><el-dialog
- v-model="centerDialogVisible"
- title=""
- width="30%"
- align-center
- style="padding: 40px; top: 300px; border-radius: 20px"
- class="custom-dialog"
- :close-on-click-modal="false"
- :close-on-press-escape="false"
- :show-close="false"
- >
- <span>当前活动尚未完成,退出后进度将不会保存,是否确认退出?</span>
- <template #footer>
- <span class="dialog-footer" style="">
- <el-button @click="handleContinue">继续体验</el-button>
- <el-button type="primary" @click="handleConfirmExit">
- 确定退出
- </el-button>
- </span>
- </template> </el-dialog
- ><el-dialog
- v-model="centerDialogVisible3"
- title=""
- width="30%"
- align-center
- style="padding: 40px; top: 300px; border-radius: 20px"
- class="custom-dialog"
- :close-on-click-modal="false"
- :close-on-press-escape="false"
- :show-close="false"
- >
- <span>恭喜您完成本次探索,收获满满!</span>
- <template #footer>
- <span class="dialog-footer" style="">
- <el-button
- style="margin-right: 0px !important"
- class="button1"
- @click="handleKnowExit"
- >
- 我知道了
- </el-button>
- </span>
- </template>
- </el-dialog>
- <el-dialog
- v-model="centerDialogVisible4"
- title=""
- width="30%"
- align-center
- style="padding: 40px; top: 300px; border-radius: 20px"
- class="custom-dialog"
- :close-on-click-modal="false"
- :close-on-press-escape="false"
- :show-close="false"
- >
- <span>答案错误</span>
- <template #footer>
- <span class="dialog-footer" style="">
- <el-button
- style="margin-right: 0px !important"
- class="button1"
- @click="handleKnowExit"
- >
- 我知道了
- </el-button>
- </span>
- </template>
- </el-dialog>
- </div>
- </div>
- </template>
- <script setup>
- import headComponent from "@/views/xjc-integratedmachine/components/head_component.vue";
- import drag_component from "@/views/xjc-integratedmachine/components/drag_component.vue";
- import { onMounted, watch, nextTick } from "vue";
- import { getAimManagementMaterial } from "@/api/xjc-integratedmachine/plan/aim.js";
- const router = useRouter();
- const headinfo = ref({});
- const draggedItem = ref(null);
- const centerDialogVisible = ref(false);
- const centerDialogVisible1 = ref(false);
- const centerDialogVisible2 = ref(false);
- const centerDialogVisible3 = ref(false);
- const centerDialogVisible4 = ref(false);
- // 移动端拖拽相关状态
- const isDragging = ref(false);
- const handleKnowExit = () => {
- centerDialogVisible.value = false;
- centerDialogVisible1.value = false;
- centerDialogVisible2.value = false;
- centerDialogVisible3.value = false;
- centerDialogVisible4.value = false;
- };
- const currentDragItem = ref(null);
- const dragStartPos = ref({ x: 0, y: 0 });
- const currentDropTarget = ref(null);
- const dragGhost = ref(null);
- const ghostStyle = ref({
- position: "fixed",
- left: "0px",
- top: "0px",
- transform: "translate(-50%, -50%)",
- zIndex: 9999,
- pointerEvents: "none",
- });
- function dragStart(event) {
- draggedItem.value = event.target;
- }
- function drop(event) {
- event.preventDefault(); // 防止默认处理(例如打开链接)
- if (event.target !== draggedItem.value) {
- const target = event.target; // 获取放置的目标元素
- const item = draggedItem.value; // 获取被拖拽的元素
- // 交换位置或进行其他操作
- item.parentNode.insertBefore(item, target); // 将被拖拽的元素插入到目标元素之前
- }
- }
- // 用于控制页面离开的变量
- let pendingRouteCallback = null;
- function setHeadinfo() {
- headinfo.value = {
- title: "目标管理",
- user: {
- avatar: "头像路径",
- nickName: "张三",
- },
- backUrl: "/xjc-integratedmachine/plan/aim/aim_management_index",
- backUrlUse: true,
- };
- }
- // 继续体验按钮处理
- const handleContinue = () => {
- centerDialogVisible.value = false;
- if (pendingRouteCallback) {
- pendingRouteCallback(false); // 阻止离开
- pendingRouteCallback = null;
- }
- }; // 确定退出按钮处理
- const handleConfirmExit = () => {
- centerDialogVisible.value = false;
- if (pendingRouteCallback) {
- pendingRouteCallback(true); // 允许离开
- pendingRouteCallback = null;
- }
- };
- const totalData = ref([]);
- onMounted(() => {
- setHeadinfo();
- getAimManagementMaterial({ pageNum: 100 })
- .then((res) => {
- console.log(res, 22222);
- totalData.value = res;
- goalOptions.value = totalData.value.children;
- })
- .catch((err) => {
- console.log("error", err);
- //loading.value = false; // 即使出错也要停止加载状态
- });
- });
- onBeforeRouteLeave((to, from, next) => {
- // 保存next回调函数
- pendingRouteCallback = next;
- if (goalOptions.value.length == 15) {
- // 任务完成,显示完成对话框
- //centerDialogVisible3.value = true;
- } else {
- // 任务未完成,显示确认对话框
- centerDialogVisible.value = true;
- }
- // 不直接调用next(),等待用户在对话框中选择
- });
- onBeforeUnmount(() => {
- console.log("组件即将卸载,可以清理定时器/事件监听等");
- });
- function jumpTo(path) {
- router.push({
- path: path,
- query: { name: 123 },
- });
- }
- // lb
- const longTermGoal = ref("");
- const midTermGoals = ref(["", ""]);
- const shortTermGoals = ref(["", "", "", ""]);
- const smallGoals = ref(["", "", "", "", "", "", "", ""]);
- // 目标选项数据
- const goalOptions = ref([]);
- watch(
- smallGoals,
- (newVal) => {
- // 判断数组所有项是否都不是空字符串
- if (newVal.every((item) => item.trim() !== "")) {
- centerDialogVisible3.value = true; // 满足条件时显示对话框
- }
- },
- { deep: true } // 监听数组内部元素变化
- );
- // 桌面端拖拽
- const onDragStart = (event, goalText) => {
- event.dataTransfer.setData("text/plain", goalText);
- };
- // 移动端触摸拖拽
- const onTouchStart = (event, goalText, obj) => {
- console.log(event, obj, 33);
- event.preventDefault();
- isDragging.value = true;
- currentDragItem.value = goalText;
- const touch = event.touches[0];
- dragStartPos.value = {
- x: touch.clientX,
- y: touch.clientY,
- };
- // 初始化虚拟图位置
- ghostStyle.value.left = touch.clientX + "px";
- ghostStyle.value.top = touch.clientY + "px";
- // 添加视觉反馈
- event.currentTarget.style.opacity = "0.5";
- event.currentTarget.style.transform = "scale(0.95)";
- };
- const onTouchMove = (event) => {
- if (!isDragging.value) return;
- event.preventDefault();
- const touch = event.touches[0];
- // 更新虚拟图位置
- ghostStyle.value.left = touch.clientX + "px";
- ghostStyle.value.top = touch.clientY + "px";
- // 获取触摸点下的元素
- const elementBelow = document.elementFromPoint(touch.clientX, touch.clientY);
- // 清除之前的高亮
- document.querySelectorAll(".goal-box.drop-highlight").forEach((el) => {
- el.classList.remove("drop-highlight");
- });
- // 检查是否是有效的放置目标
- if (
- elementBelow &&
- elementBelow.classList.contains("goal-box") &&
- elementBelow.dataset.dropTarget
- ) {
- elementBelow.classList.add("drop-highlight");
- currentDropTarget.value = elementBelow.dataset.dropTarget;
- } else {
- currentDropTarget.value = null;
- }
- };
- const onTouchEnd = (event, obj) => {
- console.log(obj.isRight, event, 22);
- if (!isDragging.value) return;
- event.preventDefault();
- // 恢复原始样式
- event.currentTarget.style.opacity = "";
- event.currentTarget.style.transform = "";
- // 清除高亮
- document.querySelectorAll(".goal-box.drop-highlight").forEach((el) => {
- el.classList.remove("drop-highlight");
- });
- // 如果有有效的放置目标,执行放置逻辑
- if (currentDropTarget.value && currentDragItem.value) {
- handleDrop(currentDropTarget.value, currentDragItem.value, obj);
- }
- // 重置状态
- isDragging.value = false;
- currentDragItem.value = null;
- currentDropTarget.value = null;
- };
- // 兼容数组 / 单对象 / ref / reactive 的查找
- function findNodeById(tree, id) {
- // 取出真实根值(兼容 Vue 的 ref/reactive)
- const root =
- tree && typeof tree === "object" && "value" in tree ? tree.value : tree;
- if (!root) return null;
- // 统一成待遍历栈
- const stack = Array.isArray(root) ? root.slice() : [root];
- const targetId = String(id);
- while (stack.length) {
- const node = stack.pop();
- if (node && String(node.id) === targetId) return node;
- const children = node && Array.isArray(node.children) ? node.children : [];
- // 压入子节点
- for (let i = 0; i < children.length; i++) {
- stack.push(children[i]);
- }
- }
- return null;
- }
- function checkArray(arr) {
- console.log(arr, 133);
- // 判断是否有空项(只包含空字符串或全是空格的也算空)
- return !arr.some((item) => item.trim() === "");
- }
- function steam(target, goalText) {
- switch (target) {
- case "longTerm":
- longTermGoal.value = goalText;
- document.querySelector(".l1").style.border = "2px solid #00754d";
- document.querySelector(".l2").style.border = "2px solid #00754d";
- break;
- case "midTerm1":
- midTermGoals.value[0] = goalText;
- document.querySelector(".l3").style.border = "2px solid #00754d";
- document.querySelector(".l4").style.border = "2px solid #00754d";
- break;
- case "midTerm2":
- midTermGoals.value[1] = goalText;
- document.querySelector(".l5").style.border = "2px solid #00754d";
- document.querySelector(".l6").style.border = "2px solid #00754d";
- break;
- case "shortTerm1":
- shortTermGoals.value[0] = goalText;
- document.querySelector(".l7").style.border = "2px solid #00754d";
- document.querySelector(".l8").style.border = "2px solid #00754d";
- break;
- case "shortTerm2":
- shortTermGoals.value[1] = goalText;
- document.querySelector(".l9").style.border = "2px solid #00754d";
- document.querySelector(".l10").style.border = "2px solid #00754d";
- break;
- case "shortTerm3":
- shortTermGoals.value[2] = goalText;
- document.querySelector(".l11").style.border = "2px solid #00754d";
- document.querySelector(".l12").style.border = "2px solid #00754d";
- break;
- case "shortTerm4":
- shortTermGoals.value[3] = goalText;
- document.querySelector(".l13").style.border = "2px solid #00754d";
- document.querySelector(".l14").style.border = "2px solid #00754d";
- break;
- default:
- if (target.startsWith("small")) {
- const index = parseInt(target.replace("small", "")) - 1;
- smallGoals.value[index] = goalText;
- }
- }
- // 输出当前状态
- console.log("长期目标:", longTermGoal.value);
- console.log("中期目标:", midTermGoals.value);
- console.log("短期目标:", shortTermGoals.value);
- console.log("小目标:", smallGoals.value);
- }
- let newArrtotalDeep2 = [];
- let newArrtotalDeep3 = [];
- let newArrtotalDeep4 = [];
- const handleDrop = (target, goalText, obj) => {
- console.log(target, goalText, obj, "mobile drop");
- if (!obj.isRight) {
- centerDialogVisible4.value = true;
- return;
- }
- if (obj.deep == 1) {
- if (target != "longTerm") {
- centerDialogVisible2.value = true;
- return;
- }
- let newArr = findNodeById(totalData.value, obj.id);
- console.log(newArr, 88);
- goalOptions.value = newArr.children;
- steam(target, goalText);
- }
- if (obj.deep == 2) {
- //判断是否拖进中期目标
- if (!(target == "midTerm1" || target == "midTerm2")) {
- centerDialogVisible2.value = true;
- return;
- }
- console.log(!checkArray(midTermGoals.value), midTermGoals, 777);
- steam(target, goalText);
- //判断中期目标是否都已拖拽完毕
- if (!checkArray(midTermGoals.value)) {
- let newArr = findNodeById(totalData.value, obj.id);
- newArrtotalDeep2.push(...newArr.children);
- } else {
- let newArr = findNodeById(totalData.value, obj.id);
- newArrtotalDeep2.push(...newArr.children);
- console.log(newArrtotalDeep2, 88);
- goalOptions.value = newArrtotalDeep2;
- }
- }
- if (obj.deep == 3) {
- //判断是否拖进中期目标
- if (
- !(
- target == "shortTerm1" ||
- target == "shortTerm2" ||
- target == "shortTerm3" ||
- target == "shortTerm4"
- )
- ) {
- centerDialogVisible2.value = true;
- return;
- }
- console.log(!checkArray(shortTermGoals.value), shortTermGoals, 777);
- steam(target, goalText);
- //判断短期目标是否都已拖拽完毕
- if (!checkArray(shortTermGoals.value)) {
- let newArr = findNodeById(totalData.value, obj.id);
- newArrtotalDeep3.push(...newArr.children);
- } else {
- let newArr = findNodeById(totalData.value, obj.id);
- newArrtotalDeep3.push(...newArr.children);
- console.log(newArrtotalDeep3, 88);
- goalOptions.value = newArrtotalDeep3;
- }
- }
- if (obj.deep == 4) {
- //判断是否拖进中期目标
- if (
- !(
- target == "small1" ||
- target == "small2" ||
- target == "small3" ||
- target == "small4" ||
- target == "small5" ||
- target == "small6" ||
- target == "small7" ||
- target == "small8"
- )
- ) {
- centerDialogVisible2.value = true;
- return;
- }
- console.log(!checkArray(smallGoals.value), smallGoals, 777);
- steam(target, goalText);
- //判断短期目标是否都已拖拽完毕
- if (!checkArray(smallGoals.value)) {
- } else {
- let newArr = findNodeById(totalData.value, obj.id);
- console.log(newArr, 88);
- goalOptions.value = newArr.children;
- }
- }
- console.log(smallGoals, 7776);
- };
- const onDrop = (target, event) => {
- console.log(target, event, 999);
- const goalText = event.dataTransfer.getData("text/plain");
- handleDrop(target, goalText);
- }; // 我知道了按钮处理
- const handleTypeError = () => {
- centerDialogVisible2.value = false;
- };
- // ed
- </script>
- <style scoped lang="scss">
- /* lb */
- ::v-deep .dialog-footer button:first-child {
- margin-right: 102px !important;
- }
- ::v-deep .el-dialog__footer {
- text-align: center;
- }
- /* 穿透修改对话框内容 */
- ::v-deep .custom-dialog .el-dialog__body span {
- margin-bottom: 40px;
- font-size: 28px;
- display: block;
- text-align: left;
- }
- /* 修改按钮样式 */
- ::v-deep .dialog-footer .el-button {
- font-size: 18px;
- padding: 12px 24px;
- }
- ::v-deep .dialog-footer .el-button--primary {
- background-color: #ff0000 !important;
- border-color: #ff0000 !important;
- height: 48px;
- border-radius: 12px;
- }
- ::v-deep .dialog-footer .el-button:not(.el-button--primary) {
- background-color: #2ac17a !important;
- color: #fff !important;
- height: 48px;
- border-radius: 12px;
- }
- .action-row {
- display: flex;
- align-items: center; // 垂直居中
- justify-content: flex-start; // 左对齐,可改为 space-between
- gap: 20px; // 元素之间的间距
- margin: 30px 0;
- .el-button {
- margin-left: 32px;
- padding: 25px 25px;
- border-radius: 12px;
- font-size: 20px; // 按钮字体变大
- }
- .instruction {
- margin: 0; // 去掉段落默认 margin
- font-weight: bold;
- margin-left: 410px;
- font-size: 22px;
- color: #00a524;
- }
- }
- .layer-1 .goal-box.empty {
- border: 4px dashed #e8d344;
- }
- .layer-1 .goal-box.filled {
- border: 4px solid #e8d344;
- }
- .layer-2 .goal-box.empty {
- border: 4px dashed #00754d;
- }
- .layer-2 .goal-box.filled {
- border: 4px solid #e8d344;
- }
- .layer-3 .goal-box.empty {
- border: 4px dashed #00754d;
- }
- .layer-3 .goal-box.filled {
- border: 4px solid #e8d344;
- }
- .layer-4 .goal-box.empty {
- border: 4px dashed #00754d;
- }
- .layer-4 .goal-box.filled {
- border: 4px solid #3ff032;
- }
- // 移动端拖拽相关样式
- .goal-box.drop-highlight {
- background: rgba(255, 255, 0, 0.3) !important;
- border-color: #ffeb3b !important;
- transform: scale(1.02);
- }
- .goal-btn.dragging {
- opacity: 0.7;
- transform: scale(1.05);
- }
- // 增强移动端触摸体验
- .goal-btn {
- touch-action: none; // 防止页面滚动
- user-select: none; // 防止文本选择
- }
- .goal-box {
- touch-action: none;
- user-select: none;
- }
- /* 拖拽虚拟图样式 */
- .drag-ghost {
- padding: 15px 20px;
- border: 3px solid #4caf50;
- border-radius: 25px;
- background: rgba(76, 175, 80, 0.9);
- color: white;
- font-size: 18px;
- font-weight: bold;
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
- backdrop-filter: blur(10px);
- animation: ghostPulse 1s ease-in-out infinite alternate;
- white-space: nowrap;
- max-width: 250px;
- text-align: center;
- }
- @keyframes ghostPulse {
- 0% {
- transform: translate(-50%, -50%) scale(0.95);
- box-shadow: 0 8px 25px rgba(76, 175, 80, 0.4);
- }
- 100% {
- transform: translate(-50%, -50%) scale(1.05);
- box-shadow: 0 12px 35px rgba(76, 175, 80, 0.6);
- }
- }
- .lbline {
- width: 130px;
- border-top: 4px dashed #00754d;
- /* transition: revert; */
- position: absolute;
- }
- .l1 {
- transform: rotate(-16deg);
- top: 103px;
- left: 720px;
- }
- .l2 {
- transform: rotate(16deg);
- top: 103px;
- left: 1050px;
- }
- .l3 {
- transform: rotate(-16deg);
- top: 275px;
- left: 239px;
- }
- .l4 {
- transform: rotate(16deg);
- top: 273px;
- left: 640px;
- }
- .l5 {
- transform: rotate(-16deg);
- top: 273px;
- left: 1140px;
- }
- .l6 {
- transform: rotate(16deg);
- top: 273px;
- left: 1490px;
- }
- .l7 {
- transform: rotate(-16deg);
- top: 445px;
- left: 101px;
- }
- .l8 {
- transform: rotate(16deg);
- top: 444px;
- left: 268px;
- }
- .l9 {
- transform: rotate(-16deg);
- top: 444px;
- left: 568px;
- }
- .l10 {
- transform: rotate(16deg);
- top: 444px;
- left: 737px;
- }
- .l11 {
- transform: rotate(-16deg);
- top: 444px;
- left: 1041px;
- }
- .l12 {
- transform: rotate(16deg);
- top: 444px;
- left: 1208px;
- }
- .l13 {
- transform: rotate(-16deg);
- top: 444px;
- left: 1496px;
- }
- .l14 {
- transform: rotate(16deg);
- top: 444px;
- left: 1673px;
- }
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- font-family: "Arial", "Microsoft YaHei", sans-serif;
- }
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- font-family: "Arial", "Microsoft YaHei", sans-serif;
- }
- body {
- background: linear-gradient(135deg, #e0f7fa 0%, #bbdefb 100%);
- min-height: 100vh;
- display: flex;
- justify-content: center;
- align-items: center;
- padding: 20px;
- margin: 0; // 确保没有默认边距
- }
- // 全局背景覆盖
- html,
- body {
- margin: 0;
- padding: 0;
- background: linear-gradient(to right, #bef3fc, #3dd3f5);
- min-height: 100vh;
- }
- .container {
- width: 100%;
- max-width: 900px;
- background: white;
- border-radius: 20px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
- padding: 30px;
- position: relative;
- overflow: hidden;
- }
- .title {
- text-align: center;
- color: #2c3e50;
- margin-bottom: 30px;
- font-size: 24px;
- font-weight: bold;
- }
- /* 金字塔容器 */
- .pyramid-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-bottom: 0px;
- position: relative;
- }
- /* 金字塔层 */
- .pyramid-layer {
- display: flex;
- justify-content: center;
- margin-bottom: 32px;
- position: relative;
- }
- /* 长期目标层 - 页面宽度的三分之一 */
- .layer-1 .goal-box.empty {
- height: 88px;
- }
- .layer-1 .goal-box.filled {
- height: 88px;
- }
- .layer-1 {
- width: 33.33%;
- }
- /* 中期目标层 - 页面宽度的二分之一 */
- .layer-2 {
- width: 85%;
- justify-content: space-between;
- }
- /* 短期目标层 - 页面宽度的四分之三 */
- .layer-3 {
- width: 98%;
- justify-content: space-between;
- }
- /* 小目标层 - 页面宽度的90% */
- .layer-4 {
- width: 98%;
- justify-content: space-between;
- }
- /* 目标框样式 */
- .goal-box {
- border-radius: 33px;
- background: rgba(255, 255, 255, 0.9);
- text-align: center;
- cursor: pointer;
- transition: all 0.3s;
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22px;
- color: #555;
- height: 140px;
- z-index: 2;
- }
- .goal-box:hover {
- background: rgba(236, 252, 237, 0.9);
- transform: translateY(-2px);
- }
- .goal-box.empty {
- margin: 0px 9px;
- color: #95a5a6;
- border-style: dashed;
- width: 100%;
- }
- .layer-2 .goal-box.filled {
- margin: 0px 9px;
- width: 100%;
- color: #fff;
- border-style: solid;
- background: #00d89c;
- font-weight: bold;
- }
- .layer-3 .goal-box.filled {
- margin: 0px 9px;
- width: 100%;
- color: #fff;
- border-style: solid;
- background: #00d89c;
- font-weight: bold;
- }
- .layer-4 .goal-box.filled {
- margin: 0px 9px;
- width: 100%;
- color: #fff;
- border-style: solid;
- background: #00d89c;
- font-weight: bold;
- }
- .goal-box.filled {
- margin: 0px 9px;
- width: 100%;
- color: #fff;
- border-style: solid;
- background: #303030;
- font-weight: bold;
- }
- /* 连接线容器 */
- .connections {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 1;
- pointer-events: none;
- }
- /* 连接线 */
- .connection {
- position: absolute;
- background: transparent;
- border-left: 4px dashed #2c8f30;
- }
- /* 长期目标到中期目标的连线 */
- .long-to-mid-1,
- .long-to-mid-2 {
- height: 60px;
- top: 60px;
- left: 50%;
- }
- .long-to-mid-1 {
- transform: translateX(-100%) rotate(-30deg);
- transform-origin: top center;
- }
- .long-to-mid-2 {
- transform: translateX(0) rotate(30deg);
- transform-origin: top center;
- }
- /* 中期目标到短期目标的连线 */
- .mid-to-short-1,
- .mid-to-short-2,
- .mid-to-short-3,
- .mid-to-short-4 {
- height: 60px;
- top: 180px;
- }
- .mid-to-short-1 {
- left: 25%;
- transform: translateX(-150%) rotate(-20deg);
- transform-origin: top center;
- }
- .mid-to-short-2 {
- left: 25%;
- transform: translateX(-50%) rotate(20deg);
- transform-origin: top center;
- }
- .mid-to-short-3 {
- left: 75%;
- transform: translateX(-150%) rotate(-20deg);
- transform-origin: top center;
- }
- .mid-to-short-4 {
- left: 75%;
- transform: translateX(-50%) rotate(20deg);
- transform-origin: top center;
- }
- /* 短期目标到小目标的连线 */
- .short-to-small-1,
- .short-to-small-2,
- .short-to-small-3,
- .short-to-small-4,
- .short-to-small-5,
- .short-to-small-6,
- .short-to-small-7,
- .short-to-small-8 {
- height: 60px;
- top: 300px;
- }
- .short-to-small-1 {
- left: 12.5%;
- transform: translateX(-175%) rotate(-15deg);
- transform-origin: top center;
- }
- .short-to-small-2 {
- left: 12.5%;
- transform: translateX(-75%) rotate(15deg);
- transform-origin: top center;
- }
- .short-to-small-3 {
- left: 37.5%;
- transform: translateX(-175%) rotate(-15deg);
- transform-origin: top center;
- }
- .short-to-small-4 {
- left: 37.5%;
- transform: translateX(-75%) rotate(15deg);
- transform-origin: top center;
- }
- .short-to-small-5 {
- left: 62.5%;
- transform: translateX(-175%) rotate(-15deg);
- transform-origin: top center;
- }
- .short-to-small-6 {
- left: 62.5%;
- transform: translateX(-75%) rotate(15deg);
- transform-origin: top center;
- }
- .short-to-small-7 {
- left: 87.5%;
- transform: translateX(-175%) rotate(-15deg);
- transform-origin: top center;
- }
- .short-to-small-8 {
- left: 87.5%;
- transform: translateX(-75%) rotate(15deg);
- transform-origin: top center;
- }
- /* 底部按钮区域 */
- .goal-options {
- display: flex;
- flex-wrap: wrap; // 允许换行
- justify-content: flex-start;
- height: 191px;
- overflow-y: auto;
- gap: 20px;
- margin-top: 40px;
- padding: 20px;
- // 可选:自适应居中
- justify-content: center;
- }
- .goal-btn {
- flex: 0 0 calc(33.33% - 13.33px); // 每行3个,减去gap平均值
- min-width: 200px; // 保持最小宽度
- padding: 10px 25px;
- border: 4px solid #95a5a6;
- border-radius: 40px;
- background: #fff;
- color: #95a5a6;
- font-size: 22px;
- height: 80px;
- text-align: center;
- cursor: grab;
- box-sizing: border-box;
- }
- .goal-btn:hover {
- border: 4px solid #a2d532;
- color: #2c3e50;
- transform: translateY(-3px);
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
- }
- .goal-btn:active {
- cursor: grabbing;
- }
- .instruction {
- text-align: center;
- margin: 30px 0;
- color: #00a524;
- font-weight: bold;
- font-size: 22px;
- }
- /* 装饰元素 */
- .decoration {
- position: absolute;
- }
- .cartoon {
- top: 20px;
- left: 20px;
- width: 80px;
- height: 80px;
- background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="40" r="30" fill="%23FFD54F"/><circle cx="40" cy="35" r="5" fill="white"/><circle cx="60" cy="35" r="5" fill="white"/><path d="M35 55 Q50 65 65 55" stroke="%23E57373" stroke-width="3" fill="none"/></svg>')
- no-repeat center;
- background-size: contain;
- }
- .robot {
- bottom: 20px;
- right: 20px;
- width: 80px;
- height: 126px;
- background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="30" r="20" fill="%234caf50"/><rect x="30" y="50" width="40" height="40" rx="5" fill="%234caf50"/><circle cx="40" cy="60" r="5" fill="white"/><circle cx="60" cy="60" r="5" fill="white"/></svg>')
- no-repeat center;
- background-size: contain;
- }
- @media (max-width: 768px) {
- .goal-options {
- padding: 15px;
- gap: 15px;
- // 移动端滚动条隐藏
- scrollbar-width: none; // Firefox
- -ms-overflow-style: none; // IE
- &::-webkit-scrollbar {
- display: none; // Chrome/Safari
- }
- }
- .goal-btn {
- width: 30%; // 移动端也保持原来的宽度
- min-width: 200px; // 保持原来的最小宽度
- padding: 40px 20px;
- font-size: 18px;
- }
- .layer-2,
- .layer-3,
- .layer-4 {
- flex-wrap: wrap;
- height: auto;
- justify-content: center;
- }
- .goal-box {
- flex: 1;
- min-width: 120px;
- }
- .connections {
- display: none;
- }
- }
- /*ed*/
- .maturity-game-page {
- // background: url("@/assets/images/wakeup/maturity/maturity-game-bg.png")
- // no-repeat;
- background: linear-gradient(to right, #bef3fc, #3dd3f5);
- background-attachment: fixed;
- z-index: 10;
- width: 100%;
- min-height: 100vh;
- position: relative;
- .page-content * {
- pointer-events: auto; /* 子元素还是能点 */
- }
- .page-content {
- width: 100%;
- position: relative;
- pointer-events: none;
- padding-top: 123px;
- min-height: calc(100vh - 123px);
- padding-bottom: 0px; // 添加底部内边距防止内容贴边
- .top {
- width: 100%;
- height: 212px;
- display: flex;
- justify-content: center;
- margin-top: 104px;
- img {
- width: 864px;
- height: 212px;
- }
- }
- .bottom {
- width: 100%;
- height: 248px;
- display: flex;
- justify-content: space-around;
- margin-top: 186px;
- img {
- width: 398px;
- height: 248px;
- }
- }
- }
- }
- </style>
|