feat: 迁移信号发生器前端至底栏
This commit is contained in:
		@@ -67,6 +67,17 @@
 | 
			
		||||
        <Hand class="icon" />
 | 
			
		||||
        嵌入式逻辑分析仪
 | 
			
		||||
      </label>
 | 
			
		||||
      <label class="tab">
 | 
			
		||||
        <input
 | 
			
		||||
          type="radio"
 | 
			
		||||
          name="function-bar"
 | 
			
		||||
          id="7"
 | 
			
		||||
          :checked="checkID === 7"
 | 
			
		||||
          @change="handleTabChange"
 | 
			
		||||
        />
 | 
			
		||||
        <Signature class="icon" />
 | 
			
		||||
        信号发生器
 | 
			
		||||
      </label>
 | 
			
		||||
      <!-- 全屏按钮 -->
 | 
			
		||||
      <button
 | 
			
		||||
        class="fullscreen-btn ml-auto btn btn-ghost btn-sm"
 | 
			
		||||
@@ -93,7 +104,10 @@
 | 
			
		||||
        <LogicAnalyzerView />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div v-else-if="checkID === 6" class="h-full overflow-y-auto">
 | 
			
		||||
        <Debugger />
 | 
			
		||||
        <DebuggerView />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div v-else-if="checkID === 7" class="h-full overflow-y-auto">
 | 
			
		||||
        <DDSCtrlView />
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -109,15 +123,17 @@ import {
 | 
			
		||||
  Binary,
 | 
			
		||||
  Hand,
 | 
			
		||||
  Monitor,
 | 
			
		||||
  Signature,
 | 
			
		||||
} from "lucide-vue-next";
 | 
			
		||||
import { useLocalStorage } from "@vueuse/core";
 | 
			
		||||
import VideoStreamView from "@/views/Project/VideoStream.vue";
 | 
			
		||||
import HdmiVideoStreamView from "@/views/Project/HdmiVideoStream.vue";
 | 
			
		||||
import OscilloscopeView from "@/views/Project/Oscilloscope.vue";
 | 
			
		||||
import LogicAnalyzerView from "@/views/Project/LogicAnalyzer.vue";
 | 
			
		||||
import DebuggerView from "./Debugger.vue";
 | 
			
		||||
import DDSCtrlView from "./DDSCtrl.vue";
 | 
			
		||||
import { isNull, toNumber } from "lodash";
 | 
			
		||||
import { onMounted, ref, watch } from "vue";
 | 
			
		||||
import Debugger from "./Debugger.vue";
 | 
			
		||||
import { useProvideLogicAnalyzer } from "@/components/LogicAnalyzer";
 | 
			
		||||
