fix: remote update failed and template not found
This commit is contained in:
		@@ -32,8 +32,10 @@ build-server self-contained=isSelfContained: _show-dir
 | 
			
		||||
  rsync -avz --delete ../wwwroot/ ./bin/Release/net9.0/linux-x64/publish/wwwroot/
 | 
			
		||||
  rsync -avz --delete ../wwwroot/ ./bin/Release/net9.0/win-x64/publish/wwwroot/
 | 
			
		||||
 | 
			
		||||
run: run-server
 | 
			
		||||
 | 
			
		||||
run-server: (build-server "true")
 | 
			
		||||
  exec ./server/bin/Release/net9.0/linux-x64/publish/server
 | 
			
		||||
  ./server/bin/Release/net9.0/linux-x64/publish/server
 | 
			
		||||
 | 
			
		||||
run-web:
 | 
			
		||||
  npm run build
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
    "preview": "vite preview",
 | 
			
		||||
    "build-only": "vite build",
 | 
			
		||||
    "type-check": "vue-tsc --build",
 | 
			
		||||
    "pregen-api": "cd server && dotnet run &",
 | 
			
		||||
    "pregen-api": "cd server && dotnet run --property:Configuration=Release &",
 | 
			
		||||
    "gen-api": "npx nswag openapi2tsclient /input:http://localhost:5000/swagger/v1/swagger.json /output:src/APIClient.ts",
 | 
			
		||||
    "postgen-api": "pkill server"
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -94,13 +94,31 @@ try
 | 
			
		||||
        logger.Info($"Use Static Files : {Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")}");
 | 
			
		||||
        app.UseDefaultFiles();
 | 
			
		||||
        app.UseStaticFiles(); // Serves files from wwwroot by default
 | 
			
		||||
        // Assets Files
 | 
			
		||||
        app.UseStaticFiles(new StaticFileOptions
 | 
			
		||||
        {
 | 
			
		||||
            FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "assets")),
 | 
			
		||||
            RequestPath = "/assets"
 | 
			
		||||
        });
 | 
			
		||||
        // Public Files
 | 
			
		||||
        app.UseStaticFiles(new StaticFileOptions
 | 
			
		||||
        {
 | 
			
		||||
            FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "EquipmentTemplates")),
 | 
			
		||||
            RequestPath = "/public/EquipmentTemplates"
 | 
			
		||||
        });
 | 
			
		||||
        // Log Files
 | 
			
		||||
        if (!Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), "log")))
 | 
			
		||||
        {
 | 
			
		||||
            Directory.CreateDirectory(Path.Combine(Directory.GetCurrentDirectory(), "log"));
 | 
			
		||||
        }
 | 
			
		||||
        app.UseStaticFiles(new StaticFileOptions
 | 
			
		||||
        {
 | 
			
		||||
            FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "log")),
 | 
			
		||||
            RequestPath = "/log"
 | 
			
		||||
        });
 | 
			
		||||
        app.MapFallbackToFile("index.html");
 | 
			
		||||
    }
 | 
			
		||||
    // Add logs
 | 
			
		||||
    app.UseHttpsRedirection();
 | 
			
		||||
    app.UseRouting();
 | 
			
		||||
    app.UseCors();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
using System.Net;
 | 
			
		||||
using DotNext;
 | 
			
		||||
namespace RemoteUpdate;
 | 
			
		||||
namespace RemoteUpdateClient;
 | 
			
		||||
 | 
			
		||||
static class RemoteUpdateClientAddr
 | 
			
		||||
