feat: 逻辑分析仪深度可用户输入自定义数字
This commit is contained in:
		@@ -259,7 +259,8 @@ public class LogicAnalyzerController : ControllerBase
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (capture_length < 0 || capture_length > 2048*32)
 | 
			
		||||
            //DDR深度为 32'h01000000 - 32'h0FFFFFFF
 | 
			
		||||
            if (capture_length < 0 || capture_length > 0x10000000 - 0x01000000)
 | 
			
		||||
                return BadRequest("采样深度设置错误");
 | 
			
		||||
            if (pre_capture_length < 0 || pre_capture_length >= capture_length)
 | 
			
		||||
                return BadRequest("预采样深度必须小于捕获深度");
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,7 @@ static class AnalyzerAddr
 | 
			
		||||
    public const UInt32 DMA1_START_WRITE_ADDR = DMA1_BASE + 0x0000_0012;
 | 
			
		||||
    public const UInt32 DMA1_END_WRITE_ADDR = DMA1_BASE + 0x0000_0013;
 | 
			
		||||
    public const UInt32 DMA1_CAPTURE_CTRL_ADDR = DMA1_BASE + 0x0000_0014;
 | 
			
		||||
    public const UInt32 STORE_OFFSET_ADDR = DDR_BASE + 0x0010_0000;
 | 
			
		||||
    public const UInt32 STORE_OFFSET_ADDR = DDR_BASE + 0x0100_0000;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0100_0000 - 0x0100_03FF 只读 32位波形存储,得到的32位数据中低八位最先捕获,高八位最后捕获。<br/>
 | 
			
		||||
 
 | 
			
		||||