import { useProvideOscilloscope } from "@/components/Oscilloscope/OscilloscopeManager";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										884
									
								
								src/views/Project/DDSCtrl.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										884
									
								
								src/views/Project/DDSCtrl.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,884 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div
 | 
			
		||||
    class="dds-controller min-h-screen bg-gradient-to-br from-slate-50 to-blue-50 dark:from-slate-900 dark:to-blue-900 p-4"
 | 
			
		||||
  >
 | 
			
		||||
    <!-- 主要内容区域 -->
 | 
			
		||||
    <div class="grid grid-cols-1 xl:grid-cols-3 gap-6">
 | 
			
		||||
      <!-- 左侧: 波形显示和自定义波形区域 (xl屏幕时占2列) -->
 | 
			
		||||
      <div class="xl:col-span-2 space-y-6">
 | 
			
		||||
        <!-- 波形显示区 -->
 | 
			
		||||
        <div
 | 
			
		||||
          class="card bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-xl border border-slate-200/50 dark:border-slate-700/50 hover:shadow-2xl transition-all duration-300"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="card-body p-6">
 | 
			
		||||
            <div class="flex items-center gap-3 mb-4">
 | 
			
		||||
              <div
 | 
			
		||||
                class="p-2 rounded-lg bg-gradient-to-r from-green-400 to-green-600"
 | 
			
		||||
              >
 | 
			
		||||
                <Signature class="w-6 h-6 text-white" />
 | 
			
		||||
              </div>
 | 
			
		||||
              <h2 class="text-2xl font-bold text-slate-800 dark:text-slate-200">
 | 
			
		||||
                实时波形显示
 | 
			
		||||
              </h2>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <!-- 波形显示容器 -->
 | 
			
		||||
            <div
 | 
			
		||||
              ref="waveformContainer"
 | 
			
		||||
              class="relative bg-slate-900 rounded-xl p-4 border-2 border-slate-700 overflow-hidden group"
 | 
			
		||||
            >
 | 
			
		||||
              <div
 | 
			
		||||
                class="absolute inset-0 bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
 | 
			
		||||
              ></div>
 | 
			
		||||
              <svg
 | 
			
		||||
                :width="svgWidth"
 | 
			
		||||
                :height="svgHeight"
 | 
			
		||||
                :viewBox="`0 0 ${svgWidth} ${svgHeight}`"
 | 
			
		||||
                class="w-full h-auto transition-all duration-500 ease-in-out"
 | 
			
		||||
                style="min-height: 300px"
 | 
			
		||||
              >
 | 
			
		||||
                <!-- 背景网格 -->
 | 
			
		||||
                <defs>
 | 
			
		||||
                  <pattern
 | 
			
		||||
                    id="grid"
 | 
			
		||||
                    width="20"
 | 
			
		||||
                    height="20"
 | 
			
		||||
                    patternUnits="userSpaceOnUse"
 | 
			
		||||
                  >
 | 
			
		||||
                    <path
 | 
			
		||||
                      d="M 20 0 L 0 0 0 20"
 | 
			
		||||
                      fill="none"
 | 
			
		||||
                      stroke="#334155"
 | 
			
		||||
                      stroke-width="0.5"
 | 
			
		||||
                      opacity="0.3"
 | 
			
		||||
                    />
 | 
			
		||||
                  </pattern>
 | 
			
		||||
                </defs>
 | 
			
		||||
                <rect :width="svgWidth" :height="svgHeight" fill="url(#grid)" />
 | 
			
		||||
 | 
			
		||||
                <!-- 波形路径 -->
 | 
			
		||||
                <path
 | 
			
		||||
                  :d="currentWaveformPath"
 | 
			
		||||
                  stroke="url(#waveGradient)"
 | 
			
		||||
                  stroke-width="3"
 | 
			
		||||
                  fill="none"
 | 
			
		||||
                  class="animate-pulse"
 | 
			
		||||
                  filter="url(#glow)"
 | 
			
		||||
                />
 | 
			
		||||
 | 
			
		||||
                <!-- 渐变定义 -->
 | 
			
		||||
                <defs>
 | 
			
		||||
                  <linearGradient
 | 
			
		||||
                    id="waveGradient"
 | 
			
		||||
                    x1="0%"
 | 
			
		||||
                    y1="0%"
 | 
			
		||||
                    x2="100%"
 | 
			
		||||
                    y2="0%"
 | 
			
		||||
                  >
 | 
			
		||||
                    <stop
 | 
			
		||||
                      offset="0%"
 | 
			
		||||
                      style="stop-color: #10b981; stop-opacity: 1"
 | 
			
		||||
                    />
 | 
			
		||||
                    <stop
 | 
			
		||||
                      offset="50%"
 | 
			
		||||
                      style="stop-color: #06d6a0; stop-opacity: 1"
 | 
			
		||||
                    />
 | 
			
		||||
                    <stop
 | 
			
		||||
                      offset="100%"
 | 
			
		||||
                      style="stop-color: #0891b2; stop-opacity: 1"
 | 
			
		||||
                    />
 | 
			
		||||
                  </linearGradient>
 | 
			
		||||
                  <filter id="glow">
 | 
			
		||||
                    <feGaussianBlur stdDeviation="2" result="coloredBlur" />
 | 
			
		||||
                    <feMerge>
 | 
			
		||||
                      <feMergeNode in="coloredBlur" />
 | 
			
		||||
                      <feMergeNode in="SourceGraphic" />
 | 
			
		||||
                    </feMerge>
 | 
			
		||||
                  </filter>
 | 
			
		||||
                </defs>
 | 
			
		||||
 | 
			
		||||
                <!-- 信息显示 -->
 | 
			
		||||
                <text
 | 
			
		||||
                  x="20"
 | 
			
		||||
                  y="30"
 | 
			
		||||
                  fill="#10b981"
 | 
			
		||||
                  font-size="16"
 | 
			
		||||
                  font-weight="bold"
 | 
			
		||||
                  class="drop-shadow-sm"
 | 
			
		||||
                >
 | 
			
		||||
                  {{ displayFrequency }}
 | 
			
		||||
                </text>
 | 
			
		||||
                <text
 | 
			
		||||
                  :x="svgWidth - 80"
 | 
			
		||||
                  y="30"
 | 
			
		||||
                  fill="#10b981"
 | 
			
		||||
                  font-size="16"
 | 
			
		||||
                  font-weight="bold"
 | 
			
		||||
                  class="drop-shadow-sm"
 | 
			
		||||
                >
 | 
			
		||||
                  φ: {{ state.phase }}°
 | 
			
		||||
                </text>
 | 
			
		||||
                <text
 | 
			
		||||
                  :x="svgWidth / 2"
 | 
			
		||||
                  :y="svgHeight - 10"
 | 
			
		||||
                  fill="#10b981"
 | 
			
		||||
                  font-size="16"
 | 
			
		||||
                  font-weight="bold"
 | 
			
		||||
                  text-anchor="middle"
 | 
			
		||||
                  class="drop-shadow-sm"
 | 
			
		||||
                >
 | 
			
		||||
                  {{ displayTimebase }}
 | 
			
		||||
                </text>
 | 
			
		||||
              </svg>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- 自定义波形区域 (xl屏幕时在波形显示下方) -->
 | 
			
		||||
        <div
 | 
			
		||||
          class="card hidden xl:block bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg border border-slate-200/50 dark:border-slate-700/50"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="card-body p-6">
 | 
			
		||||
            <h3
 | 
			
		||||
              class="font-bold text-xl text-slate-800 dark:text-slate-200 mb-4 flex items-center gap-3"
 | 
			
		||||
            >
 | 
			
		||||
              <div
 | 
			
		||||
                class="p-2 rounded-lg bg-gradient-to-r from-purple-400 to-purple-600"
 | 
			
		||||
              >
 | 
			
		||||
                <CodeIcon class="w-5 h-5 text-white" />
 | 
			
		||||
              </div>
 | 
			
		||||
              自定义波形函数
 | 
			
		||||
            </h3>
 | 
			
		||||
 | 
			
		||||
            <div class="space-y-4">
 | 
			
		||||
              <div class="flex items-center gap-3">
 | 
			
		||||
                <label
 | 
			
		||||
                  class="text-sm font-medium text-slate-700 dark:text-slate-300 min-w-fit"
 | 
			
		||||
                  >函数表达式:</label
 | 
			
		||||
                >
 | 
			
		||||
                <input
 | 
			
		||||
                  v-model="state.customExpr"
 | 
			
		||||
                  class="input input-bordered flex-1 transition-all duration-200 focus:shadow-md focus:scale-[1.02]"
 | 
			
		||||
                  placeholder="例如: sin(t) 或 x^(2/3)+0.9*sqrt(3.3-x^2)*sin(a*PI*x) [a=7.8]"
 | 
			
		||||
                  @keyup.enter="applyCustomWaveform"
 | 
			
		||||
                />
 | 
			
		||||
                <button
 | 
			
		||||
                  class="btn btn-primary font-bold hover:shadow-lg transition-all duration-300 transform hover:scale-105"
 | 
			
		||||
                  @click="applyCustomWaveform"
 | 
			
		||||
                >
 | 
			
		||||
                  <PlayIcon class="w-4 h-4 mr-2" />
 | 
			
		||||
                  应用
 | 
			
		||||
                </button>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <div>
 | 
			
		||||
                <div
 | 
			
		||||
                  class="text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"
 | 
			
		||||
                >
 | 
			
		||||
                  示例函数:
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex flex-wrap gap-2">
 | 
			
		||||
                  <button
 | 
			
		||||
                    class="btn btn-sm btn-outline btn-secondary hover:shadow-md transition-all duration-200 transform hover:scale-105"
 | 
			
		||||
                    @click="applyExampleFunction('sin(t)')"
 | 
			
		||||
                  >
 | 
			
		||||
                    正弦波
 | 
			
		||||
                  </button>
 | 
			
		||||
                  <button
 | 
			
		||||
                    class="btn btn-sm btn-outline btn-secondary hover:shadow-md transition-all duration-200 transform hover:scale-105"
 | 
			
		||||
                    @click="applyExampleFunction('sin(t)^3')"
 | 
			
		||||
                  >
 | 
			
		||||
                    立方正弦
 | 
			
		||||
                  </button>
 | 
			
		||||
                  <button
 | 
			
		||||
                    class="btn btn-sm btn-outline btn-secondary hover:shadow-md transition-all duration-200 transform hover:scale-105"
 | 
			
		||||
                    @click="
 | 
			
		||||
                      applyExampleFunction(
 | 
			
		||||
                        '((x)^(2/3)+0.9*sqrt(3.3-(x)^2)*sin(10*PI*(x)))*0.75',
 | 
			
		||||
                      )
 | 
			
		||||
                    "
 | 
			
		||||
                  >
 | 
			
		||||
                    心形函数
 | 
			
		||||
                  </button>
 | 
			
		||||
                  <button
 | 
			
		||||
                    class="btn btn-sm btn-outline btn-secondary hover:shadow-md transition-all duration-200 transform hover:scale-105"
 | 
			
		||||
                    @click="applyExampleFunction('sin(t) + 0.3*sin(3*t)')"
 | 
			
		||||
                  >
 | 
			
		||||
                    谐波叠加
 | 
			
		||||
                  </button>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <!-- 右侧控制面板 (xl屏幕时占1列) -->
 | 
			
		||||
      <div class="space-y-6">
 | 
			
		||||
        <!-- 自动应用开关 -->
 | 
			
		||||
        <div
 | 
			
		||||
          class="card bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg border border-slate-200/50 dark:border-slate-700/50"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="card-body p-4 gap-5">
 | 
			
		||||
            <div class="flex items-center justify-between">
 | 
			
		||||
              <div class="flex items-center gap-3">
 | 
			
		||||
                <Settings class="w-5 h-5 text-blue-600" />
 | 
			
		||||
                <span class="font-semibold text-slate-800 dark:text-slate-200"
 | 
			
		||||
                  >自动应用设置</span
 | 
			
		||||
                >
 | 
			
		||||
              </div>
 | 
			
		||||
              <input
 | 
			
		||||
                type="checkbox"
 | 
			
		||||
                class="toggle toggle-primary scale-125"
 | 
			
		||||
                v-model="state.autoApply"
 | 
			
		||||
                id="auto-apply-toggle"
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <!-- 应用按钮 -->
 | 
			
		||||
            <div class="text-center">
 | 
			
		||||
              <button
 | 
			
		||||
                class="btn btn-primary btn-lg w-full shadow-xl hover:shadow-2xl transition-all duration-300 transform hover:scale-105"
 | 
			
		||||
                :disabled="state.isApplying"
 | 
			
		||||
                @click="applyWaveSettings"
 | 
			
		||||
              >
 | 
			
		||||
                <span v-if="state.isApplying" class="flex items-center gap-3">
 | 
			
		||||
                  <span class="loading loading-spinner loading-md"></span>
 | 
			
		||||
                  正在应用设置...
 | 
			
		||||
                </span>
 | 
			
		||||
                <span v-else class="flex items-center gap-3">
 | 
			
		||||
                  <Zap class="w-5 h-5" />
 | 
			
		||||
                  应用输出波形
 | 
			
		||||
                </span>
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- 波形选择 -->
 | 
			
		||||
        <div
 | 
			
		||||
          class="card bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg border border-slate-200/50 dark:border-slate-700/50"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="card-body p-4">
 | 
			
		||||
            <h3
 | 
			
		||||
              class="font-bold text-lg text-slate-800 dark:text-slate-200 mb-4"
 | 
			
		||||
            >
 | 
			
		||||
              波形类型
 | 
			
		||||
            </h3>
 | 
			
		||||
            <div class="grid grid-cols-3 xl:grid-cols-2 gap-2">
 | 
			
		||||
              <div
 | 
			
		||||
                v-for="(wave, index) in waveforms"
 | 
			
		||||
                :key="`wave-${index}`"
 | 
			
		||||
                :class="[
 | 
			
		||||
                  'btn transition-all duration-300 transform hover:scale-105',
 | 
			
		||||
                  state.waveformIndex === index
 | 
			
		||||
                    ? 'btn-primary shadow-lg shadow-blue-500/25'
 | 
			
		||||
                    : 'btn-outline btn-primary hover:shadow-md',
 | 
			
		||||
                ]"
 | 
			
		||||
                @click="selectWaveform(index)"
 | 
			
		||||
              >
 | 
			
		||||
                {{ wave.name }}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- 参数控制 -->
 | 
			
		||||
        <div
 | 
			
		||||
          class="card bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg border border-slate-200/50 dark:border-slate-700/50"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="card-body p-4">
 | 
			
		||||
            <div class="card-title flex flex-row items-center justify-between">
 | 
			
		||||
              <h3 class="font-bold text-lg text-slate-800 dark:text-slate-200">
 | 
			
		||||
                信号参数
 | 
			
		||||
              </h3>
 | 
			
		||||
              <button
 | 
			
		||||
                @click="resetConfiguration"
 | 
			
		||||
                class="w-8 h-8 bg-transparent text-red-600 text-sm border border-red-200 rounded-md py-2 px-2.5 transition duration-300 ease ring ring-transparent hover:ring-red-600/10 focus:ring-red-600/10 hover:border-red-600 shadow-sm focus:shadow flex items-center justify-center"
 | 
			
		||||
                type="button"
 | 
			
		||||
                title="重置配置"
 | 
			
		||||
              >
 | 
			
		||||
                <RefreshCcw />
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <!-- 时基控制 -->
 | 
			
		||||
            <div>
 | 
			
		||||
              <label
 | 
			
		||||
                class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"
 | 
			
		||||
                >时基</label
 | 
			
		||||
              >
 | 
			
		||||
              <div class="flex items-center gap-2">
 | 
			
		||||
                <button
 | 
			
		||||
                  class="btn btn-sm btn-circle btn-outline btn-primary hover:shadow-md transition-all duration-200"
 | 
			
		||||
                  @click="decreaseTimebase"
 | 
			
		||||
                >
 | 
			
		||||
                  <Minus class="w-4 h-4" />
 | 
			
		||||
                </button>
 | 
			
		||||
                <input
 | 
			
		||||
                  v-model="state.timebaseInput"
 | 
			
		||||
                  @blur="applyTimebaseInput"
 | 
			
		||||
                  @keyup.enter="applyTimebaseInput"
 | 
			
		||||
                  class="input input-bordered flex-1 text-center transition-all duration-200 focus:shadow-md focus:scale-105"
 | 
			
		||||
                  type="text"
 | 
			
		||||
                />
 | 
			
		||||
                <button
 | 
			
		||||
                  class="btn btn-sm btn-circle btn-outline btn-primary hover:shadow-md transition-all duration-200"
 | 
			
		||||
                  @click="increaseTimebase"
 | 
			
		||||
                >
 | 
			
		||||
                  <Plus class="w-4 h-4" />
 | 
			
		||||
                </button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <!-- 频率控制 -->
 | 
			
		||||
            <div>
 | 
			
		||||
              <label
 | 
			
		||||
                class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"
 | 
			
		||||
                >频率</label
 | 
			
		||||
              >
 | 
			
		||||
              <div class="flex items-center gap-2">
 | 
			
		||||
                <button
 | 
			
		||||
                  class="btn btn-sm btn-circle btn-outline btn-primary hover:shadow-md transition-all duration-200"
 | 
			
		||||
                  @click="decreaseFrequency"
 | 
			
		||||
                >
 | 
			
		||||
                  <Minus class="w-4 h-4" />
 | 
			
		||||
                </button>
 | 
			
		||||
                <input
 | 
			
		||||
                  v-model="state.frequencyInput"
 | 
			
		||||
                  @blur="applyFrequencyInput"
 | 
			
		||||
                  @keyup.enter="applyFrequencyInput"
 | 
			
		||||
                  class="input input-bordered flex-1 text-center transition-all duration-200 focus:shadow-md focus:scale-105"
 | 
			
		||||
                  type="text"
 | 
			
		||||
                />
 | 
			
		||||
                <button
 | 
			
		||||
                  class="btn btn-sm btn-circle btn-outline btn-primary hover:shadow-md transition-all duration-200"
 | 
			
		||||
                  @click="increaseFrequency"
 | 
			
		||||
                >
 | 
			
		||||
                  <Plus class="w-4 h-4" />
 | 
			
		||||
                </button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <!-- 相位控制 -->
 | 
			
		||||
            <div>
 | 
			
		||||
              <label
 | 
			
		||||
                class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"
 | 
			
		||||
                >相位</label
 | 
			
		||||
              >
 | 
			
		||||
              <div class="flex items-center gap-2">
 | 
			
		||||
                <button
 | 
			
		||||
                  class="btn btn-sm btn-circle btn-outline btn-primary hover:shadow-md transition-all duration-200"
 | 
			
		||||
                  @click="decreasePhase"
 | 
			
		||||
                >
 | 
			
		||||
                  <Minus class="w-4 h-4" />
 | 
			
		||||
                </button>
 | 
			
		||||
                <input
 | 
			
		||||
                  v-model="state.phaseInput"
 | 
			
		||||
                  @blur="applyPhaseInput"
 | 
			
		||||
                  @keyup.enter="applyPhaseInput"
 | 
			
		||||
                  class="input input-bordered flex-1 text-center transition-all duration-200 focus:shadow-md focus:scale-105"
 | 
			
		||||
                  type="text"
 | 
			
		||||
                />
 | 
			
		||||
                <button
 | 
			
		||||
                  class="btn btn-sm btn-circle btn-outline btn-primary hover:shadow-md transition-all duration-200"
 | 
			
		||||
                  @click="increasePhase"
 | 
			
		||||
                >
 | 
			
		||||
                  <Plus class="w-4 h-4" />
 | 
			
		||||
                </button>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- 小屏幕时自定义波形区域移到最后 -->
 | 
			
		||||
        <div class="xl:hidden">
 | 
			
		||||
          <div
 | 
			
		||||
            class="card bg-white/80 dark:bg-slate-800/80 backdrop-blur-sm shadow-lg border border-slate-200/50 dark:border-slate-700/50"
 | 
			
		||||
          >
 | 
			
		||||
            <div class="card-body p-6">
 | 
			
		||||
              <h3
 | 
			
		||||
                class="font-bold text-xl text-slate-800 dark:text-slate-200 mb-4 flex items-center gap-3"
 | 
			
		||||
              >
 | 
			
		||||
                <div
 | 
			
		||||
                  class="p-2 rounded-lg bg-gradient-to-r from-purple-400 to-purple-600"
 | 
			
		||||
                >
 | 
			
		||||
                  <CodeIcon class="w-5 h-5 text-white" />
 | 
			
		||||
                </div>
 | 
			
		||||
                自定义波形函数
 | 
			
		||||
              </h3>
 | 
			
		||||
 | 
			
		||||
              <div class="space-y-4">
 | 
			
		||||
                <div
 | 
			
		||||
                  class="flex flex-col sm:flex-row items-start sm:items-center gap-3"
 | 
			
		||||
                >
 | 
			
		||||
                  <label
 | 
			
		||||
                    class="text-sm font-medium text-slate-700 dark:text-slate-300 min-w-fit"
 | 
			
		||||
                    >函数表达式:</label
 | 
			
		||||
                  >
 | 
			
		||||
                  <input
 | 
			
		||||
                    v-model="state.customExpr"
 | 
			
		||||
                    class="input input-bordered flex-1 transition-all duration-200 focus:shadow-md focus:scale-[1.02]"
 | 
			
		||||
                    placeholder="例如: sin(t) 或 x^(2/3)+0.9*sqrt(3.3-x^2)*sin(a*PI*x) [a=7.8]"
 | 
			
		||||
                    @keyup.enter="applyCustomWaveform"
 | 
			
		||||
                  />
 | 
			
		||||
                  <button
 | 
			
		||||
                    class="btn btn-primary font-bold hover:shadow-lg transition-all duration-300 transform hover:scale-105 w-full sm:w-auto"
 | 
			
		||||
                    @click="applyCustomWaveform"
 | 
			
		||||
                  >
 | 
			
		||||
                    <PlayIcon class="w-4 h-4 mr-2" />
 | 
			
		||||
                    应用
 | 
			
		||||
                  </button>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div>
 | 
			
		||||
                  <div
 | 
			
		||||
                    class="text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"
 | 
			
		||||
                  >
 | 
			
		||||
                    示例函数:
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div class="grid grid-cols-2 sm:flex sm:flex-wrap gap-2">
 | 
			
		||||
                    <button
 | 
			
		||||
                      class="btn btn-sm btn-outline btn-secondary hover:shadow-md transition-all duration-200 transform hover:scale-105"
 | 
			
		||||
                      @click="applyExampleFunction('sin(t)')"
 | 
			
		||||
                    >
 | 
			
		||||
                      正弦波
 | 
			
		||||
                    </button>
 | 
			
		||||
                    <button
 | 
			
		||||
                      class="btn btn-sm btn-outline btn-secondary hover:shadow-md transition-all duration-200 transform hover:scale-105"
 | 
			
		||||
                      @click="applyExampleFunction('sin(t)^3')"
 | 
			
		||||
                    >
 | 
			
		||||
                      立方正弦
 | 
			
		||||
                    </button>
 | 
			
		||||
                    <button
 | 
			
		||||
                      class="btn btn-sm btn-outline btn-secondary hover:shadow-md transition-all duration-200 transform hover:scale-105"
 | 
			
		||||
                      @click="
 | 
			
		||||
                        applyExampleFunction(
 | 
			
		||||
                          '((x)^(2/3)+0.9*sqrt(3.3-(x)^2)*sin(10*PI*(x)))*0.75',
 | 
			
		||||
                        )
 | 
			
		||||
                      "
 | 
			
		||||
                    >
 | 
			
		||||
                      心形函数
 | 
			
		||||
                    </button>
 | 
			
		||||
                    <button
 | 
			
		||||
                      class="btn btn-sm btn-outline btn-secondary hover:shadow-md transition-all duration-200 transform hover:scale-105"
 | 
			
		||||
                      @click="applyExampleFunction('sin(t) + 0.3*sin(3*t)')"
 | 
			
		||||
                    >
 | 
			
		||||
                      谐波叠加
 | 
			
		||||
                    </button>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
 | 
			
		||||
