+
+
+
+
+
diff --git a/verifyTool/kfzgs-launcher-start.bat b/verifyTool/kfzgs-launcher-start.bat
new file mode 100644
index 00000000..82109f4c
--- /dev/null
+++ b/verifyTool/kfzgs-launcher-start.bat
@@ -0,0 +1,11 @@
+@echo off
+chcp 65001 >nul
+cd /d "%~dp0"
+
+set "LAUNCHER=%~dp0kfzgs-launcher.ps1"
+
+echo 启动核价器后台服务(端口 5000)...
+powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%LAUNCHER%" 2>&1
+
+echo 服务已退出
+pause
diff --git a/verifyTool/kfzgs-launcher.ps1 b/verifyTool/kfzgs-launcher.ps1
new file mode 100644
index 00000000..ecbab202
--- /dev/null
+++ b/verifyTool/kfzgs-launcher.ps1
@@ -0,0 +1,188 @@
+# kfzgs-launcher.ps1 - 核价器启动脚本 (便携版)
+# 用法:
+# 协议模式: powershell -File kfzgs-launcher.ps1 -url "kfzgs://launch?dir=..."
+# 服务模式: powershell -File kfzgs-launcher.ps1
+param([string]$url = "")
+
+$SERVICE_ID = "kfz-goods-pricing"
+$HTTP_PORT = 5000
+$SCRIPT_DIR = Split-Path $MyInvocation.MyCommand.Path -Parent
+
+# 从同目录下的 config.json 读取 exe 路径,没有则用同目录下默认 exe
+$CONFIG_FILE = Join-Path $SCRIPT_DIR "launcher-config.json"
+$DEFAULT_EXE = Join-Path $SCRIPT_DIR "kfz-goods-pricing.exe"
+
+function Load-Config {
+ if (Test-Path $CONFIG_FILE) {
+ try {
+ $config = Get-Content $CONFIG_FILE -Raw | ConvertFrom-Json
+ if ($config.exe_path) { return $config.exe_path }
+ } catch {}
+ }
+ return $DEFAULT_EXE
+}
+
+function Save-Config($exePath) {
+ @{ exe_path = $exePath; id = $SERVICE_ID; name = "核价器" } | ConvertTo-Json | Out-File $CONFIG_FILE -Encoding UTF8
+}
+
+$EXE_PATH = Load-Config
+
+function Parse-KfzgsUrl($rawUrl) {
+ $decoded = [System.Uri]::UnescapeDataString($rawUrl)
+ $stripped = $decoded -replace "^kfzgs://", ""
+ $action = ""
+ $params = @{}
+ if ($stripped -match "^([^?]+)\?(.*)$") {
+ $action = $Matches[1]
+ $query = $Matches[2]
+ $query -split "&" | ForEach-Object {
+ $parts = $_ -split "=", 2
+ if ($parts.Count -eq 2) { $params[$parts[0]] = $parts[1] }
+ }
+ } elseif ($stripped -notmatch "[?]") {
+ $action = $stripped
+ }
+ return @{ action = $action; params = $params }
+}
+
+function Invoke-ProtocolHandler($rawUrl) {
+ $parsed = Parse-KfzgsUrl $rawUrl
+ $p = $parsed.params
+ if ($parsed.action -eq "launch") {
+ $dir = $p["dir"] -replace "/", "\"
+ $exeName = $p["exe"]
+
+ if ($exeName) {
+ if ($dir) {
+ if ($dir.EndsWith("\")) { $fullPath = "$dir$exeName" }
+ else { $fullPath = "$dir\$exeName" }
+ } else {
+ $fullPath = Join-Path $SCRIPT_DIR $exeName
+ }
+ } elseif ($dir -and $dir -match "\.exe$") {
+ if (Test-Path $dir) { $fullPath = $dir }
+ else { $fullPath = Join-Path $SCRIPT_DIR $dir }
+ } else { $fullPath = $EXE_PATH }
+
+ if ($fullPath -ne $EXE_PATH) {
+ Save-Config $fullPath
+ $script:EXE_PATH = $fullPath
+ }
+
+ if (Test-Path $fullPath) {
+ $parentDir = Split-Path $fullPath -Parent
+ Start-Process -FilePath $fullPath -WorkingDirectory $parentDir -WindowStyle Normal
+ }
+ }
+}
+
+function Start-ApiServer {
+ $listener = New-Object System.Net.HttpListener
+ $listener.Prefixes.Add("http://127.0.0.1:$HTTP_PORT/")
+ try { $listener.Start() } catch { exit 1 }
+
+ $services = @{}
+ $services[$SERVICE_ID] = @{
+ id = $SERVICE_ID
+ name = "核价器"
+ exe_path = $EXE_PATH
+ running = $false
+ }
+
+ while ($listener.IsListening) {
+ $context = $listener.GetContext()
+ $request = $context.Request
+ $response = $context.Response
+
+ if ($request.HttpMethod -eq "OPTIONS") {
+ $response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
+ $response.AddHeader("Access-Control-Allow-Headers", "Content-Type")
+ $response.StatusCode = 204
+ $response.Close()
+ continue
+ }
+
+ $path = $request.Url.AbsolutePath
+ $result = @{ code = 0; msg = "ok" }
+
+ try {
+ if ($path -eq "/api/services") {
+ if ($request.HttpMethod -eq "GET") {
+ $services[$SERVICE_ID]["exe_path"] = $EXE_PATH
+ $result["data"] = @($services.Values)
+ } elseif ($request.HttpMethod -eq "POST") {
+ $reader = New-Object System.IO.StreamReader($request.InputStream)
+ $body = $reader.ReadToEnd() | ConvertFrom-Json
+ if ($body.id -and $body.exe_path) {
+ $services[$body.id] = @{
+ id = $body.id
+ name = if ($body.name) { $body.name } else { $body.id }
+ exe_path = $body.exe_path
+ running = $false
+ }
+ Save-Config $body.exe_path
+ $script:EXE_PATH = $body.exe_path
+ $result["msg"] = "已注册: $($body.id)"
+ $result["data"] = $services[$body.id]
+ } else {
+ $result["code"] = 1
+ $result["msg"] = "缺少 id 或 exe_path"
+ }
+ }
+ }
+ elseif ($path -match "^/api/services/([^/]+)$" -and $request.HttpMethod -eq "GET") {
+ $sid = $Matches[1]
+ if ($services.ContainsKey($sid)) {
+ $result["data"] = $services[$sid]
+ } else {
+ $result["code"] = 1; $result["msg"] = "未找到"
+ }
+ }
+ elseif ($path -match "^/api/services/([^/]+)/start$" -and $request.HttpMethod -eq "POST") {
+ $sid = $Matches[1]
+ if ($services.ContainsKey($sid)) {
+ $svc = $services[$sid]
+ $exePath = if ($svc["exe_path"] -and (Test-Path $svc["exe_path"])) { $svc["exe_path"] } else { $EXE_PATH }
+ if (Test-Path $exePath) {
+ $parentDir = Split-Path $exePath -Parent
+ $proc = Start-Process -FilePath $exePath -WorkingDirectory $parentDir -WindowStyle Normal -PassThru
+ $svc["running"] = $true
+ $result["msg"] = "已启动"
+ $result["pid"] = $proc.Id
+ } else { $result["code"] = 1; $result["msg"] = "文件不存在: $exePath" }
+ } else { $result["code"] = 1; $result["msg"] = "未找到" }
+ }
+ elseif ($path -match "^/api/services/([^/]+)/stop$" -and $request.HttpMethod -eq "POST") {
+ $sid = $Matches[1]
+ if ($services.ContainsKey($sid)) {
+ $killed = $false
+ Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -eq $services[$sid]["exe_path"] } | ForEach-Object {
+ Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
+ $killed = $true
+ }
+ $services[$sid]["running"] = $false
+ $result["msg"] = if ($killed) { "已停止" } else { "未在运行" }
+ $result["killed"] = $killed
+ } else { $result["code"] = 1; $result["msg"] = "未找到" }
+ }
+ else { $result["code"] = 404; $result["msg"] = "未知接口" }
+ } catch {
+ $result["code"] = 1; $result["msg"] = $_.Exception.Message
+ }
+
+ $json = $result | ConvertTo-Json -Compress -Depth 3
+ $buffer = [System.Text.Encoding]::UTF8.GetBytes($json)
+ $response.AddHeader("Access-Control-Allow-Origin", "*")
+ $response.ContentType = "application/json; charset=utf-8"
+ $response.ContentLength64 = $buffer.Length
+ $response.OutputStream.Write($buffer, 0, $buffer.Length)
+ $response.Close()
+ }
+}
+
+if ($url -and $url -match "^kfzgs://") {
+ Invoke-ProtocolHandler $url
+} else {
+ Start-ApiServer
+}
diff --git a/verifyTool/launcher-config.json b/verifyTool/launcher-config.json
new file mode 100644
index 00000000..d1bee650
--- /dev/null
+++ b/verifyTool/launcher-config.json
@@ -0,0 +1 @@
+{"exe_path": "D:\\project\\psi_web\\verifyTool\\kfz-goods-pricing.exe","id": "kfz-goods-pricing","name": "核价器"}
\ No newline at end of file
diff --git a/verifyTool/setup.bat b/verifyTool/setup.bat
new file mode 100644
index 00000000..f4c6de09
--- /dev/null
+++ b/verifyTool/setup.bat
@@ -0,0 +1,47 @@
+@echo off
+chcp 65001 >nul
+setlocal enabledelayedexpansion
+title 核价器协议注册
+
+set "PROTOCOL=kfzgs"
+set "EXE_NAME=kfz-goods-pricing.exe"
+
+set "SD=%~dp0"
+set "SD=%SD:~0,-1%"
+
+set "EXE=%EXE_NAME%"
+if not "%EXE:~1,2%"==":\" set "EXE=%SD%\%EXE%"
+
+if not exist "%EXE%" (
+ echo [ERR] 未找到: %EXE%
+ pause
+ goto :eof
+)
+
+for %%A in ("%EXE%") do set "EXE_DIR=%%~dpA"
+set "EXE_DIR=%EXE_DIR:~0,-1%"
+
+echo ============================================
+echo 核价器协议注册
+echo ============================================
+echo Protocol: %PROTOCOL%://
+echo EXE: %EXE%
+echo WorkDir: %EXE_DIR%
+echo ============================================
+echo.
+
+reg delete "HKCR\%PROTOCOL%" /f >nul 2>&1
+reg add "HKCR\%PROTOCOL%" /ve /d "URL:%PROTOCOL% Protocol" /f >nul
+reg add "HKCR\%PROTOCOL%" /v "URL Protocol" /d "" /f >nul
+reg add "HKCR\%PROTOCOL%\DefaultIcon" /ve /d "\"%EXE%\",0" /f >nul
+reg add "HKCR\%PROTOCOL%\shell\open\command" /ve /d "cmd /c start \"\" /D \"%EXE_DIR%\" \"%EXE%\" \"%%1\"" /f >nul
+
+if !errorlevel! equ 0 (
+ echo [OK] %PROTOCOL%:// 注册成功
+ echo 测试: Win+R ^> %PROTOCOL%://launch
+) else (
+ echo [FAIL] 请右键以管理员身份运行
+)
+
+echo.
+pause
diff --git a/vite.config.js b/vite.config.js
index 9b8b213a..8f1edeb6 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -212,6 +212,22 @@ module.exports = defineConfig({
}
})
})
+
+ server.middlewares.use('/api/local/exe-config', (req, res) => {
+ res.setHeader('Access-Control-Allow-Origin', '*')
+ res.setHeader('Content-Type', 'application/json')
+ if (req.method !== 'GET') return res.end(JSON.stringify({ code: 405 }))
+
+ try {
+ const { execSync } = require('child_process')
+ const cmd = 'powershell -NoProfile -Command "(Get-ItemProperty -Path \'Registry::HKEY_CLASSES_ROOT\\kfzprice\\shell\\open\\command\' -Name \'(default)\').\'(default)\'"'
+ const raw = execSync(cmd, { encoding: 'utf8', timeout: 5000 }).trim()
+ const match = raw.match(/"([^"]+\.exe)"/i)
+ res.end(JSON.stringify({ code: 0, data: { exe_path: match ? match[1] : '' } }))
+ } catch (err) {
+ res.end(JSON.stringify({ code: 500, msg: err.message }))
+ }
+ })
}
}
],