@@ -76,28 +76,12 @@ const channelDivOptions = [
 | 
			
		||||
  { value: 32, label: "32通道", description: "启用32个通道 (CH0-CH31)" },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// 捕获深度选项
 | 
			
		||||
const captureLengthOptions = [
 | 
			
		||||
  { value: 256, label: "256" },
 | 
			
		||||
  { value: 512, label: "512" },
 | 
			
		||||
  { value: 1024, label: "1K" },
 | 
			
		||||
  { value: 2048, label: "2K" },
 | 
			
		||||
  { value: 4096, label: "4K" },
 | 
			
		||||
  { value: 8192, label: "8K" },
 | 
			
		||||
  { value: 16384, label: "16K" },
 | 
			
		||||
  { value: 32768, label: "32K" },
 | 
			
		||||
];
 | 
			
		||||
// 捕获深度限制常量
 | 
			
		||||
const CAPTURE_LENGTH_MIN = 1024; // 最小捕获深度 1024
 | 
			
		||||
const CAPTURE_LENGTH_MAX = 0x10000000 - 0x01000000; // 最大捕获深度
 | 
			
		||||
 | 
			
		||||
// 预捕获深度选项
 | 
			
		||||
const preCaptureLengthOptions = [
 | 
			
		||||
  { value: 0, label: "0" },
 | 
			
		||||
  { value: 16, label: "16" },
 | 
			
		||||
  { value: 32, label: "32" },
 | 
			
		||||
  { value: 64, label: "64" },
 | 
			
		||||
  { value: 128, label: "128" },
 | 
			
		||||
  { value: 256, label: "256" },
 | 
			
		||||
  { value: 512, label: "512" },
 | 
			
		||||
];
 | 
			
		||||
// 预捕获深度限制常量  
 | 
			
		||||
const PRE_CAPTURE_LENGTH_MIN = 0; // 最小预捕获深度 0
 | 
			
		||||
 | 
			
		||||
// 默认颜色数组
 | 
			
		||||
const defaultColors = [
 | 
			
		||||
@@ -126,8 +110,8 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
 | 
			
		||||
    // 触发设置相关状态
 | 
			
		||||
    const currentGlobalMode = ref<GlobalCaptureMode>(GlobalCaptureMode.AND);
 | 
			
		||||
    const currentChannelDiv = ref<number>(8); // 默认启用8个通道
 | 
			
		||||
    const captureLength = ref<number>(1024); // 捕获深度,默认1024
 | 
			
		||||
    const preCaptureLength = ref<number>(0); // 预捕获深度,默认0
 | 
			
		||||
    const captureLength = ref<number>(CAPTURE_LENGTH_MIN); // 捕获深度,默认为最小值
 | 
			
		||||
    const preCaptureLength = ref<number>(PRE_CAPTURE_LENGTH_MIN); // 预捕获深度,默认0
 | 
			
		||||
    const isApplying = ref(false);
 | 
			
		||||
    const isCapturing = ref(false); // 添加捕获状态标识
 | 
			
		||||
 | 
			
		||||
@@ -181,6 +165,64 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // 验证捕获深度
 | 
			
		||||
    const validateCaptureLength = (value: number): { valid: boolean; message?: string } => {
 | 
			
		||||
      if (!Number.isInteger(value)) {
 | 
			
		||||
        return { valid: false, message: "捕获深度必须是整数" };
 | 
			
		||||
      }
 | 
			
		||||
      if (value < CAPTURE_LENGTH_MIN) {
 | 
			
		||||
        return { valid: false, message: `捕获深度不能小于 ${CAPTURE_LENGTH_MIN}` };
 | 
			
		||||
      }
 | 
			
		||||
      if (value > CAPTURE_LENGTH_MAX) {
 | 
			
		||||
        return { valid: false, message: `捕获深度不能大于 ${CAPTURE_LENGTH_MAX.toLocaleString()}` };
 | 
			
		||||
      }
 | 
			
		||||
      return { valid: true };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // 验证预捕获深度
 | 
			
		||||
    const validatePreCaptureLength = (value: number, currentCaptureLength: number): { valid: boolean; message?: string } => {
 | 
			
		||||
      if (!Number.isInteger(value)) {
 | 
			
		||||
        return { valid: false, message: "预捕获深度必须是整数" };
 | 
			
		||||
      }
 | 
			
		||||
      if (value < PRE_CAPTURE_LENGTH_MIN) {
 | 
			
		||||
        return { valid: false, message: `预捕获深度不能小于 ${PRE_CAPTURE_LENGTH_MIN}` };
 | 
			
		||||
      }
 | 
			
		||||
      if (value >= currentCaptureLength) {
 | 
			
		||||
        return { valid: false, message: `预捕获深度不能大于等于捕获深度 (${currentCaptureLength})` };
 | 
			
		||||
      }
 | 
			
		||||
      return { valid: true };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // 设置捕获深度
 | 
			
		||||
    const setCaptureLength = (value: number) => {
 | 
			
		||||
      const validation = validateCaptureLength(value);
 | 
			
		||||
      if (!validation.valid) {
 | 
			
		||||
        alert?.error(validation.message!, 3000);
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // 检查预捕获深度是否仍然有效
 | 
			
		||||
      if (preCaptureLength.value >= value) {
 | 
			
		||||
        preCaptureLength.value = Math.max(0, value - 1);
 | 
			
		||||
        alert?.warn(`预捕获深度已自动调整为 ${preCaptureLength.value}`, 3000);
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      captureLength.value = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // 设置预捕获深度
 | 
			
		||||
    const setPreCaptureLength = (value: number) => {
 | 
			
		||||
      const validation = validatePreCaptureLength(value, captureLength.value);
 | 
			
		||||
      if (!validation.valid) {
 | 
			
		||||
        alert?.error(validation.message!, 3000);
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      preCaptureLength.value = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // 设置通道组
 | 
			
		||||
    const setChannelDiv = (channelCount: number) => {
 | 
			
		||||
      // 验证通道数量是否有效
 | 
			
		||||
@@ -717,8 +759,15 @@ const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
 | 
			
		||||
      operators,
 | 
			
		||||
      signalValues,
 | 
			
		||||
      channelDivOptions, // 导出通道组选项
 | 
			
		||||
      captureLengthOptions, // 导出捕获深度选项
 | 
			
		||||
      preCaptureLengthOptions, // 导出预捕获深度选项
 | 
			
		||||
 | 
			
		||||
      // 捕获深度常量和验证
 | 
			
		||||
      CAPTURE_LENGTH_MIN,
 | 
			
		||||
      CAPTURE_LENGTH_MAX,
 | 
			
		||||
      PRE_CAPTURE_LENGTH_MIN,
 | 
			
		||||
      validateCaptureLength,
 | 
			
		||||
      validatePreCaptureLength,
 | 
			
		||||
      setCaptureLength,
 | 
			
		||||
      setPreCaptureLength,
 | 
			
		||||
 | 
			
		||||
      // 触发设置方法
 | 
			
		||||
      setChannelDiv, // 导出设置通道组方法
 | 
			
		||||
 
 | 
			
		||||
@@ -3,89 +3,182 @@
 | 
			
		||||
    <!-- 通道配置 -->
 | 
			
		||||
    <div class="form-control">
 | 
			
		||||
      <!-- 全局触发模式选择和通道组配置 -->
 | 
			
		||||
      <div class="flex flex-col lg:flex-row justify-between gap-4 my-4 mx-2">
 | 
			
		||||
        <!-- 左侧:全局触发模式和通道组选择 -->
 | 
			
		||||
        <div class="flex flex-col lg:flex-row gap-4">
 | 
			
		||||
          <div class="flex flex-row gap-2 items-center">
 | 
			
		||||
            <label class="label">
 | 
			
		||||
              <span class="label-text text-sm">全局触发逻辑</span>
 | 
			
		||||
      <div class="flex flex-col gap-6 my-4 mx-2">
 | 
			
		||||
        <div class="flex flex-col lg:flex-row gap-6">
 | 
			
		||||
          <div class="flex flex-col gap-2">
 | 
			
		||||
            <label class="block text-sm font-semibold antialiased">
 | 
			
		||||
              全局触发逻辑
 | 
			
		||||
            </label>
 | 
			
		||||
            <select
 | 
			
		||||
              v-model="currentGlobalMode"
 | 
			
		||||
              @change="setGlobalMode(currentGlobalMode)"
 | 
			
		||||
              class="select select-sm select-bordered"
 | 
			
		||||
            >
 | 
			
		||||
              <option
 | 
			
		||||
                v-for="mode in globalModes"
 | 
			
		||||
                :key="mode.value"
 | 
			
		||||
                :value="mode.value"
 | 
			
		||||
            <div class="relative w-[200px]">
 | 
			
		||||
              <button 
 | 
			
		||||
                tabindex="0" 
 | 
			
		||||
                type="button" 
 | 
			
		||||
                class="flex items-center gap-4 justify-between h-max outline-none focus:outline-none bg-transparent ring-transparent border border-slate-200 transition-all duration-300 ease-in disabled:opacity-50 disabled:pointer-events-none select-none text-start text-sm rounded-md py-2 px-2.5 ring shadow-sm hover:border-slate-800 hover:ring-slate-800/10 focus:border-slate-800 focus:ring-slate-800/10 w-full"
 | 
			
		||||
                @click="toggleGlobalModeDropdown"
 | 
			
		||||
                :aria-expanded="showGlobalModeDropdown"
 | 
			
		||||
                aria-haspopup="listbox" 
 | 
			
		||||
                role="combobox"
 | 
			
		||||
              >
 | 
			
		||||
                {{ mode.label }} - {{ mode.description }}
 | 
			
		||||
              </option>
 | 
			
		||||
            </select>
 | 
			
		||||
                <span>{{ currentGlobalModeLabel }}</span>
 | 
			
		||||
                <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor" class="h-[1em] w-[1em] translate-x-0.5 stroke-[1.5]">
 | 
			
		||||
                  <path d="M17 8L12 3L7 8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
 | 
			
		||||
                  <path d="M17 16L12 21L7 16" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
 | 
			
		||||
                </svg>
 | 
			
		||||
              </button>
 | 
			
		||||
              <input readonly style="display:none" :value="currentGlobalMode" />
 | 
			
		||||
              <!-- 下拉菜单 -->
 | 
			
		||||
              <div v-if="showGlobalModeDropdown" class="absolute top-full left-0 right-0 mt-1 bg-white border border-slate-200 rounded-md shadow-lg z-50">
 | 
			
		||||
                <div
 | 
			
		||||
                  v-for="mode in globalModes"
 | 
			
		||||
                  :key="mode.value"
 | 
			
		||||
                  @click="selectGlobalMode(mode.value)"
 | 
			
		||||
                  class="px-3 py-2 text-sm text-slate-700 hover:bg-slate-100 cursor-pointer"
 | 
			
		||||
                  :class="{ 'bg-slate-100': mode.value === currentGlobalMode }"
 | 
			
		||||
                >
 | 
			
		||||
                  {{ mode.label }}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p class="flex items-center text-xs text-slate-400">
 | 
			
		||||
              {{ currentGlobalModeDescription }}
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="flex flex-row gap-2 items-center">
 | 
			
		||||
            <label class="label">
 | 
			
		||||
              <span class="label-text text-sm">通道组</span>
 | 
			
		||||
          <div class="flex flex-col gap-2">
 | 
			
		||||
            <label class="block text-sm font-semibold antialiased">
 | 
			
		||||
              通道组
 | 
			
		||||
            </label>
 | 
			
		||||
            <select
 | 
			
		||||
              v-model="currentChannelDiv"
 | 
			
		||||
              @change="setChannelDiv(currentChannelDiv)"
 | 
			
		||||
              class="select select-sm select-bordered"
 | 
			
		||||
            >
 | 
			
		||||
              <option
 | 
			
		||||
                v-for="option in channelDivOptions"
 | 
			
		||||
                :key="option.value"
 | 
			
		||||
                :value="option.value"
 | 
			
		||||
            <div class="relative w-[200px]">
 | 
			
		||||
              <button 
 | 
			
		||||
                tabindex="0" 
 | 
			
		||||
                type="button" 
 | 
			
		||||
                class="flex items-center gap-4 justify-between h-max outline-none focus:outline-none bg-transparent ring-transparent border border-slate-200 transition-all duration-300 ease-in disabled:opacity-50 disabled:pointer-events-none select-none text-start text-sm rounded-md py-2 px-2.5 ring shadow-sm hover:border-slate-800 hover:ring-slate-800/10 focus:border-slate-800 focus:ring-slate-800/10 w-full"
 | 
			
		||||
                @click="toggleChannelDivDropdown"
 | 
			
		||||
                :aria-expanded="showChannelDivDropdown"
 | 
			
		||||
                aria-haspopup="listbox" 
 | 
			
		||||
                role="combobox"
 | 
			
		||||
              >
 | 
			
		||||
                {{ option.label }}
 | 
			
		||||
              </option>
 | 
			
		||||
            </select>
 | 
			
		||||
                <span>{{ currentChannelDivLabel }}</span>
 | 
			
		||||
                <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor" class="h-[1em] w-[1em] translate-x-0.5 stroke-[1.5]">
 | 
			
		||||
                  <path d="M17 8L12 3L7 8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
 | 
			
		||||
                  <path d="M17 16L12 21L7 16" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path>
 | 
			
		||||
                </svg>
 | 
			
		||||
              </button>
 | 
			
		||||
              <input readonly style="display:none" :value="currentChannelDiv" />
 | 
			
		||||
              <!-- 下拉菜单 -->
 | 
			
		||||
              <div v-if="showChannelDivDropdown" class="absolute top-full left-0 right-0 mt-1 bg-white border border-slate-200 rounded-md shadow-lg z-50">
 | 
			
		||||
                <div
 | 
			
		||||
                  v-for="option in channelDivOptions"
 | 
			
		||||
                  :key="option.value"
 | 
			
		||||
                  @click="selectChannelDiv(option.value)"
 | 
			
		||||
                  class="px-3 py-2 text-sm text-slate-700 hover:bg-slate-100 cursor-pointer"
 | 
			
		||||
                  :class="{ 'bg-slate-100': option.value === currentChannelDiv }"
 | 
			
		||||
                >
 | 
			
		||||
                  {{ option.label }}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p class="flex items-center text-xs text-slate-400">
 | 
			
		||||
              {{ currentChannelDivDescription }}
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="flex flex-row gap-2 items-center">
 | 
			
		||||
            <label class="label">
 | 
			
		||||
              <span class="label-text text-sm">捕获深度</span>
 | 
			
		||||
          <div class="flex flex-col gap-2">
 | 
			
		||||
            <label class="block text-sm font-semibold antialiased">
 | 
			
		||||
              捕获深度
 | 
			
		||||
            </label>
 | 
			
		||||
            <select
 | 
			
		||||
              v-model="captureLength"
 | 
			
		||||
              class="select select-sm select-bordered"
 | 
			
		||||
            >
 | 
			
		||||
              <option
 | 
			
		||||
                v-for="option in captureLengthOptions"
 | 
			
		||||
                :key="option.value"
 | 
			
		||||
                :value="option.value"
 | 
			
		||||
            <div class="relative w-[200px]">
 | 
			
		||||
              <button 
 | 
			
		||||
                @click="decreaseCaptureLength"
 | 
			
		||||
                class="absolute right-9 top-1 rounded-md border border-transparent p-1.5 text-center text-sm transition-all hover:bg-slate-100 focus:bg-slate-100 active:bg-slate-100 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none" 
 | 
			
		||||
                type="button"
 | 
			
		||||
                :disabled="captureLength <= CAPTURE_LENGTH_MIN"
 | 
			
		||||
              >
 | 
			
		||||
                {{ option.label }}
 | 
			
		||||
              </option>
 | 
			
		||||
            </select>
 | 
			
		||||
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
 | 
			
		||||
                  <path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
 | 
			
		||||
                </svg>
 | 
			
		||||
              </button>
 | 
			
		||||
              <input 
 | 
			
		||||
                v-model.number="captureLength"
 | 
			
		||||
                @change="handleCaptureLengthChange"
 | 
			
		||||
                type="number" 
 | 
			
		||||
                :min="CAPTURE_LENGTH_MIN"
 | 
			
		||||
                :max="CAPTURE_LENGTH_MAX"
 | 
			
		||||
                class="w-full bg-transparent placeholder:text-sm border border-slate-200 rounded-md pl-3 pr-20 py-2 transition duration-300 ease focus:outline-none focus:border-slate-400 ring ring-transparent hover:ring-slate-800/10 focus:ring-slate-800/10 hover:border-slate-800 shadow-sm focus:shadow appearance-none [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" 
 | 
			
		||||
                :placeholder="CAPTURE_LENGTH_MIN.toString()"
 | 
			
		||||
              />
 | 
			
		||||
              <button 
 | 
			
		||||
                @click="increaseCaptureLength"
 | 
			
		||||
                class="absolute right-1 top-1 rounded-md border border-transparent p-1.5 text-center text-sm transition-all hover:bg-slate-100 focus:bg-slate-100 active:bg-slate-100 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none" 
 | 
			
		||||
                type="button"
 | 
			
		||||
                :disabled="captureLength >= CAPTURE_LENGTH_MAX"
 | 
			
		||||
              >
 | 
			
		||||
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
 | 
			
		||||
                  <path d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" />
 | 
			
		||||
                </svg>
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p class="flex items-center text-xs text-slate-400">
 | 
			
		||||
              范围: {{ CAPTURE_LENGTH_MIN.toLocaleString() }} - {{ CAPTURE_LENGTH_MAX.toLocaleString() }}
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div class="flex flex-row gap-2 items-center">
 | 
			
		||||
            <label class="label">
 | 
			
		||||
              <span class="label-text text-sm">预捕获深度</span>
 | 
			
		||||
          <div class="flex flex-col gap-2">
 | 
			
		||||
            <label class="block text-sm font-semibold antialiased">
 | 
			
		||||
              预捕获深度
 | 
			
		||||
            </label>
 | 
			
		||||
            <select
 | 
			
		||||
              v-model="preCaptureLength"
 | 
			
		||||
              class="select select-sm select-bordered"
 | 
			
		||||
            >
 | 
			
		||||
              <option
 | 
			
		||||
                v-for="option in preCaptureLengthOptions"
 | 
			
		||||
                :key="option.value"
 | 
			
		||||
                :value="option.value"
 | 
			
		||||
            <div class="relative w-[200px]">
 | 
			
		||||
              <button 
 | 
			
		||||
                @click="decreasePreCaptureLength"
 | 
			
		||||
                class="absolute right-9 top-1 rounded-md border border-transparent p-1.5 text-center text-sm transition-all hover:bg-slate-100 focus:bg-slate-100 active:bg-slate-100 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none" 
 | 
			
		||||
                type="button"
 | 
			
		||||
                :disabled="preCaptureLength <= PRE_CAPTURE_LENGTH_MIN"
 | 
			
		||||
              >
 | 
			
		||||
                {{ option.label }}
 | 
			
		||||
              </option>
 | 
			
		||||
            </select>
 | 
			
		||||
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
 | 
			
		||||
                  <path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
 | 
			
		||||
                </svg>
 | 
			
		||||
              </button>
 | 
			
		||||
              <input 
 | 
			
		||||
                v-model.number="preCaptureLength"
 | 
			
		||||
                @change="handlePreCaptureLengthChange"
 | 
			
		||||
                type="number" 
 | 
			
		||||
                :min="PRE_CAPTURE_LENGTH_MIN"
 | 
			
		||||
                :max="Math.max(0, captureLength - 1)"
 | 
			
		||||
                class="w-full bg-transparent placeholder:text-sm border border-slate-200 rounded-md pl-3 pr-20 py-2 transition duration-300 ease focus:outline-none focus:border-slate-400 ring ring-transparent hover:ring-slate-800/10 focus:ring-slate-800/10 hover:border-slate-800 shadow-sm focus:shadow appearance-none [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" 
 | 
			
		||||
                :placeholder="PRE_CAPTURE_LENGTH_MIN.toString()"
 | 
			
		||||
              />
 | 
			
		||||
              <button 
 | 
			
		||||
                @click="increasePreCaptureLength"
 | 
			
		||||
                class="absolute right-1 top-1 rounded-md border border-transparent p-1.5 text-center text-sm transition-all hover:bg-slate-100 focus:bg-slate-100 active:bg-slate-100 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none" 
 | 
			
		||||
                type="button"
 | 
			
		||||
                :disabled="preCaptureLength >= Math.max(0, captureLength - 1)"
 | 
			
		||||
              >
 | 
			
		||||
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
 | 
			
		||||
                  <path d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" />
 | 
			
		||||
                </svg>
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p class="flex items-center text-xs text-slate-400">
 | 
			
		||||
              范围: {{ PRE_CAPTURE_LENGTH_MIN }} - {{ Math.max(0, captureLength - 1) }}
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="flex flex-col gap-2">
 | 
			
		||||
            <label class="block text-sm font-semibold antialiased">
 | 
			
		||||
              重置配置
 | 
			
		||||
            </label>
 | 
			
		||||
            <div class="relative w-[200px]">
 | 
			
		||||
              <button 
 | 
			
		||||
                @click="resetConfiguration"
 | 
			
		||||
                class="w-10 h-10 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="重置配置"
 | 
			
		||||
              >
 | 
			
		||||
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" class="w-4 h-4">
 | 
			
		||||
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
 | 
			
		||||
                </svg>
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p class="flex items-center text-xs text-slate-400">
 | 
			
		||||
              恢复所有设置到默认值
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- 右侧:操作按钮 -->
 | 
			
		||||
        <div class="flex flex-row gap-2">
 | 
			
		||||
          <button @click="resetConfiguration" class="btn btn-outline btn-sm">
 | 
			
		||||
            重置配置
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <!-- 通道列表 -->
 | 
			
		||||
@@ -177,6 +270,7 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed, ref, onMounted, onUnmounted } from "vue";
 | 
			
		||||
import { useRequiredInjection } from "@/utils/Common";
 | 
			
		||||
import { useLogicAnalyzerState } from "./LogicAnalyzerManager";
 | 
			
		||||
 | 
			
		||||
@@ -193,10 +287,121 @@ const {
 | 
			
		||||
  operators,
 | 
			
		||||
  signalValues,
 | 
			
		||||
  channelDivOptions,
 | 
			
		||||
  captureLengthOptions,
 | 
			
		||||
  preCaptureLengthOptions,
 | 
			
		||||
  CAPTURE_LENGTH_MIN,
 | 
			
		||||
  CAPTURE_LENGTH_MAX,
 | 
			
		||||
  PRE_CAPTURE_LENGTH_MIN,
 | 
			
		||||
  validateCaptureLength,
 | 
			
		||||
  validatePreCaptureLength,
 | 
			
		||||
  setCaptureLength,
 | 
			
		||||
  setPreCaptureLength,
 | 
			
		||||
  setChannelDiv,
 | 
			
		||||
  setGlobalMode,
 | 
			
		||||
  resetConfiguration,
 | 
			
		||||
} = useRequiredInjection(useLogicAnalyzerState);
 | 
			
		||||
 | 
			
		||||
// 下拉菜单状态
 | 
			
		||||
const showGlobalModeDropdown = ref(false);
 | 
			
		||||
const showChannelDivDropdown = ref(false);
 | 
			
		||||
 | 
			
		||||
// 处理捕获深度变化
 | 
			
		||||
const handleCaptureLengthChange = () => {
 | 
			
		||||
  setCaptureLength(captureLength.value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 处理预捕获深度变化
 | 
			
		||||
const handlePreCaptureLengthChange = () => {
 | 
			
		||||
  setPreCaptureLength(preCaptureLength.value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 增加捕获深度
 | 
			
		||||
const increaseCaptureLength = () => {
 | 
			
		||||
  const newValue = Math.min(captureLength.value + 1024, CAPTURE_LENGTH_MAX);
 | 
			
		||||
  setCaptureLength(newValue);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 减少捕获深度
 | 
			
		||||
const decreaseCaptureLength = () => {
 | 
			
		||||
  const newValue = Math.max(captureLength.value - 1024, CAPTURE_LENGTH_MIN);
 | 
			
		||||
  setCaptureLength(newValue);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 增加预捕获深度
 | 
			
		||||
const increasePreCaptureLength = () => {
 | 
			
		||||
  const maxValue = Math.max(0, captureLength.value - 1);
 | 
			
		||||
  const newValue = Math.min(preCaptureLength.value + 64, maxValue);
 | 
			
		||||
  setPreCaptureLength(newValue);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 减少预捕获深度
 | 
			
		||||
const decreasePreCaptureLength = () => {
 | 
			
		||||
  const newValue = Math.max(preCaptureLength.value - 64, PRE_CAPTURE_LENGTH_MIN);
 | 
			
		||||
  setPreCaptureLength(newValue);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 计算属性:获取当前全局模式的标签
 | 
			
		||||
const currentGlobalModeLabel = computed(() => {
 | 
			
		||||
  const mode = globalModes.find(m => m.value === currentGlobalMode.value);
 | 
			
		||||
  return mode ? mode.label : '';
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 计算属性:获取当前全局模式的描述
 | 
			
		||||
const currentGlobalModeDescription = computed(() => {
 | 
			
		||||
  const mode = globalModes.find(m => m.value === currentGlobalMode.value);
 | 
			
		||||
  return mode ? mode.description : '';
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 计算属性:获取当前通道组的标签
 | 
			
		||||
const currentChannelDivLabel = computed(() => {
 | 
			
		||||
  const option = channelDivOptions.find(opt => opt.value === currentChannelDiv.value);
 | 
			
		||||
  return option ? option.label : '';
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 计算属性:获取当前通道组的描述
 | 
			
		||||
const currentChannelDivDescription = computed(() => {
 | 
			
		||||
  const option = channelDivOptions.find(opt => opt.value === currentChannelDiv.value);
 | 
			
		||||
  return option ? option.description : '';
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 全局模式下拉菜单相关函数
 | 
			
		||||
const toggleGlobalModeDropdown = () => {
 | 
			
		||||
  showGlobalModeDropdown.value = !showGlobalModeDropdown.value;
 | 
			
		||||
  if (showGlobalModeDropdown.value) {
 | 
			
		||||
    showChannelDivDropdown.value = false;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const selectGlobalMode = (mode: any) => {
 | 
			
		||||
  setGlobalMode(mode);
 | 
			
		||||
  showGlobalModeDropdown.value = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 通道组下拉菜单相关函数
 | 
			
		||||
const toggleChannelDivDropdown = () => {
 | 
			
		||||
  showChannelDivDropdown.value = !showChannelDivDropdown.value;
 | 
			
		||||
  if (showChannelDivDropdown.value) {
 | 
			
		||||
    showGlobalModeDropdown.value = false;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const selectChannelDiv = (value: number) => {
 | 
			
		||||
  setChannelDiv(value);
 | 
			
		||||
  showChannelDivDropdown.value = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 点击其他地方关闭下拉菜单
 | 
			
		||||
const handleClickOutside = (event: MouseEvent) => {
 | 
			
		||||
  const target = event.target as HTMLElement;
 | 
			
		||||
  if (!target.closest('.relative')) {
 | 
			
		||||
    showGlobalModeDropdown.value = false;
 | 
			
		||||
    showChannelDivDropdown.value = false;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  document.addEventListener('click', handleClickOutside);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
  document.removeEventListener('click', handleClickOutside);
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -61,13 +61,6 @@
 | 
			
		||||
              <Settings class="w-5 h-5" />
 | 
			
		||||
              触发设置
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 配置摘要 -->
 | 
			
		||||
            <div class="flex items-center gap-4 text-sm text-gray-500">
 | 
			
		||||
              <span>{{ analyzer.enabledChannelCount.value }}/32 通道</span>
 | 
			
		||||
              <span>捕获: {{ analyzer.captureLength.value }}</span>
 | 
			
		||||
              <span>预捕获: {{ analyzer.preCaptureLength.value }}</span>
 | 
			
		||||
              <span>{{ analyzer.globalModes.find(m => m.value === analyzer.currentGlobalMode.value)?.label || '未知' }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="flex items-center gap-4">
 | 
			
		||||
            <!-- 状态指示 -->
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user