import { useEquipments } from "@/stores/equipments";
 | 
			
		||||
import { useDialogStore } from "@/stores/dialog";
 | 
			
		||||
import { toInteger } from "lodash";
 | 
			
		||||
import { AuthManager } from "@/utils/AuthManager";
 | 
			
		||||
import { DDSClient } from "@/APIClient";
 | 
			
		||||
import { compile, type EvalFunction } from "mathjs";
 | 
			
		||||
import {
 | 
			
		||||
  Settings,
 | 
			
		||||
  Signature,
 | 
			
		||||
  Plus,
 | 
			
		||||
  Minus,
 | 
			
		||||
  Zap,
 | 
			
		||||
  Play as PlayIcon,
 | 
			
		||||
  Code as CodeIcon,
 | 
			
		||||
  RefreshCcw,
 | 
			
		||||
} from "lucide-vue-next";
 | 
			
		||||
 | 
			
		||||
// 新增:重置DDS参数
 | 
			
		||||
function resetConfiguration() {
 | 
			
		||||
  state.value.frequency = 1000;
 | 
			
		||||
  state.value.phase = 0;
 | 
			
		||||
  state.value.timebase = 1;
 | 
			
		||||
  state.value.waveformIndex = 0;
 | 
			
		||||
  state.value.customExpr = "";
 | 
			
		||||
  state.value.frequencyInput = "1.00 kHz";
 | 
			
		||||
  state.value.phaseInput = "0";
 | 
			
		||||
  state.value.timebaseInput = "1.00 s/div";
 | 
			
		||||
  // 清除自定义波形
 | 
			
		||||
  customWaveformFunction.value = null;
 | 
			
		||||
  // 应用重置后的设置
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 状态变量
 | 
			
		||||
const dds = AuthManager.createClient(DDSClient);
 | 
			
		||||
const eqps = useEquipments();
 | 
			
		||||
const dialog = useDialogStore();
 | 
			
		||||
 | 
			
		||||
// 响应式SVG宽高与容器引用
 | 
			
		||||
const waveformContainer = ref<HTMLElement | null>(null);
 | 
			
		||||
const svgWidth = ref(400);
 | 
			
		||||
const svgHeight = ref(300);
 | 
			
		||||
 | 
			
		||||
function updateSvgWidth() {
 | 
			
		||||
  if (waveformContainer.value) {
 | 
			
		||||
    svgWidth.value = waveformContainer.value.offsetWidth || 400;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  updateSvgWidth();
 | 
			
		||||
  window.addEventListener("resize", updateSvgWidth);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onBeforeUnmount(() => {
 | 
			
		||||
  window.removeEventListener("resize", updateSvgWidth);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const state = ref({
 | 
			
		||||
  frequency: 1000,
 | 
			
		||||
  phase: 0,
 | 
			
		||||
  timebase: 1,
 | 
			
		||||
  waveformIndex: 0,
 | 
			
		||||
  customExpr: "",
 | 
			
		||||
  isApplying: false,
 | 
			
		||||
  frequencyInput: "1.00 kHz",
 | 
			
		||||
  phaseInput: "0",
 | 
			
		||||
  timebaseInput: "1.00 s/div",
 | 
			
		||||
  autoApply: false,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const waveforms = [
 | 
			
		||||
  {
 | 
			
		||||
    name: "正弦波",
 | 
			
		||||
    type: "sine",
 | 
			
		||||
    fn: (x: number, width: number, height: number, phaseRad: number) =>
 | 
			
		||||
      (height / 2) * Math.sin(2 * Math.PI * (x / width) * 2 + phaseRad),
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: "方波",
 | 
			
		||||
    type: "square",
 | 
			
		||||
    fn: (x: number, width: number, height: number, phaseRad: number) => {
 | 
			
		||||
      const normX = (x / width + phaseRad / (2 * Math.PI)) % 1;
 | 
			
		||||
      return normX < 0.5 ? height / 4 : -height / 4;
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: "三角波",
 | 
			
		||||
    type: "triangle",
 | 
			
		||||
    fn: (x: number, width: number, height: number, phaseRad: number) => {
 | 
			
		||||
      const normX = (x / width + phaseRad / (2 * Math.PI)) % 1;
 | 
			
		||||
      return height / 2 - height * Math.abs(2 * normX - 1);
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: "锯齿波",
 | 
			
		||||
    type: "sawtooth",
 | 
			
		||||
    fn: (x: number, width: number, height: number, phaseRad: number) => {
 | 
			
		||||
      const normX = (x / width + phaseRad / (2 * Math.PI)) % 1;
 | 
			
		||||
      return height / 2 - (height / 2) * (2 * normX);
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: "自定义",
 | 
			
		||||
    type: "custom",
 | 
			
		||||
    fn: (x: number, width: number, height: number, phaseRad: number) => {
 | 
			
		||||
      if (customWaveformFunction.value) {
 | 
			
		||||
        try {
 | 
			
		||||
          const t = 2 * Math.PI * (x / width) * 2 + phaseRad;
 | 
			
		||||
          // 归一化x到[-1,1],便于自定义表达式使用
 | 
			
		||||
          const xn = (x / width) * 2 - 1;
 | 
			
		||||
          const scope = { t, x, xn, width, height, phaseRad, PI: Math.PI };
 | 
			
		||||
          const y = customWaveformFunction.value.evaluate(scope);
 | 
			
		||||
          if (typeof y === "number" && isFinite(y)) {
 | 
			
		||||
            return y * (height / 2);
 | 
			
		||||
          }
 | 
			
		||||
          return 0;
 | 
			
		||||
        } catch {
 | 
			
		||||
          return 0;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return 0;
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// 自定义表达式函数引用(mathjs EvalFunction)
 | 
			
		||||
const customWaveformFunction = ref<EvalFunction | null>(null);
 | 
			
		||||
 | 
			
		||||
function formatFrequency(freq: number): string {
 | 
			
		||||
  if (freq >= 1000000) {
 | 
			
		||||
    return `${(freq / 1000000).toFixed(2)} MHz`;
 | 
			
		||||
  } else if (freq >= 1000) {
 | 
			
		||||
    return `${(freq / 1000).toFixed(2)} kHz`;
 | 
			
		||||
  } else {
 | 
			
		||||
    return `${freq.toFixed(2)} Hz`;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parseFrequency(str: string): number {
 | 
			
		||||
  let value = parseFloat(str);
 | 
			
		||||
  if (str.includes("MHz")) value *= 1e6;
 | 
			
		||||
  else if (str.includes("kHz")) value *= 1e3;
 | 
			
		||||
  else if (str.includes("Hz")) value *= 1;
 | 
			
		||||
  return isNaN(value) ? 1000 : value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function formatTimebase(tb: number): string {
 | 
			
		||||
  if (tb < 0.001) {
 | 
			
		||||
    return `${(tb * 1e6).toFixed(0)} μs/div`;
 | 
			
		||||
  } else if (tb < 1) {
 | 
			
		||||
    return `${(tb * 1000).toFixed(0)} ms/div`;
 | 
			
		||||
  } else {
 | 
			
		||||
    return `${tb.toFixed(2)} s/div`;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parseTimebase(str: string): number {
 | 
			
		||||
  let value = parseFloat(str);
 | 
			
		||||
  if (str.includes("μs")) value /= 1e6;
 | 
			
		||||
  else if (str.includes("ms")) value /= 1e3;
 | 
			
		||||
  else if (str.includes("s")) value /= 1;
 | 
			
		||||
  return isNaN(value) ? 1 : value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const displayTimebase = computed(() => formatTimebase(state.value.timebase));
 | 
			
		||||
const displayFrequency = computed(() => formatFrequency(state.value.frequency));
 | 
			
		||||
 | 
			
		||||
// 生成波形SVG路径
 | 
			
		||||
const currentWaveformPath = computed(() => {
 | 
			
		||||
  const width = svgWidth.value;
 | 
			
		||||
  const height = svgHeight.value - 60;
 | 
			
		||||
  const xOffset = 0;
 | 
			
		||||
  const yOffset = 40;
 | 
			
		||||
  const currentWaveform = waveforms[state.value.waveformIndex];
 | 
			
		||||
  const phaseRadians = (state.value.phase * Math.PI) / 180;
 | 
			
		||||
  const freqLog = Math.log10(state.value.frequency) - 2;
 | 
			
		||||
  const frequencyFactor = Math.max(0.1, Math.min(10, freqLog));
 | 
			
		||||
  const timebaseFactor = 1 / state.value.timebase;
 | 
			
		||||
  const scaleFactor = timebaseFactor * frequencyFactor;
 | 
			
		||||
 | 
			
		||||
  let path = `M${xOffset},${yOffset + height / 2}`;
 | 
			
		||||
  const waveFunction = currentWaveform.fn;
 | 
			
		||||
 | 
			
		||||
  for (let x = 0; x <= width; x++) {
 | 
			
		||||
    const scaledX = x * scaleFactor;
 | 
			
		||||
    const y = waveFunction(scaledX, width, height, phaseRadians);
 | 
			
		||||
    path += ` L${x + xOffset},${yOffset + height / 2 - y}`;
 | 
			
		||||
  }
 | 
			
		||||
  return path;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 只允许number类型key
 | 
			
		||||
type NumberStateKey = "frequency" | "phase" | "timebase";
 | 
			
		||||
 | 
			
		||||
// 通用增减函数(类型安全)
 | 
			
		||||
function adjustValue(
 | 
			
		||||
  key: NumberStateKey,
 | 
			
		||||
  delta: number,
 | 
			
		||||
  steps: number[],
 | 
			
		||||
  min: number,
 | 
			
		||||
  max: number,
 | 
			
		||||
) {
 | 
			
		||||
  let v = state.value[key];
 | 
			
		||||
  if (typeof v !== "number") return;
 | 
			
		||||
  let step = steps.find((s) => Math.abs(v) < s * 10) || steps[steps.length - 1];
 | 
			
		||||
  v += delta * step;
 | 
			
		||||
  v = Math.max(min, Math.min(max, v));
 | 
			
		||||
  state.value[key] = parseFloat(v.toFixed(2));
 | 
			
		||||
  if (key === "frequency") {
 | 
			
		||||
    state.value.frequencyInput = formatFrequency(state.value.frequency);
 | 
			
		||||
  }
 | 
			
		||||
  if (key === "phase") {
 | 
			
		||||
    state.value.phaseInput = state.value.phase.toString();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function increaseTimebase() {
 | 
			
		||||
  adjustValue("timebase", 1, [0.001, 0.01, 0.1, 0.5], 0.001, 5);
 | 
			
		||||
  state.value.timebaseInput = formatTimebase(state.value.timebase);
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function decreaseTimebase() {
 | 
			
		||||
  adjustValue("timebase", -1, [0.001, 0.01, 0.1, 0.5], 0.001, 5);
 | 
			
		||||
  state.value.timebaseInput = formatTimebase(state.value.timebase);
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function applyTimebaseInput() {
 | 
			
		||||
  const value = parseTimebase(state.value.timebaseInput);
 | 
			
		||||
  state.value.timebase = Math.min(Math.max(value, 0.001), 5);
 | 
			
		||||
  state.value.timebaseInput = formatTimebase(state.value.timebase);
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function selectWaveform(index: number) {
 | 
			
		||||
  state.value.waveformIndex = index;
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function increaseFrequency() {
 | 
			
		||||
  adjustValue("frequency", 1, [0.1, 1, 10, 100, 1000, 10000], 0.1, 10000000);
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function decreaseFrequency() {
 | 
			
		||||
  adjustValue("frequency", -1, [0.1, 1, 10, 100, 1000, 10000], 0.1, 10000000);
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function applyFrequencyInput() {
 | 
			
		||||
  const value = parseFrequency(state.value.frequencyInput);
 | 
			
		||||
  state.value.frequency = Math.min(Math.max(value, 0.1), 10000000);
 | 
			
		||||
  state.value.frequencyInput = formatFrequency(state.value.frequency);
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function increasePhase() {
 | 
			
		||||
  adjustValue("phase", 15, [1], 0, 359.99);
 | 
			
		||||
  if (state.value.phase >= 360) state.value.phase -= 360;
 | 
			
		||||
  state.value.phaseInput = state.value.phase.toString();
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function decreasePhase() {
 | 
			
		||||
  adjustValue("phase", -15, [1], 0, 359.99);
 | 
			
		||||
  if (state.value.phase < 0) state.value.phase += 360;
 | 
			
		||||
  state.value.phaseInput = state.value.phase.toString();
 | 
			
		||||
  if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function applyPhaseInput() {
 | 
			
		||||
  let value = parseFloat(state.value.phaseInput);
 | 
			
		||||
  if (!isNaN(value)) {
 | 
			
		||||
    while (value >= 360) value -= 360;
 | 
			
		||||
    while (value < 0) value += 360;
 | 
			
		||||
    state.value.phase = value;
 | 
			
		||||
    state.value.phaseInput = state.value.phase.toString();
 | 
			
		||||
    if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
  } else {
 | 
			
		||||
    state.value.phaseInput = state.value.phase.toString();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 自定义波形表达式
 | 
			
		||||
function applyCustomWaveform() {
 | 
			
		||||
  if (!state.value.customExpr) {
 | 
			
		||||
    customWaveformFunction.value = null;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  customWaveformFunction.value = null;
 | 
			
		||||
  try {
 | 
			
		||||
    const expr = state.value.customExpr;
 | 
			
		||||
    const compiled = compile(expr);
 | 
			
		||||
    customWaveformFunction.value = compiled;
 | 
			
		||||
    state.value.waveformIndex = waveforms.findIndex((w) => w.type === "custom");
 | 
			
		||||
    if (state.value.autoApply) applyWaveSettings();
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    dialog.error("表达式无效");
 | 
			
		||||
    customWaveformFunction.value = null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function applyExampleFunction(expr: string) {
 | 
			
		||||
  state.value.customExpr = expr;
 | 
			
		||||
  applyCustomWaveform();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 应用输出(集中设备操作和错误处理)
 | 
			
		||||
async function applyWaveSettings() {
 | 
			
		||||
  try {
 | 
			
		||||
    state.value.isApplying = true;
 | 
			
		||||
    const idx = state.value.waveformIndex;
 | 
			
		||||
    const freq = state.value.frequency;
 | 
			
		||||
    const ph = state.value.phase;
 | 
			
		||||
    let ok = true;
 | 
			
		||||
    const ret1 = await dds.setWaveNum(eqps.boardAddr, eqps.boardPort, 0, idx);
 | 
			
		||||
    if (!ret1) ok = false;
 | 
			
		||||
    const ret2 = await dds.setFreq(
 | 
			
		||||
      eqps.boardAddr,
 | 
			
		||||
      eqps.boardPort,
 | 
			
		||||
      0,
 | 
			
		||||
      idx,
 | 
			
		||||
      Math.round((freq * Math.pow(2, 32 - 20)) / 10),
 | 
			
		||||
    );
 | 
			
		||||
    if (!ret2) ok = false;
 | 
			
		||||
    const ret3 = await dds.setPhase(
 | 
			
		||||
      eqps.boardAddr,
 | 
			
		||||
      eqps.boardPort,
 | 
			
		||||
      0,
 | 
			
		||||
      idx,
 | 
			
		||||
      Math.round((ph * 4096) / 360),
 | 
			
		||||
    );
 | 
			
		||||
    if (!ret3) ok = false;
 | 
			
		||||
    if (!ok) dialog.error("应用失败");
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    dialog.error("应用失败");
 | 
			
		||||
    console.error(e);
 | 
			
		||||
  } finally {
 | 
			
		||||
    state.value.isApplying = false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="postcss">
 | 
			
		||||
.dds-controller {
 | 
			
		||||
  animation: fadeIn 0.6s ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@keyframes fadeIn {
 | 
			
		||||
  from {
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
    transform: translateY(20px);
 | 
			
		||||
  }
 | 
			
		||||
  to {
 | 
			
		||||
    opacity: 1;
 | 
			
		||||
    transform: translateY(0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.card {
 | 
			
		||||
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.card:hover {
 | 
			
		||||
  transform: translateY(-2px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn {
 | 
			
		||||
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.input:focus {
 | 
			
		||||
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 自定义动画 */
 | 
			
		||||
@keyframes pulse-gentle {
 | 
			
		||||
  0%,
 | 
			
		||||
  100% {
 | 
			
		||||
    opacity: 1;
 | 
			
		||||
  }
 | 
			
		||||
  50% {
 | 
			
		||||
    opacity: 0.8;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.animate-pulse {
 | 
			
		||||
  animation: pulse-gentle 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 渐变文字效果 */
 | 
			
		||||
.bg-clip-text {
 | 
			
		||||
  -webkit-background-clip: text;
 | 
			
		||||
  background-clip: text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 毛玻璃效果增强 */
 | 
			
		||||
.backdrop-blur-sm {
 | 
			
		||||
  backdrop-filter: blur(8px);
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
		Reference in New Issue
	
	Block a user