static class RemoteUpdaterAddr
 | 
			
		||||
{
 | 
			
		||||
    public const UInt32 Base = 0x20_00_00_00;
 | 
			
		||||
 | 
			
		||||
@@ -91,7 +91,7 @@ static class FlashAddr
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// [TODO:description]
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class RemoteUpdateClient
 | 
			
		||||
public class RemoteUpdater
 | 
			
		||||
{
 | 
			
		||||
    private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
@@ -112,7 +112,7 @@ public class RemoteUpdateClient
 | 
			
		||||
    /// <param name="timeout">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="timeoutForWait">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    public RemoteUpdateClient(string address, int port, int timeout = 2000, int timeoutForWait = 60 * 1000)
 | 
			
		||||
    public RemoteUpdater(string address, int port, int timeout = 2000, int timeoutForWait = 60 * 1000)
 | 
			
		||||
    {
 | 
			
		||||
        if (timeout < 0)
 | 
			
		||||
            throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
 | 
			
		||||
@@ -142,7 +142,7 @@ public class RemoteUpdateClient
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            var ret = await UDPClientPool.WriteAddr(
 | 
			
		||||
                this.ep, RemoteUpdateClientAddr.WriteCtrl,
 | 
			
		||||
                this.ep, RemoteUpdaterAddr.WriteCtrl,
 | 
			
		||||
                Convert.ToUInt32((writeSectorNum << 16) | (1 << 15) | Convert.ToInt32(flashAddr / 4096)), this.timeout);
 | 
			
		||||
            if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
            if (!ret.Value) return new(new Exception("Enable write flash failed"));
 | 
			
		||||
@@ -150,7 +150,7 @@ public class RemoteUpdateClient
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            var ret = await UDPClientPool.ReadAddrWithWait(
 | 
			
		||||
                this.ep, RemoteUpdateClientAddr.WriteSign,
 | 
			
		||||
                this.ep, RemoteUpdaterAddr.WriteSign,
 | 
			
		||||
                0x00_00_00_01, 0x00_00_00_01, this.timeoutForWait);
 | 
			
		||||
            if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
            if (!ret.Value) return new(new Exception(
 | 
			
		||||
@@ -158,14 +158,14 @@ public class RemoteUpdateClient
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            var ret = await UDPClientPool.WriteAddr(this.ep, RemoteUpdateClientAddr.WriteFIFO, bytesData, this.timeout);
 | 
			
		||||
            var ret = await UDPClientPool.WriteAddr(this.ep, RemoteUpdaterAddr.WriteFIFO, bytesData, this.timeout);
 | 
			
		||||
            if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
            if (!ret.Value) return new(new Exception("Send data to flash failed"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            var ret = await UDPClientPool.ReadAddrWithWait(
 | 
			
		||||
                this.ep, RemoteUpdateClientAddr.WriteSign,
 | 
			
		||||
                this.ep, RemoteUpdaterAddr.WriteSign,
 | 
			
		||||
                0x00_00_01_00, 0x00_00_01_00, this.timeoutForWait);
 | 
			
		||||
            if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
            return ret.Value;
 | 
			
		||||
@@ -314,14 +314,14 @@ public class RemoteUpdateClient
 | 
			
		||||
    private async ValueTask<Result<bool>> CheckBitstreamCRC(int bitstreamNum, int bitstreamLen, UInt32 checkSum)
 | 
			
		||||
    {
 | 
			
		||||
        {
 | 
			
		||||
            var ret = await UDPClientPool.WriteAddr(this.ep, RemoteUpdateClientAddr.ReadCtrl2, 0x00_00_00_00, this.timeout);
 | 
			
		||||
            var ret = await UDPClientPool.WriteAddr(this.ep, RemoteUpdaterAddr.ReadCtrl2, 0x00_00_00_00, this.timeout);
 | 
			
		||||
            if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
            if (!ret.Value) return new(new Exception("Write read control 2 failed"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            var ret = await UDPClientPool.WriteAddr(
 | 
			
		||||
                this.ep, RemoteUpdateClientAddr.ReadCtrl1,
 | 
			
		||||
                this.ep, RemoteUpdaterAddr.ReadCtrl1,
 | 
			
		||||
                Convert.ToUInt32((bitstreamLen << 16) | (1 << 15) | Convert.ToInt32(FlashAddr.Bitstream[bitstreamNum] / 4096)),
 | 
			
		||||
                this.timeout);
 | 
			
		||||
            if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
@@ -330,7 +330,7 @@ public class RemoteUpdateClient
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            var ret = await UDPClientPool.ReadAddrWithWait(
 | 
			
		||||
                this.ep, RemoteUpdateClientAddr.ReadSign,
 | 
			
		||||
                this.ep, RemoteUpdaterAddr.ReadSign,
 | 
			
		||||
                0x00_00_01_00, 0x00_00_01_00, this.timeoutForWait);
 | 
			
		||||
            if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
            if (!ret.Value) return new(new Exception(
 | 
			
		||||
@@ -338,7 +338,7 @@ public class RemoteUpdateClient
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            var ret = await UDPClientPool.ReadAddr(this.ep, RemoteUpdateClientAddr.ReadCRC, this.timeout);
 | 
			
		||||
            var ret = await UDPClientPool.ReadAddr(this.ep, RemoteUpdaterAddr.ReadCRC, this.timeout);
 | 
			
		||||
            if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
 | 
			
		||||
            var bytes = ret.Value.Options.Data;
 | 
			
		||||
@@ -368,7 +368,7 @@ public class RemoteUpdateClient
 | 
			
		||||
                $"Bitsteam num should be 0 ~ 3 for HotRest, but given {bitstreamNum}", nameof(bitstreamNum)));
 | 
			
		||||
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(
 | 
			
		||||
            this.ep, RemoteUpdateClientAddr.HotResetCtrl,
 | 
			
		||||
            this.ep, RemoteUpdaterAddr.HotResetCtrl,
 | 
			
		||||
            ((FlashAddr.Bitstream[bitstreamNum] << 8) | 1), this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
        return ret.Value;
 | 
			
		||||
@@ -542,7 +542,7 @@ public class RemoteUpdateClient
 | 
			
		||||
        logger.Trace("Clear udp data finished");
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            var ret = await UDPClientPool.ReadAddr(this.ep, RemoteUpdateClientAddr.Version, this.timeout);
 | 
			
		||||
            var ret = await UDPClientPool.ReadAddr(this.ep, RemoteUpdaterAddr.Version, this.timeout);
 | 
			
		||||
            if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
 | 
			
		||||
            var retData = ret.Value.Options.Data;
 | 
			
		||||
@@ -1394,7 +1394,6 @@ export interface ISystemException extends IException {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class ArgumentException extends SystemException implements IArgumentException {
 | 
			
		||||
    message?: string;
 | 
			
		||||
    paramName?: string | undefined;
 | 
			
		||||
 | 
			
		||||
    constructor(data?: IArgumentException) {
 | 
			
		||||
@@ -1471,4 +1470,4 @@ function throwException(message: string, status: number, response: string, heade
 | 
			
		||||
        throw result;
 | 
			
		||||
    else
 | 
			
		||||
        throw new ApiException(message, status, response, headers, null);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,13 @@
 | 
			
		||||
      <input id="component-drawer" type="checkbox" class="drawer-toggle" v-model="showComponentsMenu" />
 | 
			
		||||
      <div class="drawer-side">
 | 
			
		||||
        <label for="component-drawer" aria-label="close sidebar" class="drawer-overlay !bg-opacity-50"></label>
 | 
			
		||||
        <div class="menu p-0 w-[460px] min-h-full bg-base-100 shadow-xl flex flex-col">          <!-- 菜单头部 -->
 | 
			
		||||
        <div class="menu p-0 w-[460px] min-h-full bg-base-100 shadow-xl flex flex-col">
 | 
			
		||||
          <!-- 菜单头部 -->
 | 
			
		||||
          <div class="p-6 border-b border-base-300 flex justify-between items-center">
 | 
			
		||||
            <h3 class="text-xl font-bold text-primary flex items-center gap-2">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-primary">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
 | 
			
		||||
                stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
 | 
			
		||||
                class="text-primary">
 | 
			
		||||
                <circle cx="12" cy="12" r="10"></circle>
 | 
			
		||||
                <path d="M12 8v8"></path>
 | 
			
		||||
                <path d="M8 12h8"></path>
 | 
			
		||||
@@ -16,55 +19,53 @@
 | 
			
		||||
              添加元器件
 | 
			
		||||
            </h3>
 | 
			
		||||
            <label for="component-drawer" class="btn btn-ghost btn-sm btn-circle" @click="closeMenu">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
 | 
			
		||||
                stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
 | 
			
		||||
                <line x1="18" y1="6" x2="6" y2="18"></line>
 | 
			
		||||
                <line x1="6" y1="6" x2="18" y2="18"></line>
 | 
			
		||||
              </svg>
 | 
			
		||||
            </label>
 | 
			
		||||
          </div>
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          <!-- 导航栏 -->
 | 
			
		||||
          <div class="tabs tabs-boxed bg-base-200 mx-6 mt-4 rounded-box">
 | 
			
		||||
            <a class="tab" :class="{ 'tab-active': activeTab === 'components' }" @click="activeTab = 'components'">元器件</a>
 | 
			
		||||
            <a class="tab" :class="{ 'tab-active': activeTab === 'components' }"
 | 
			
		||||
              @click="activeTab = 'components'">元器件</a>
 | 
			
		||||
            <a class="tab" :class="{ 'tab-active': activeTab === 'templates' }" @click="activeTab = 'templates'">模板</a>
 | 
			
		||||
            <a class="tab" :class="{ 'tab-active': activeTab === 'virtual' }" @click="activeTab = 'virtual'">虚拟外设</a>
 | 
			
		||||
          </div>
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          <!-- 搜索框 -->
 | 
			
		||||
          <div class="px-6 py-4 border-b border-base-300">
 | 
			
		||||
            <div class="join w-full">
 | 
			
		||||
              <div class="join-item flex-1 relative">
 | 
			
		||||
                <input
 | 
			
		||||
                  type="text"
 | 
			
		||||
                  placeholder="搜索..."
 | 
			
		||||
                  class="input input-bordered input-sm w-full pl-10"
 | 
			
		||||
                  v-model="searchQuery"
 | 
			
		||||
                  @keyup.enter="searchComponents"
 | 
			
		||||
                />
 | 
			
		||||
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="absolute left-3 top-1/2 -translate-y-1/2 text-base-content opacity-60">
 | 
			
		||||
                <input type="text" placeholder="搜索..." class="input input-bordered input-sm w-full pl-10"
 | 
			
		||||
                  v-model="searchQuery" @keyup.enter="searchComponents" />
 | 
			
		||||
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
 | 
			
		||||
                  stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
 | 
			
		||||
                  class="absolute left-3 top-1/2 -translate-y-1/2 text-base-content opacity-60">
 | 
			
		||||
                  <circle cx="11" cy="11" r="8"></circle>
 | 
			
		||||
                  <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
 | 
			
		||||
                </svg>
 | 
			
		||||
              </div>
 | 
			
		||||
              <button class="btn btn-sm join-item" @click="searchComponents">搜索</button>
 | 
			
		||||
              <button class="btn btn-sm join-item" @click="searchComponents">
 | 
			
		||||
                搜索
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          <!-- 元器件列表 (组件选项卡) -->
 | 
			
		||||
          <div v-if="activeTab === 'components'" class="px-6 py-4 overflow-auto flex-1">
 | 
			
		||||
            <div v-if="filteredComponents.length > 0" class="grid grid-cols-2 gap-4">
 | 
			
		||||
              <div v-for="(component, index) in filteredComponents" :key="index"
 | 
			
		||||
                  class="card bg-base-200 hover:bg-base-300 transition-all duration-300 hover:shadow-md cursor-pointer"
 | 
			
		||||
                  @click="addComponent(component)">
 | 
			
		||||
                class="card bg-base-200 hover:bg-base-300 transition-all duration-300 hover:shadow-md cursor-pointer"
 | 
			
		||||
                @click="addComponent(component)">
 | 
			
		||||
                <div class="card-body p-3 items-center text-center">
 | 
			
		||||
                  <div class="bg-base-100 rounded-lg w-full h-[90px] flex items-center justify-center overflow-hidden p-2">
 | 
			
		||||
                  <div
 | 
			
		||||
                    class="bg-base-100 rounded-lg w-full h-[90px] flex items-center justify-center overflow-hidden p-2">
 | 
			
		||||
                    <!-- 直接使用组件作为预览 -->
 | 
			
		||||
                    <component 
 | 
			
		||||
                      v-if="componentModules[component.type]"
 | 
			
		||||
                      :is="componentModules[component.type].default"
 | 
			
		||||
                      class="component-preview"
 | 
			
		||||
                      :size="getPreviewSize(component.type)"
 | 
			
		||||
                    />
 | 
			
		||||
                    <component v-if="componentModules[component.type]" :is="componentModules[component.type].default"
 | 
			
		||||
                      class="component-preview" :size="getPreviewSize(component.type)" />
 | 
			
		||||
                    <!-- 加载中状态 -->
 | 
			
		||||
                    <span v-else class="text-xs text-gray-400">加载中...</span>
 | 
			
		||||
                  </div>
 | 
			
		||||
@@ -75,7 +76,9 @@
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 无搜索结果 -->
 | 
			
		||||
            <div v-else class="py-16 text-center">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="mx-auto text-base-300 mb-3">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none"
 | 
			
		||||
                stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"
 | 
			
		||||
                class="mx-auto text-base-300 mb-3">
 | 
			
		||||
                <circle cx="11" cy="11" r="8"></circle>
 | 
			
		||||
                <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
 | 
			
		||||
                <line x1="8" y1="11" x2="14" y2="11"></line>
 | 
			
		||||
@@ -86,25 +89,31 @@
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          <!-- 模板列表 (模板选项卡) -->
 | 
			
		||||
          <div v-if="activeTab === 'templates'" class="px-6 py-4 overflow-auto flex-1">
 | 
			
		||||
            <div v-if="filteredTemplates.length > 0" class="grid grid-cols-2 gap-4">
 | 
			
		||||
              <div v-for="(template, index) in filteredTemplates" :key="index"
 | 
			
		||||
                  class="card bg-base-200 hover:bg-base-300 transition-all duration-300 hover:shadow-md cursor-pointer"
 | 
			
		||||
                  @click="addTemplate(template)">
 | 
			
		||||
                class="card bg-base-200 hover:bg-base-300 transition-all duration-300 hover:shadow-md cursor-pointer"
 | 
			
		||||
                @click="addTemplate(template)">
 | 
			
		||||
                <div class="card-body p-3 items-center text-center">
 | 
			
		||||
                  <div class="bg-base-100 rounded-lg w-full h-[90px] flex items-center justify-center overflow-hidden p-2">
 | 
			
		||||
                    <img :src="template.thumbnailUrl || '/placeholder-template.png'" alt="Template thumbnail" class="max-h-full max-w-full object-contain" />
 | 
			
		||||
                  <div
 | 
			
		||||
                    class="bg-base-100 rounded-lg w-full h-[90px] flex items-center justify-center overflow-hidden p-2">
 | 
			
		||||
                    <img :src="template.thumbnailUrl || '/placeholder-template.png'
 | 
			
		||||
                      " alt="Template thumbnail" class="max-h-full max-w-full object-contain" />
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <h3 class="card-title text-sm mt-2">{{ template.name }}</h3>
 | 
			
		||||
                  <p class="text-xs opacity-70">{{ template.description || '模板' }}</p>
 | 
			
		||||
                  <p class="text-xs opacity-70">
 | 
			
		||||
                    {{ template.description || "模板" }}
 | 
			
		||||
                  </p>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 无搜索结果 -->
 | 
			
		||||
            <div v-else class="py-16 text-center">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="mx-auto text-base-300 mb-3">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none"
 | 
			
		||||
                stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"
 | 
			
		||||
                class="mx-auto text-base-300 mb-3">
 | 
			
		||||
                <circle cx="11" cy="11" r="8"></circle>
 | 
			
		||||
                <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
 | 
			
		||||
                <line x1="8" y1="11" x2="14" y2="11"></line>
 | 
			
		||||
@@ -115,21 +124,18 @@
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
            <!-- 虚拟外设列表 (虚拟外设选项卡) -->
 | 
			
		||||
          <!-- 虚拟外设列表 (虚拟外设选项卡) -->
 | 
			
		||||
          <div v-if="activeTab === 'virtual'" class="px-6 py-4 overflow-auto flex-1">
 | 
			
		||||
            <div v-if="filteredVirtualDevices.length > 0" class="grid grid-cols-2 gap-4">
 | 
			
		||||
              <div v-for="(device, index) in filteredVirtualDevices" :key="index"
 | 
			
		||||
                  class="card bg-base-200 hover:bg-base-300 transition-all duration-300 hover:shadow-md cursor-pointer"
 | 
			
		||||
                  @click="addComponent(device)">
 | 
			
		||||
                class="card bg-base-200 hover:bg-base-300 transition-all duration-300 hover:shadow-md cursor-pointer"
 | 
			
		||||
                @click="addComponent(device)">
 | 
			
		||||
                <div class="card-body p-3 items-center text-center">
 | 
			
		||||
                  <div class="bg-base-100 rounded-lg w-full h-[90px] flex items-center justify-center overflow-hidden p-2">
 | 
			
		||||
                  <div
 | 
			
		||||
                    class="bg-base-100 rounded-lg w-full h-[90px] flex items-center justify-center overflow-hidden p-2">
 | 
			
		||||
                    <!-- 直接使用组件作为预览 -->
 | 
			
		||||
                    <component 
 | 
			
		||||
                      v-if="componentModules[device.type]"
 | 
			
		||||
                      :is="componentModules[device.type].default"
 | 
			
		||||
                      class="component-preview"
 | 
			
		||||
                      :size="getPreviewSize(device.type)"
 | 
			
		||||
                    />
 | 
			
		||||
                    <component v-if="componentModules[device.type]" :is="componentModules[device.type].default"
 | 
			
		||||
                      class="component-preview" :size="getPreviewSize(device.type)" />
 | 
			
		||||
                    <!-- 加载中状态 -->
 | 
			
		||||
                    <span v-else class="text-xs text-gray-400">加载中...</span>
 | 
			
		||||
                  </div>
 | 
			
		||||
@@ -140,7 +146,9 @@
 | 
			
		||||
            </div>
 | 
			
		||||
            <!-- 无搜索结果 -->
 | 
			
		||||
            <div v-else class="py-16 text-center">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="mx-auto text-base-300 mb-3">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none"
 | 
			
		||||
                stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"
 | 
			
		||||
                class="mx-auto text-base-300 mb-3">
 | 
			
		||||
                <circle cx="11" cy="11" r="8"></circle>
 | 
			
		||||
                <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
 | 
			
		||||
                <line x1="8" y1="11" x2="14" y2="11"></line>
 | 
			
		||||
@@ -151,11 +159,12 @@
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          
 | 
			
		||||
 | 
			
		||||
          <!-- 底部操作区 -->
 | 
			
		||||
          <div class="p-4 border-t border-base-300 bg-base-200 flex justify-between">
 | 
			
		||||
            <label for="component-drawer" class="btn btn-sm btn-ghost" @click="closeMenu">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
 | 
			
		||||
                stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1">
 | 
			
		||||
                <path d="M19 12H5M12 19l-7-7 7-7"></path>
 | 
			
		||||
              </svg>
 | 
			
		||||
              返回
 | 
			
		||||
@@ -171,7 +180,8 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, computed, shallowRef, onMounted } from 'vue';
 | 
			
		||||
import { ref, computed, shallowRef, onMounted } from "vue";
 | 
			
		||||
import motherboardSvg from "../components/equipments/svg/motherboard.svg";
 | 
			
		||||
 | 
			
		||||
// Props 定义
 | 
			
		||||
interface Props {
 | 
			
		||||
@@ -181,51 +191,54 @@ interface Props {
 | 
			
		||||
const props = defineProps<Props>();
 | 
			
		||||
 | 
			
		||||
// 定义组件发出的事件
 | 
			
		||||
const emit = defineEmits(['close', 'add-component', 'add-template', 'update:open']);
 | 
			
		||||
const emit = defineEmits([
 | 
			
		||||
  "close",
 | 
			
		||||
  "add-component",
 | 
			
		||||
  "add-template",
 | 
			
		||||
  "update:open",
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
// 当前激活的选项卡
 | 
			
		||||
const activeTab = ref('components');
 | 
			
		||||
const activeTab = ref("components");
 | 
			
		||||
 | 
			
		||||
// --- 搜索功能 ---
 | 
			
		||||
const searchQuery = ref('');
 | 
			
		||||
const searchQuery = ref("");
 | 
			
		||||
 | 
			
		||||
// --- 可用元器件列表 ---
 | 
			
		||||
const availableComponents = [
 | 
			
		||||
  { type: 'MechanicalButton', name: '机械按钮' },
 | 
			
		||||
  { type: 'Switch', name: '开关' },
 | 
			
		||||
  { type: 'Pin', name: '引脚' },
 | 
			
		||||
  { type: 'SMT_LED', name: '贴片LED' },
 | 
			
		||||
  { type: 'SevenSegmentDisplay', name: '数码管' },
 | 
			
		||||
  { type: 'HDMI', name: 'HDMI接口' },
 | 
			
		||||
  { type: 'DDR', name: 'DDR内存' },
 | 
			
		||||
  { type: 'ETH', name: '以太网接口' },
 | 
			
		||||
  { type: 'SD', name: 'SD卡插槽' },
 | 
			
		||||
  { type: 'SFP', name: 'SFP光纤模块' },
 | 
			
		||||
  { type: 'SMA', name: 'SMA连接器' },
 | 
			
		||||
  { type: 'MotherBoard', name: '主板' },
 | 
			
		||||
  { type: 'PG2L100H_FBG676', name: 'PG2L100H FBG676芯片' }
 | 
			
		||||
  { type: "MechanicalButton", name: "机械按钮" },
 | 
			
		||||
  { type: "Switch", name: "开关" },
 | 
			
		||||
  { type: "Pin", name: "引脚" },
 | 
			
		||||
  { type: "SMT_LED", name: "贴片LED" },
 | 
			
		||||
  { type: "SevenSegmentDisplay", name: "数码管" },
 | 
			
		||||
  { type: "HDMI", name: "HDMI接口" },
 | 
			
		||||
  { type: "DDR", name: "DDR内存" },
 | 
			
		||||
  { type: "ETH", name: "以太网接口" },
 | 
			
		||||
  { type: "SD", name: "SD卡插槽" },
 | 
			
		||||
  { type: "SFP", name: "SFP光纤模块" },
 | 
			
		||||
  { type: "SMA", name: "SMA连接器" },
 | 
			
		||||
  { type: "MotherBoard", name: "主板" },
 | 
			
		||||
  { type: "PG2L100H_FBG676", name: "PG2L100H FBG676芯片" },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// --- 可用虚拟外设列表 ---
 | 
			
		||||
const availableVirtualDevices = [
 | 
			
		||||
  { type: 'DDS', name: '信号发生器' }
 | 
			
		||||
];
 | 
			
		||||
const availableVirtualDevices = [{ type: "DDS", name: "信号发生器" }];
 | 
			
		||||
 | 
			
		||||
// --- 可用模板列表 ---
 | 
			
		||||
const availableTemplates = ref([
 | 
			
		||||
  { 
 | 
			
		||||
    name: 'PG2L100H 基础开发板', 
 | 
			
		||||
    id: 'PG2L100H_Pango100pro',
 | 
			
		||||
    description: '包含主板和两个LED的基本设置',
 | 
			
		||||
    path: '/src/components/equipments/templates/PG2L100H_Pango100pro.json',
 | 
			
		||||
    thumbnailUrl: '/src/components/equipments/svg/motherboard.svg'
 | 
			
		||||
  }
 | 
			
		||||
  {
 | 
			
		||||
    name: "PG2L100H 基础开发板",
 | 
			
		||||
    id: "PG2L100H_Pango100pro",
 | 
			
		||||
    description: "包含主板和两个LED的基本设置",
 | 
			
		||||
    path: "/public/EquipmentTemplates/PG2L100H_Pango100pro.json",
 | 
			
		||||
    thumbnailUrl: motherboardSvg,
 | 
			
		||||
  },
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
// 显示/隐藏组件菜单
 | 
			
		||||
const showComponentsMenu = computed({
 | 
			
		||||
  get: () => props.open,
 | 
			
		||||
  set: (value) => emit('update:open', value)
 | 
			
		||||
  set: (value) => emit("update:open", value),
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 组件模块缓存
 | 
			
		||||
@@ -237,13 +250,13 @@ async function loadComponentModule(type: string) {
 | 
			
		||||
    try {
 | 
			
		||||
      // 假设组件都在 src/components/equipments/ 目录下,且文件名与 type 相同
 | 
			
		||||
      const module = await import(`../components/equipments/${type}.vue`);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      // 将模块添加到缓存中
 | 
			
		||||
      componentModules.value = {
 | 
			
		||||
        ...componentModules.value,
 | 
			
		||||
        [type]: module
 | 
			
		||||
        [type]: module,
 | 
			
		||||
      };
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      console.log(`Loaded module for ${type}:`, module);
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error(`Failed to load component module ${type}:`, error);
 | 
			
		||||
@@ -263,7 +276,7 @@ async function preloadComponentModules() {
 | 
			
		||||
      console.error(`Failed to preload component ${component.type}:`, error);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  // 加载虚拟外设组件
 | 
			
		||||
  for (const device of availableVirtualDevices) {
 | 
			
		||||
    try {
 | 
			
		||||
@@ -278,21 +291,21 @@ async function preloadComponentModules() {
 | 
			
		||||
function getPreviewSize(componentType: string): number {
 | 
			
		||||
  // 根据组件类型返回适当的预览尺寸
 | 
			
		||||
  const previewSizes: Record<string, number> = {
 | 
			
		||||
    'MechanicalButton': 0.4,  // 按钮较大,需要更小尺寸
 | 
			
		||||
    'Switch': 0.35,           // 开关较大,需要更小尺寸
 | 
			
		||||
    'Pin': 0.8,               // 引脚较小,可以大一些
 | 
			
		||||
    'SMT_LED': 0.7,           // LED可以保持适中
 | 
			
		||||
    'SevenSegmentDisplay': 0.4, // 数码管较大,需要较小尺寸
 | 
			
		||||
    'HDMI': 0.5,              // HDMI接口较大
 | 
			
		||||
    'DDR': 0.5,               // DDR内存较大
 | 
			
		||||
    'ETH': 0.5,               // 以太网接口较大
 | 
			
		||||
    'SD': 0.6,                // SD卡插槽适中
 | 
			
		||||
    'SFP': 0.4,               // SFP光纤模块较大
 | 
			
		||||
    'SMA': 0.7,               // SMA连接器可以适中
 | 
			
		||||
    'MotherBoard': 0.13,      // 主板最大,需要最小尺寸
 | 
			
		||||
    'DDS': 0.3                // 信号发生器较大,需要较小尺寸
 | 
			
		||||
    MechanicalButton: 0.4, // 按钮较大,需要更小尺寸
 | 
			
		||||
    Switch: 0.35, // 开关较大,需要更小尺寸
 | 
			
		||||
    Pin: 0.8, // 引脚较小,可以大一些
 | 
			
		||||
    SMT_LED: 0.7, // LED可以保持适中
 | 
			
		||||
    SevenSegmentDisplay: 0.4, // 数码管较大,需要较小尺寸
 | 
			
		||||
    HDMI: 0.5, // HDMI接口较大
 | 
			
		||||
    DDR: 0.5, // DDR内存较大
 | 
			
		||||
    ETH: 0.5, // 以太网接口较大
 | 
			
		||||
    SD: 0.6, // SD卡插槽适中
 | 
			
		||||
    SFP: 0.4, // SFP光纤模块较大
 | 
			
		||||
    SMA: 0.7, // SMA连接器可以适中
 | 
			
		||||
    MotherBoard: 0.13, // 主板最大,需要最小尺寸
 | 
			
		||||
    DDS: 0.3, // 信号发生器较大,需要较小尺寸
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  // 返回对应尺寸,如果没有特定配置则返回默认值0.5
 | 
			
		||||
  return previewSizes[componentType] || 0.5;
 | 
			
		||||
}
 | 
			
		||||
@@ -306,7 +319,7 @@ function searchComponents() {
 | 
			
		||||
// 关闭菜单
 | 
			
		||||
function closeMenu() {
 | 
			
		||||
  showComponentsMenu.value = false;
 | 
			
		||||
  emit('close');
 | 
			
		||||
  emit("close");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 添加新元器件
 | 
			
		||||
@@ -316,25 +329,28 @@ async function addComponent(componentTemplate: { type: string; name: string }) {
 | 
			
		||||
  let defaultProps: Record<string, any> = {};
 | 
			
		||||
 | 
			
		||||
  // 尝试直接调用组件导出的getDefaultProps方法
 | 
			
		||||
  if(moduleRef){
 | 
			
		||||
    if (typeof moduleRef.getDefaultProps === 'function') {
 | 
			
		||||
  if (moduleRef) {
 | 
			
		||||
    if (typeof moduleRef.getDefaultProps === "function") {
 | 
			
		||||
      defaultProps = moduleRef.getDefaultProps();
 | 
			
		||||
      console.log(`Got default props from ${componentTemplate.type}:`, defaultProps);
 | 
			
		||||
      console.log(
 | 
			
		||||
        `Got default props from ${componentTemplate.type}:`,
 | 
			
		||||
        defaultProps,
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      // 回退到配置文件
 | 
			
		||||
      console.log(`No getDefaultProps found for ${componentTemplate.type}`);
 | 
			
		||||
    }
 | 
			
		||||
  } else{
 | 
			
		||||
  } else {
 | 
			
		||||
    console.log(`Failed to load module for ${componentTemplate.type}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 发送添加组件事件给父组件
 | 
			
		||||
  emit('add-component', {
 | 
			
		||||
  emit("add-component", {
 | 
			
		||||
    type: componentTemplate.type,
 | 
			
		||||
    name: componentTemplate.name,
 | 
			
		||||
    props: defaultProps
 | 
			
		||||
    props: defaultProps,
 | 
			
		||||
  });
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  // 关闭菜单
 | 
			
		||||
  closeMenu();
 | 
			
		||||
}
 | 
			
		||||
@@ -347,58 +363,62 @@ async function addTemplate(template: any) {
 | 
			
		||||
    if (!response.ok) {
 | 
			
		||||
      throw new Error(`Failed to load template: ${response.statusText}`);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    const templateData = await response.json();
 | 
			
		||||
    console.log('加载模板:', templateData);
 | 
			
		||||
    
 | 
			
		||||
    console.log("加载模板:", templateData);
 | 
			
		||||
 | 
			
		||||
    // 发出事件,将模板数据传递给父组件
 | 
			
		||||
    emit('add-template', {
 | 
			
		||||
    emit("add-template", {
 | 
			
		||||
      id: template.id,
 | 
			
		||||
      name: template.name,
 | 
			
		||||
      template: templateData
 | 
			
		||||
      template: templateData,
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // 关闭菜单
 | 
			
		||||
    closeMenu();
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error('加载模板出错:', error);
 | 
			
		||||
    alert('无法加载模板文件,请检查控制台错误信息');
 | 
			
		||||
    console.error("加载模板出错:", error);
 | 
			
		||||
    alert("无法加载模板文件,请检查控制台错误信息");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 过滤后的元器件列表 (用于菜单)
 | 
			
		||||
const filteredComponents = computed(() => {
 | 
			
		||||
  if (!searchQuery.value || activeTab.value !== 'components') {
 | 
			
		||||
  if (!searchQuery.value || activeTab.value !== "components") {
 | 
			
		||||
    return availableComponents;
 | 
			
		||||
  }
 | 
			
		||||
  const query = searchQuery.value.toLowerCase();
 | 
			
		||||
  return availableComponents.filter(component =>
 | 
			
		||||
    component.name.toLowerCase().includes(query) ||
 | 
			
		||||
    component.type.toLowerCase().includes(query)
 | 
			
		||||
  return availableComponents.filter(
 | 
			
		||||
    (component) =>
 | 
			
		||||
      component.name.toLowerCase().includes(query) ||
 | 
			
		||||
      component.type.toLowerCase().includes(query),
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 过滤后的模板列表 (用于菜单)
 | 
			
		||||
const filteredTemplates = computed(() => {
 | 
			
		||||
  if (!searchQuery.value || activeTab.value !== 'templates') {
 | 
			
		||||
  if (!searchQuery.value || activeTab.value !== "templates") {
 | 
			
		||||
    return availableTemplates.value;
 | 
			
		||||
  }
 | 
			
		||||
  const query = searchQuery.value.toLowerCase();
 | 
			
		||||
  return availableTemplates.value.filter(template =>
 | 
			
		||||
    template.name.toLowerCase().includes(query) ||
 | 
			
		||||
    (template.description && template.description.toLowerCase().includes(query))
 | 
			
		||||
  return availableTemplates.value.filter(
 | 
			
		||||
    (template) =>
 | 
			
		||||
      template.name.toLowerCase().includes(query) ||
 | 
			
		||||
      (template.description &&
 | 
			
		||||
        template.description.toLowerCase().includes(query)),
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 过滤后的虚拟外设列表 (用于菜单)
 | 
			
		||||
const filteredVirtualDevices = computed(() => {
 | 
			
		||||
  if (!searchQuery.value || activeTab.value !== 'virtual') {
 | 
			
		||||
  if (!searchQuery.value || activeTab.value !== "virtual") {
 | 
			
		||||
    return availableVirtualDevices;
 | 
			
		||||
  }
 | 
			
		||||
  const query = searchQuery.value.toLowerCase();
 | 
			
		||||
  return availableVirtualDevices.filter(device =>
 | 
			
		||||
    device.name.toLowerCase().includes(query) ||
 | 
			
		||||
    device.type.toLowerCase().includes(query)
 | 
			
		||||
  return availableVirtualDevices.filter(
 | 
			
		||||
    (device) =>
 | 
			
		||||
      device.name.toLowerCase().includes(query) ||
 | 
			
		||||
      device.type.toLowerCase().includes(query),
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -427,6 +447,7 @@ onMounted(() => {
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
    transform: translateY(20px);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  to {
 | 
			
		||||
    opacity: 1;
 | 
			
		||||
    transform: translateY(0);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,12 @@
 | 
			
		||||
    <!-- Input File -->
 | 
			
		||||
    <fieldset class="fieldset w-full">
 | 
			
		||||
      <legend class="fieldset-legend text-sm">选择或拖拽上传文件</legend>
 | 
			
		||||
      <input type="file" class="file-input w-full" :value="fileInput" @change="handleFileChange" />
 | 
			
		||||
      <input
 | 
			
		||||
        type="file"
 | 
			
		||||
        ref="fileInput"
 | 
			
		||||
        class="file-input w-full"
 | 
			
		||||
        @change="handleFileChange"
 | 
			
		||||
      />
 | 
			
		||||
      <label class="fieldset-label">文件最大容量: {{ maxMemory }}MB</label>
 | 
			
		||||
    </fieldset>
 | 
			
		||||
 | 
			
		||||
@@ -20,8 +25,7 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed, ref } from "vue";
 | 
			
		||||
import { useEquipments } from "@/stores/equipments";
 | 
			
		||||
import { computed, useTemplateRef, onMounted } from "vue";
 | 
			
		||||
import { useDialogStore } from "@/stores/dialog";
 | 
			
		||||
import { isNull, isUndefined } from "lodash";
 | 
			
		||||
 | 
			
		||||
@@ -41,16 +45,19 @@ const emits = defineEmits<{
 | 
			
		||||
}>();
 | 
			
		||||
 | 
			
		||||
const dialog = useDialogStore();
 | 
			
		||||
const eqps = useEquipments();
 | 
			
		||||
 | 
			
		||||
const buttonText = computed(() => {
 | 
			
		||||
  return isUndefined(props.downloadEvent) ? "上传" : "上传并下载";
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// var bitstream: File | null = null;
 | 
			
		||||
const fileInput = useTemplateRef("fileInput");
 | 
			
		||||
const bitstream = defineModel<File | undefined>();
 | 
			
		||||
const fileInput = computed(() => {
 | 
			
		||||
  return !isUndefined(bitstream.value) ? bitstream.value.name : "";
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  if (!isUndefined(bitstream.value) && !isNull(fileInput.value)) {
 | 
			
		||||
    let fileList = new DataTransfer();
 | 
			
		||||
    fileList.items.add(bitstream.value);
 | 
			
		||||
    fileInput.value.files = fileList.files;
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function handleFileChange(event: Event): void {
 | 
			
		||||
@@ -111,7 +118,6 @@ async function handleClick(event: Event): Promise<void> {
 | 
			
		||||
    console.error(e);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="postcss">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,33 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="motherboard-container" :style="{
 | 
			
		||||
    width: width + 'px',
 | 
			
		||||
    height: height + 'px',
 | 
			
		||||
    position: 'relative',
 | 
			
		||||
  }">
 | 
			
		||||
    <svg xmlns="http://www.w3.org/2000/svg" :width="width" :height="height" :viewBox="`0 0 800 600`"
 | 
			
		||||
      class="motherboard-svg">
 | 
			
		||||
      <image href="../equipments/svg/motherboard.svg" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" />
 | 
			
		||||
  <div
 | 
			
		||||
    class="motherboard-container"
 | 
			
		||||
    :style="{
 | 
			
		||||
      width: width + 'px',
 | 
			
		||||
      height: height + 'px',
 | 
			
		||||
      position: 'relative',
 | 
			
		||||
    }"
 | 
			
		||||
  >
 | 
			
		||||
    <svg
 | 
			
		||||
      xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
      :width="width"
 | 
			
		||||
      :height="height"
 | 
			
		||||
      :viewBox="`0 0 800 600`"
 | 
			
		||||
      class="motherboard-svg"
 | 
			
		||||
    >
 | 
			
		||||
      <image
 | 
			
		||||
        href="../equipments/svg/motherboard.svg"
 | 
			
		||||
        width="100%"
 | 
			
		||||
        height="100%"
 | 
			
		||||
        preserveAspectRatio="xMidYMid meet"
 | 
			
		||||
      />
 | 
			
		||||
    </svg>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="tsx">
 | 
			
		||||
import { JtagClient, type FileParameter } from "@/APIClient";
 | 
			
		||||
import z from "zod";
 | 
			
		||||
import MotherBoardCaps from "./MotherBoardCaps.vue";
 | 
			
		||||
import { useEquipments } from "@/stores/equipments";
 | 
			
		||||
import UploadCard from "@/components/UploadCard.vue";
 | 
			
		||||
import { useDialogStore } from "@/stores/dialog";
 | 
			
		||||
import { toNumber, toString } from "lodash";
 | 
			
		||||
import { computed, ref, defineComponent, watch } from "vue";
 | 
			
		||||
import {ref, computed, defineComponent, watch } from "vue";
 | 
			
		||||
 | 
			
		||||
// 主板特有属性
 | 
			
		||||
interface MotherBoardProps {
 | 
			
		||||
@@ -32,8 +41,11 @@ const props = withDefaults(defineProps<MotherBoardProps>(), getDefaultProps());
 | 
			
		||||
// 计算实际宽高
 | 
			
		||||
const width = computed(() => 800 * props.size);
 | 
			
		||||
const height = computed(() => 600 * props.size);
 | 
			
		||||
 | 
			
		||||
const eqps = useEquipments();
 | 
			
		||||
 | 
			
		||||
const bitstreamFile = ref<File | null> ();
 | 
			
		||||
 | 
			
		||||
watch([props.jtagAddr, props.jtagPort], () => {
 | 
			
		||||
  eqps.jtagIPAddr = props.jtagAddr;
 | 
			
		||||
  eqps.jtagPort = props.jtagPort;
 | 
			
		||||
@@ -50,7 +62,7 @@ defineExpose({
 | 
			
		||||
  getCapabilities: () => {
 | 
			
		||||
    // 返回组件定义而不是直接返回JSX
 | 
			
		||||
    return defineComponent({
 | 
			
		||||
      name: "MotherBoardCapabilities",
 | 
			
		||||
      name: "MotherBoardCaps",
 | 
			
		||||
      props: {
 | 
			
		||||
        jtagAddr: {
 | 
			
		||||
          type: String,
 | 
			
		||||
@@ -59,118 +71,14 @@ defineExpose({
 | 
			
		||||
        jtagPort: {
 | 
			
		||||
          type: String,
 | 
			
		||||
          default: props.jtagPort,
 | 
			
		||||
        },
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      setup(props) {
 | 
			
		||||
        const jtagController = new JtagClient();
 | 
			
		||||
        const dialog = useDialogStore();
 | 
			
		||||
 | 
			
		||||
        // 使用传入的属性或默认值
 | 
			
		||||
        const jtagIDCode = ref("");
 | 
			
		||||
        const boardAddress = computed(() => props.jtagAddr);
 | 
			
		||||
        const boardPort = computed(() => toNumber(props.jtagPort));
 | 
			
		||||
 | 
			
		||||
        async function uploadBitstream(event: Event, bitstream: File) {
 | 
			
		||||
          if (!boardAddress.value || !boardPort.value) {
 | 
			
		||||
            dialog.error("开发板地址或端口空缺");
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          const fileParam = {
 | 
			
		||||
            data: bitstream,
 | 
			
		||||
            fileName: bitstream.name,
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          try {
 | 
			
		||||
            const resp = await jtagController.uploadBitstream(
 | 
			
		||||
              boardAddress.value,
 | 
			
		||||
              fileParam,
 | 
			
		||||
            );
 | 
			
		||||
            return resp;
 | 
			
		||||
          } catch (e) {
 | 
			
		||||
            dialog.error("上传错误");
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async function downloadBitstream() {
 | 
			
		||||
          if (!boardAddress.value || !boardPort.value) {
 | 
			
		||||
            dialog.error("开发板地址或端口空缺");
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          try {
 | 
			
		||||
            const resp = await jtagController.downloadBitstream(
 | 
			
		||||
              boardAddress.value,
 | 
			
		||||
              boardPort.value,
 | 
			
		||||
            );
 | 
			
		||||
            return resp;
 | 
			
		||||
          } catch (e) {
 | 
			
		||||
            dialog.error("上传错误");
 | 
			
		||||
            console.error(e);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        watch([boardAddress, boardPort], () => {
 | 
			
		||||
          if (
 | 
			
		||||
            z.string().ip().safeParse(boardAddress).success &&
 | 
			
		||||
            z.number().positive().safeParse(boardPort).success
 | 
			
		||||
          )
 | 
			
		||||
            getIDCode();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        async function getIDCode() {
 | 
			
		||||
          if (!boardAddress.value || !boardPort.value) {
 | 
			
		||||
            dialog.error("开发板地址或端口空缺");
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          try {
 | 
			
		||||
            const resp = await jtagController.getDeviceIDCode(
 | 
			
		||||
              boardAddress.value,
 | 
			
		||||
              boardPort.value,
 | 
			
		||||
            );
 | 
			
		||||
            jtagIDCode.value = toString(resp);
 | 
			
		||||
          } catch (e) {
 | 
			
		||||
            dialog.error("获取IDCode错误");
 | 
			
		||||
            console.error(e);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return () => (
 | 
			
		||||
          <div>
 | 
			
		||||
            <h1 class="font-bold text-center text-2xl">Jtag</h1>
 | 
			
		||||
            <div class="flex">
 | 
			
		||||
              <p class="grow">IDCode: {jtagIDCode.value}</p>
 | 
			
		||||
              <button class="btn btn-circle w-8 h-8" onClick={getIDCode}>
 | 
			
		||||
                <svg
 | 
			
		||||
                  class="icon opacity-70"
 | 
			
		||||
                  viewBox="0 0 1024 1024"
 | 
			
		||||
                  version="1.1"
 | 
			
		||||
                  xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
                  p-id="4865"
 | 
			
		||||
                  width="200"
 | 
			
		||||
                  height="200"
 | 
			
		||||
                >
 | 
			
		||||
                  <path
 | 
			
		||||
                    d="M894.481158 505.727133c0 49.589418-9.711176 97.705276-28.867468 143.007041-18.501376 43.74634-44.98454 83.031065-78.712713 116.759237-33.728172 33.728172-73.012897 60.211337-116.759237 78.712713-45.311998 19.156292-93.417623 28.877701-143.007041 28.877701s-97.695043-9.721409-142.996808-28.877701c-43.756573-18.501376-83.031065-44.98454-116.76947-78.712713-33.728172-33.728172-60.211337-73.012897-78.712713-116.759237-19.156292-45.301765-28.867468-93.417623-28.867468-143.007041 0-49.579185 9.711176-97.695043 28.867468-142.996808 18.501376-43.74634 44.98454-83.031065 78.712713-116.759237 33.738405-33.728172 73.012897-60.211337 116.76947-78.712713 45.301765-19.166525 93.40739-28.877701 142.996808-28.877701 52.925397 0 104.008842 11.010775 151.827941 32.745798 46.192042 20.977777 86.909395 50.79692 121.016191 88.608084 4.389984 4.860704 8.646937 9.854439 12.781094 14.97097l0-136.263453c0-11.307533 9.168824-20.466124 20.466124-20.466124 11.307533 0 20.466124 9.15859 20.466124 20.466124l0 183.64253c0 5.433756-2.148943 10.632151-5.986341 14.46955-3.847631 3.837398-9.046027 5.996574-14.479783 5.996574l-183.64253-0.020466c-11.307533 0-20.466124-9.168824-20.466124-20.466124 0-11.307533 9.168824-20.466124 20.466124-20.466124l132.293025 0.020466c-3.960195-4.952802-8.063653-9.782807-12.289907-14.479783-30.320563-33.605376-66.514903-60.098773-107.549481-78.753645-42.467207-19.289322-87.850837-29.072129-134.902456-29.072129-87.195921 0-169.172981 33.9533-230.816946 95.597265-61.654198 61.654198-95.597265 143.621025-95.597265 230.816946s33.943067 169.172981 95.597265 230.816946c61.643965 61.654198 143.621025 95.607498 230.816946 95.607498s169.172981-33.9533 230.816946-95.607498c61.654198-61.643965 95.597265-143.621025 95.597265-230.816946 0-11.2973 9.168824-20.466124 20.466124-20.466124C885.322567 485.261009 894.481158 494.429833 894.481158 505.727133z"
 | 
			
		||||
                    p-id="4866"
 | 
			
		||||
                  ></path>
 | 
			
		||||
                </svg>
 | 
			
		||||
              </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="divider"></div>
 | 
			
		||||
            <UploadCard
 | 
			
		||||
              class="bg-base-200"
 | 
			
		||||
              upload-event={uploadBitstream}
 | 
			
		||||
              download-event={downloadBitstream}
 | 
			
		||||
              defaultFile={eqps.jtagBitstream}
 | 
			
		||||
              onFinishedUpload={(file: File) => {
 | 
			
		||||
                eqps.jtagBitstream = file;
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              {" "}
 | 
			
		||||
            </UploadCard>
 | 
			
		||||
          </div>
 | 
			
		||||
          <MotherBoardCaps
 | 
			
		||||
            jtagAddr={props.jtagAddr}
 | 
			
		||||
            jtagPort={props.jtagPort}
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										128
									
								
								src/components/equipments/MotherBoardCaps.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/components/equipments/MotherBoardCaps.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <h1 class="font-bold text-center text-2xl">Jtag</h1>
 | 
			
		||||
    <div class="flex">
 | 
			
		||||
      <p class="grow">IDCode: {{ jtagIDCode }}</p>
 | 
			
		||||
      <button class="btn btn-circle w-8 h-8" :onclick="getIDCode">
 | 
			
		||||
        <svg
 | 
			
		||||
          class="icon opacity-70"
 | 
			
		||||
          viewBox="0 0 1024 1024"
 | 
			
		||||
          version="1.1"
 | 
			
		||||
          xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
          p-id="4865"
 | 
			
		||||
          width="200"
 | 
			
		||||
          height="200"
 | 
			
		||||
        >
 | 
			
		||||
          <path
 | 
			
		||||
            d="M894.481158 505.727133c0 49.589418-9.711176 97.705276-28.867468 143.007041-18.501376 43.74634-44.98454 83.031065-78.712713 116.759237-33.728172 33.728172-73.012897 60.211337-116.759237 78.712713-45.311998 19.156292-93.417623 28.877701-143.007041 28.877701s-97.695043-9.721409-142.996808-28.877701c-43.756573-18.501376-83.031065-44.98454-116.76947-78.712713-33.728172-33.728172-60.211337-73.012897-78.712713-116.759237-19.156292-45.301765-28.867468-93.417623-28.867468-143.007041 0-49.579185 9.711176-97.695043 28.867468-142.996808 18.501376-43.74634 44.98454-83.031065 78.712713-116.759237 33.738405-33.728172 73.012897-60.211337 116.76947-78.712713 45.301765-19.166525 93.40739-28.877701 142.996808-28.877701 52.925397 0 104.008842 11.010775 151.827941 32.745798 46.192042 20.977777 86.909395 50.79692 121.016191 88.608084 4.389984 4.860704 8.646937 9.854439 12.781094 14.97097l0-136.263453c0-11.307533 9.168824-20.466124 20.466124-20.466124 11.307533 0 20.466124 9.15859 20.466124 20.466124l0 183.64253c0 5.433756-2.148943 10.632151-5.986341 14.46955-3.847631 3.837398-9.046027 5.996574-14.479783 5.996574l-183.64253-0.020466c-11.307533 0-20.466124-9.168824-20.466124-20.466124 0-11.307533 9.168824-20.466124 20.466124-20.466124l132.293025 0.020466c-3.960195-4.952802-8.063653-9.782807-12.289907-14.479783-30.320563-33.605376-66.514903-60.098773-107.549481-78.753645-42.467207-19.289322-87.850837-29.072129-134.902456-29.072129-87.195921 0-169.172981 33.9533-230.816946 95.597265-61.654198 61.654198-95.597265 143.621025-95.597265 230.816946s33.943067 169.172981 95.597265 230.816946c61.643965 61.654198 143.621025 95.607498 230.816946 95.607498s169.172981-33.9533 230.816946-95.607498c61.654198-61.643965 95.597265-143.621025 95.597265-230.816946 0-11.2973 9.168824-20.466124 20.466124-20.466124C885.322567 485.261009 894.481158 494.429833 894.481158 505.727133z"
 | 
			
		||||
            p-id="4866"
 | 
			
		||||
          ></path>
 | 
			
		||||
        </svg>
 | 
			
		||||
      </button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="divider"></div>
 | 
			
		||||
    <UploadCard
 | 
			
		||||
      class="bg-base-200"
 | 
			
		||||
      :upload-event="uploadBitstream"
 | 
			
		||||
      :download-event="downloadBitstream"
 | 
			
		||||
    >
 | 
			
		||||
    </UploadCard>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { JtagClient } from "@/APIClient";
 | 
			
		||||
import z from "zod";
 | 
			
		||||
import UploadCard from "@/components/UploadCard.vue";
 | 
			
		||||
import { useDialogStore } from "@/stores/dialog";
 | 
			
		||||
import { toNumber, toString } from "lodash";
 | 
			
		||||
import { ref, computed, watch } from "vue";
 | 
			
		||||
 | 
			
		||||
interface CapsProps {
 | 
			
		||||
  jtagAddr?: string;
 | 
			
		||||
  jtagPort?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<CapsProps>(), {
 | 
			
		||||
  jtagAddr: "127.0.0.1",
 | 
			
		||||
  jtagPort: "1234",
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const jtagController = new JtagClient();
 | 
			
		||||
const dialog = useDialogStore();
 | 
			
		||||
 | 
			
		||||
// 使用传入的属性或默认值
 | 
			
		||||
const jtagIDCode = ref("");
 | 
			
		||||
const boardAddress = computed(() => props.jtagAddr);
 | 
			
		||||
const boardPort = computed(() => toNumber(props.jtagPort));
 | 
			
		||||
 | 
			
		||||
async function uploadBitstream(event: Event, bitstream: File) {
 | 
			
		||||
  if (!boardAddress.value || !boardPort.value) {
 | 
			
		||||
    dialog.error("开发板地址或端口空缺");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const fileParam = {
 | 
			
		||||
    data: bitstream,
 | 
			
		||||
    fileName: bitstream.name,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const resp = await jtagController.uploadBitstream(
 | 
			
		||||
      boardAddress.value,
 | 
			
		||||
      fileParam,
 | 
			
		||||
    );
 | 
			
		||||
    return resp;
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    dialog.error("上传错误");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function downloadBitstream() {
 | 
			
		||||
  if (!boardAddress.value || !boardPort.value) {
 | 
			
		||||
    dialog.error("开发板地址或端口空缺");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const resp = await jtagController.downloadBitstream(
 | 
			
		||||
      boardAddress.value,
 | 
			
		||||
      boardPort.value,
 | 
			
		||||
    );
 | 
			
		||||
    return resp;
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    dialog.error("上传错误");
 | 
			
		||||
    console.error(e);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
watch([boardAddress, boardPort], () => {
 | 
			
		||||
  if (
 | 
			
		||||
    z.string().ip().safeParse(boardAddress).success &&
 | 
			
		||||
    z.number().positive().safeParse(boardPort).success
 | 
			
		||||
  )
 | 
			
		||||
    getIDCode();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
async function getIDCode() {
 | 
			
		||||
  if (!boardAddress.value || !boardPort.value) {
 | 
			
		||||
    dialog.error("开发板地址或端口空缺");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const resp = await jtagController.getDeviceIDCode(
 | 
			
		||||
      boardAddress.value,
 | 
			
		||||
      boardPort.value,
 | 
			
		||||
    );
 | 
			
		||||
    jtagIDCode.value = toString(resp);
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    dialog.error("获取IDCode错误");
 | 
			
		||||
    console.error(e);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="postcss">
 | 
			
		||||
@import "@/assets/main.css";
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="bg-base-200 min-h-screen p-6">
 | 
			
		||||
    <div class="flex flex-row align-middle">
 | 
			
		||||
    <div class="flex flex-row justify-between items-center">
 | 
			
		||||
      <h1 class="text-3xl font-bold mb-6">FPGA 设备管理</h1>
 | 
			
		||||
      <button class="btn btn-ghost self-end" @click="
 | 
			
		||||
      <button class="btn btn-ghost text-error hover:underline" @click="
 | 
			
		||||
        () => {
 | 
			
		||||
          isEditMode = !isEditMode;
 | 
			
		||||
        }
 | 
			
		||||
@@ -13,16 +13,19 @@
 | 
			
		||||
 | 
			
		||||
    <div class="card bg-base-100 shadow-xl">
 | 
			
		||||
      <div class="card-body">
 | 
			
		||||
        <h2 class="card-title mb-4">IP 地址列表</h2>
 | 
			
		||||
        <div class="flex flex-row justify-between items-center">
 | 
			
		||||
          <h2 class="card-title mb-4">IP 地址列表</h2>
 | 
			
		||||
          <button class="btn btn-ghost" @click="">刷新</button>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="overflow-x-auto">
 | 
			
		||||
          <table class="table w-full">
 | 
			
		||||
            <!-- 表头 -->
 | 
			
		||||
            <thead>
 | 
			
		||||
              <tr class="bg-base-300">
 | 
			
		||||
                <th>IP 地址</th>
 | 
			
		||||
                <th>版本号</th>
 | 
			
		||||
                <th>默认启动位流</th>
 | 
			
		||||
                <th class="w-50">IP 地址</th>
 | 
			
		||||
                <th class="w-30">版本号</th>
 | 
			
		||||
                <th class="w-50">默认启动位流</th>
 | 
			
		||||
                <th class="w-80">黄金位流</th>
 | 
			
		||||
                <th class="w-80">应用位流1</th>
 | 
			
		||||
                <th class="w-80">应用位流2</th>
 | 
			
		||||
@@ -35,7 +38,7 @@
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr class="hover">
 | 
			
		||||
                <td class="font-medium">
 | 
			
		||||
                  <input v-if="isEditMode" type="text" placeholder="Type here" class="input" />
 | 
			
		||||
                  <input v-if="isEditMode" type="text" placeholder="Type here" class="input m-0" v-model="devAddr" />
 | 
			
		||||
                  <span v-else>{{ devAddr }}</span>
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>v1.2.3</td>
 | 
			
		||||
@@ -50,29 +53,25 @@
 | 
			
		||||
                <!-- 黄金位流上传区 -->
 | 
			
		||||
                <td>
 | 
			
		||||
                  <div class="flex flex-col items-center gap-2">
 | 
			
		||||
                    <input type="file" class="file-input file-input-primary"
 | 
			
		||||
                      @change="handleFileChange($event, goldBitstreamFile)" />
 | 
			
		||||
                    <input type="file" class="file-input file-input-primary" @change="handleFileChange($event, 0)" />
 | 
			
		||||
                  </div>
 | 
			
		||||
                </td>
 | 
			
		||||
                <!-- 应用位流1上传区 -->
 | 
			
		||||
                <td>
 | 
			
		||||
                  <div class="flex flex-col items-center gap-2">
 | 
			
		||||
                    <input type="file" class="file-input file-input-secondary"
 | 
			
		||||
                      @change="handleFileChange($event, appBitstream1File)" />
 | 
			
		||||
                    <input type="file" class="file-input file-input-secondary" @change="handleFileChange($event, 1)" />
 | 
			
		||||
                  </div>
 | 
			
		||||
                </td>
 | 
			
		||||
                <!-- 应用位流2上传区 -->
 | 
			
		||||
                <td>
 | 
			
		||||
                  <div class="flex flex-col items-center gap-2">
 | 
			
		||||
                    <input type="file" class="file-input file-input-accent"
 | 
			
		||||
                      @change="handleFileChange($event, appBitstream2File)" />
 | 
			
		||||
                    <input type="file" class="file-input file-input-accent" @change="handleFileChange($event, 2)" />
 | 
			
		||||
                  </div>
 | 
			
		||||
                </td>
 | 
			
		||||
                <!-- 应用位流3上传区 -->
 | 
			
		||||
                <td>
 | 
			
		||||
                  <div class="flex flex-col items-center gap-2">
 | 
			
		||||
                    <input type="file" class="file-input file-input-info"
 | 
			
		||||
                      @change="handleFileChange($event, appBitstream3File)" />
 | 
			
		||||
                    <input type="file" class="file-input file-input-info" @change="handleFileChange($event, 3)" />
 | 
			
		||||
                  </div>
 | 
			
		||||
                </td>
 | 
			
		||||
 | 
			
		||||
@@ -138,7 +137,7 @@ const devPort = 1234;
 | 
			
		||||
const remoteUpdater = new RemoteUpdaterClient();
 | 
			
		||||
 | 
			
		||||
// 处理文件上传
 | 
			
		||||
function handleFileChange(event: Event, fileRef: any) {
 | 
			
		||||
function handleFileChange(event: Event, bistreamNum: number) {
 | 
			
		||||
  const target = event.target as HTMLInputElement;
 | 
			
		||||
  const file = target.files?.[0]; // 获取选中的第一个文件
 | 
			
		||||
 | 
			
		||||
@@ -146,8 +145,16 @@ function handleFileChange(event: Event, fileRef: any) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!isUndefined(fileRef)) {
 | 
			
		||||
    fileRef.value = file;
 | 
			
		||||
  if (bistreamNum === 0) {
 | 
			
		||||
    goldBitstreamFile.value = file;
 | 
			
		||||
  } else if (bistreamNum === 1) {
 | 
			
		||||
    appBitstream1File.value = file;
 | 
			
		||||
  } else if (bistreamNum === 2) {
 | 
			
		||||
    appBitstream2File.value = file;
 | 
			
		||||
  } else if (bistreamNum === 3) {
 | 
			
		||||
    appBitstream3File.value = file;
 | 
			
		||||
  } else {
 | 
			
		||||
    goldBitstreamFile.value = file;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -172,12 +179,13 @@ async function uploadAndDownloadBitstreams(
 | 
			
		||||
  appBitstream3?: File,
 | 
			
		||||
) {
 | 
			
		||||
  let cnt = 0;
 | 
			
		||||
  if (isUndefined(goldBitstream)) cnt++;
 | 
			
		||||
  if (isUndefined(appBitstream1)) cnt++;
 | 
			
		||||
  if (isUndefined(appBitstream2)) cnt++;
 | 
			
		||||
  if (isUndefined(appBitstream3)) cnt++;
 | 
			
		||||
  if ((cnt = 0)) {
 | 
			
		||||
  if (!isUndefined(goldBitstream)) cnt++;
 | 
			
		||||
  if (!isUndefined(appBitstream1)) cnt++;
 | 
			
		||||
  if (!isUndefined(appBitstream2)) cnt++;
 | 
			
		||||
  if (!isUndefined(appBitstream3)) cnt++;
 | 
			
		||||
  if (cnt === 0) {
 | 
			
		||||
    dialog.error("未选择比特流");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
@@ -189,10 +197,9 @@ async function uploadAndDownloadBitstreams(
 | 
			
		||||
        Common.toFileParameterOrNull(appBitstream2),
 | 
			
		||||
        Common.toFileParameterOrNull(appBitstream3),
 | 
			
		||||
      );
 | 
			
		||||
      if (ret) {
 | 
			
		||||
      if (!ret) {
 | 
			
		||||
        dialog.warn("上传比特流出错");
 | 
			
		||||
      } else {
 | 
			
		||||
        dialog.info("上传比特流成功");
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    {
 | 
			
		||||
@@ -230,6 +237,15 @@ async function hotresetBitstream(devAddr: string, bitstreamNum: number) {
 | 
			
		||||
    console.error(e);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function refreshData() {
 | 
			
		||||
  try {
 | 
			
		||||
    const ret = await remoteUpdater.getFirmwareVersion(devAddr.value, devPort);
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    dialog.error("获取数据失败");
 | 
			
		||||
    console.error(e);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="postcss">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user