Compare commits
11 Commits
493b339a37
...
6ab8256033
Author | SHA1 | Date |
---|---|---|
raojinlin | 6ab8256033 | |
raojinlin | ced23f350a | |
raojinlin | 565f04d49d | |
raojinlin | 029b227cde | |
Lykin | a431467b5f | |
Lykin | 78b7700b1b | |
Lykin | 28d2336124 | |
Lykin | 2250e15971 | |
Lykin | 70ed556e80 | |
Lykin | 13e80da978 | |
Lykin | 64ae79f565 |
|
@ -94,11 +94,11 @@ jobs:
|
|||
Set-Content -Path certificate\certificate.txt -Value '${{ secrets.WIN_SIGNING_CERT }}'
|
||||
certutil -decode certificate\certificate.txt certificate\certificate.pfx
|
||||
echo "Signing TinyRDM installer"
|
||||
& 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ secrets.WIN_SIGNING_CERT_PASSWORD }}' TinyRDM-${{ steps.normalise_platform.outputs.tag }}-installer.exe
|
||||
& 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe' sign /fd sha256 /tr http://ts.ssl.com /f certificate\certificate.pfx /p '${{ secrets.WIN_SIGNING_CERT_PASSWORD }}' 'Tiny RDM.exe'
|
||||
|
||||
- name: Rename installer
|
||||
working-directory: ./build/bin
|
||||
run: Rename-Item -Path "TinyRDM-${{ steps.normalise_platform.outputs.tag }}-installer.exe" -NewName "TinyRDM_Setup_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.exe"
|
||||
run: Rename-Item -Path "Tiny RDM.exe" -NewName "TinyRDM_Setup_${{ steps.normalise_version.outputs.version }}_${{ steps.normalise_platform.outputs.tag }}.exe"
|
||||
|
||||
- name: Upload release asset (Installer)
|
||||
uses: softprops/action-gh-release@v1
|
||||
|
|
|
@ -37,7 +37,7 @@ Linux.</strong>
|
|||
* Provides visually and user-friendly UI, light and dark themes (Thanks to [Naive UI](https://github.com/tusen-ai/naive-ui)
|
||||
and [IconPark](https://iconpark.oceanengine.com)).
|
||||
* Multi-language support ([Need more languages ? Click here to contribute](.github/CONTRIBUTING.md)).
|
||||
* Better connection management: supports SSH Tunnel/SSL/Sentinel Mode/Cluster Mode.
|
||||
* Better connection management: supports SSH Tunnel/SSL/Sentinel Mode/Cluster Mode/HTTP proxy/SOCKS5 proxy.
|
||||
* Visualize key value operations, CRUD support for Lists, Hashes, Strings, Sets, Sorted Sets, and Streams.
|
||||
* Support multiple data viewing format and decode/decompression methods.
|
||||
* Use SCAN for segmented loading, making it easy to list millions of keys.
|
||||
|
@ -50,11 +50,8 @@ Linux.</strong>
|
|||
* Support real-time commands monitoring.
|
||||
* Support import/export data.
|
||||
* Support publish/subscribe.
|
||||
* support import/export connection profile
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [ ] Custom data encoder and decoder for value display
|
||||
* Support import/export connection profile.
|
||||
* Custom data encoder and decoder for value display.
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
* 界面精美易用,提供浅色/深色主题(感谢[Naive UI](https://github.com/tusen-ai/naive-ui)
|
||||
和 [IconPark](https://iconpark.oceanengine.com))
|
||||
* 多国语言支持:英文/中文([需要更多语言支持?点我贡献语言](.github/CONTRIBUTING_zh.md))
|
||||
* 更好用的连接管理:支持SSH隧道/SSL/哨兵模式/集群模式
|
||||
* 更好用的连接管理:支持SSH隧道/SSL/哨兵模式/集群模式/HTTP代理/SOCKS5代理
|
||||
* 可视化键值操作,增删查改一应俱全
|
||||
* 支持多种数据查看格式以及转码/解压方式
|
||||
* 采用SCAN分段加载,可轻松处理数百万键列表
|
||||
|
@ -46,10 +46,7 @@
|
|||
* 支持导入/导出数据
|
||||
* 支持发布订阅
|
||||
* 支持导入/导出连接配置
|
||||
|
||||
## 未来版本规划
|
||||
|
||||
- [ ] 自定义数据展示编码/解码
|
||||
* 自定义数据展示编码/解码
|
||||
|
||||
## 安装
|
||||
|
||||
|
|
|
@ -11,8 +11,10 @@ import (
|
|||
"github.com/vrischmann/userdir"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/net/proxy"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
@ -21,6 +23,7 @@ import (
|
|||
"time"
|
||||
. "tinyrdm/backend/storage"
|
||||
"tinyrdm/backend/types"
|
||||
_ "tinyrdm/backend/utils/proxy"
|
||||
)
|
||||
|
||||
type cmdHistoryItem struct {
|
||||
|
@ -54,9 +57,34 @@ func (c *connectionService) Start(ctx context.Context) {
|
|||
}
|
||||
|
||||
func (c *connectionService) buildOption(config types.ConnectionConfig) (*redis.Options, error) {
|
||||
var sshClient *ssh.Client
|
||||
var dialer proxy.Dialer
|
||||
var dialerErr error
|
||||
if config.Proxy.Type == 1 {
|
||||
// use system proxy
|
||||
dialer = proxy.FromEnvironment()
|
||||
} else if config.Proxy.Type == 2 {
|
||||
// use custom proxy
|
||||
proxyUrl := url.URL{
|
||||
Host: fmt.Sprintf("%s:%d", config.Proxy.Addr, config.Proxy.Port),
|
||||
}
|
||||
if len(config.Proxy.Username) > 0 {
|
||||
proxyUrl.User = url.UserPassword(config.Proxy.Username, config.Proxy.Password)
|
||||
}
|
||||
switch config.Proxy.Schema {
|
||||
case "socks5", "socks5h", "http", "https":
|
||||
proxyUrl.Scheme = config.Proxy.Schema
|
||||
default:
|
||||
proxyUrl.Scheme = "http"
|
||||
}
|
||||
if dialer, dialerErr = proxy.FromURL(&proxyUrl, proxy.Direct); dialerErr != nil {
|
||||
return nil, dialerErr
|
||||
}
|
||||
}
|
||||
|
||||
var sshConfig *ssh.ClientConfig
|
||||
var sshAddr string
|
||||
if config.SSH.Enable {
|
||||
sshConfig := &ssh.ClientConfig{
|
||||
sshConfig = &ssh.ClientConfig{
|
||||
User: config.SSH.Username,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(config.SSH.Password)},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
|
@ -84,11 +112,7 @@ func (c *connectionService) buildOption(config types.ConnectionConfig) (*redis.O
|
|||
return nil, errors.New("invalid login type")
|
||||
}
|
||||
|
||||
var err error
|
||||
sshClient, err = ssh.Dial("tcp", fmt.Sprintf("%s:%d", config.SSH.Addr, config.SSH.Port), sshConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sshAddr = fmt.Sprintf("%s:%d", config.SSH.Addr, config.SSH.Port)
|
||||
}
|
||||
|
||||
var tlsConfig *tls.Config
|
||||
|
@ -150,9 +174,31 @@ func (c *connectionService) buildOption(config types.ConnectionConfig) (*redis.O
|
|||
option.Addr = fmt.Sprintf("%s:%d", config.Addr, port)
|
||||
}
|
||||
}
|
||||
if sshClient != nil {
|
||||
|
||||
if len(sshAddr) > 0 {
|
||||
if dialer != nil {
|
||||
// ssh with proxy
|
||||
conn, err := dialer.Dial("tcp", sshAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sc, chans, reqs, err := ssh.NewClientConn(conn, sshAddr, sshConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dialer = ssh.NewClient(sc, chans, reqs)
|
||||
} else {
|
||||
// ssh without proxy
|
||||
sshClient, err := ssh.Dial("tcp", sshAddr, sshConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dialer = sshClient
|
||||
}
|
||||
}
|
||||
if dialer != nil {
|
||||
option.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return sshClient.Dial(network, addr)
|
||||
return dialer.Dial(network, addr)
|
||||
}
|
||||
option.ReadTimeout = -2
|
||||
option.WriteTimeout = -2
|
||||
|
|
|
@ -27,6 +27,7 @@ type ConnectionConfig struct {
|
|||
SSH ConnectionSSH `json:"ssh,omitempty" yaml:"ssh,omitempty"`
|
||||
Sentinel ConnectionSentinel `json:"sentinel,omitempty" yaml:"sentinel,omitempty"`
|
||||
Cluster ConnectionCluster `json:"cluster,omitempty" yaml:"cluster,omitempty"`
|
||||
Proxy ConnectionProxy `json:"proxy,omitempty" yaml:"proxy,omitempty"`
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
|
@ -76,3 +77,12 @@ type ConnectionSentinel struct {
|
|||
type ConnectionCluster struct {
|
||||
Enable bool `json:"enable,omitempty" yaml:"enable,omitempty"`
|
||||
}
|
||||
|
||||
type ConnectionProxy struct {
|
||||
Type int `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
Schema string `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Addr string `json:"addr,omitempty" yaml:"addr,omitempty"`
|
||||
Port int `json:"port,omitempty" yaml:"port,omitempty"`
|
||||
Username string `json:"username,omitempty" yaml:"username,omitempty"`
|
||||
Password string `json:"password,omitempty" yaml:"password,omitempty"`
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
type HttpProxy struct {
|
||||
scheme string // HTTP Proxy scheme
|
||||
host string // HTTP Proxy host or host:port
|
||||
auth *proxy.Auth // authentication
|
||||
forward proxy.Dialer // forwarding Dialer
|
||||
}
|
||||
|
||||
func (p *HttpProxy) Dial(network, addr string) (net.Conn, error) {
|
||||
c, err := p.forward.Dial(network, p.host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.SetDeadline(time.Now().Add(15 * time.Second))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqUrl := &url.URL{
|
||||
Scheme: "",
|
||||
Host: addr,
|
||||
}
|
||||
|
||||
// create with CONNECT method
|
||||
req, err := http.NewRequest("CONNECT", reqUrl.String(), nil)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
req.Close = false
|
||||
|
||||
// authentication
|
||||
if p.auth != nil {
|
||||
req.SetBasicAuth(p.auth.User, p.auth.Password)
|
||||
req.Header.Add("Proxy-Authorization", req.Header.Get("Authorization"))
|
||||
}
|
||||
|
||||
// send request
|
||||
err = req.Write(c)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||
if err != nil {
|
||||
res.Body.Close()
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
c.Close()
|
||||
return nil, fmt.Errorf("proxy connection error: StatusCode[%d]", res.StatusCode)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func NewHttpProxyDialer(u *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
|
||||
var auth *proxy.Auth
|
||||
if u.User != nil {
|
||||
pwd, _ := u.User.Password()
|
||||
auth = &proxy.Auth{
|
||||
User: u.User.Username(),
|
||||
Password: pwd,
|
||||
}
|
||||
}
|
||||
|
||||
hp := &HttpProxy{
|
||||
scheme: u.Scheme,
|
||||
host: u.Host,
|
||||
auth: auth,
|
||||
forward: forward,
|
||||
}
|
||||
return hp, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialerType("http", NewHttpProxyDialer)
|
||||
proxy.RegisterDialerType("https", NewHttpProxyDialer)
|
||||
}
|
|
@ -13,21 +13,21 @@
|
|||
"lodash": "^4.17.21",
|
||||
"monaco-editor": "^0.46.0",
|
||||
"pinia": "^2.1.7",
|
||||
"sass": "^1.70.0",
|
||||
"vue": "^3.4.18",
|
||||
"sass": "^1.71.1",
|
||||
"vue": "^3.4.19",
|
||||
"vue-chartjs": "^5.3.0",
|
||||
"vue-i18n": "^9.9.1",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"naive-ui": "^2.37.3",
|
||||
"prettier": "^3.2.5",
|
||||
"unplugin-auto-import": "^0.17.5",
|
||||
"unplugin-icons": "^0.18.5",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.1.0"
|
||||
"vite": "^5.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@antfu/install-pkg": {
|
||||
|
@ -902,9 +902,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz",
|
||||
"integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==",
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
|
||||
"integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
|
@ -915,36 +915,36 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.18.tgz",
|
||||
"integrity": "sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
|
||||
"integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.9",
|
||||
"@vue/shared": "3.4.18",
|
||||
"@vue/shared": "3.4.19",
|
||||
"entities": "^4.5.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.18.tgz",
|
||||
"integrity": "sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
|
||||
"integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/compiler-core": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.18.tgz",
|
||||
"integrity": "sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz",
|
||||
"integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.23.9",
|
||||
"@vue/compiler-core": "3.4.18",
|
||||
"@vue/compiler-dom": "3.4.18",
|
||||
"@vue/compiler-ssr": "3.4.18",
|
||||
"@vue/shared": "3.4.18",
|
||||
"@vue/compiler-core": "3.4.19",
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/compiler-ssr": "3.4.19",
|
||||
"@vue/shared": "3.4.19",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.6",
|
||||
"postcss": "^8.4.33",
|
||||
|
@ -952,12 +952,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.18.tgz",
|
||||
"integrity": "sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
|
||||
"integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-api": {
|
||||
|
@ -966,29 +966,29 @@
|
|||
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.18.tgz",
|
||||
"integrity": "sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz",
|
||||
"integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.18.tgz",
|
||||
"integrity": "sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz",
|
||||
"integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/reactivity": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.18.tgz",
|
||||
"integrity": "sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz",
|
||||
"integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==",
|
||||
"dependencies": {
|
||||
"@vue/runtime-core": "3.4.18",
|
||||
"@vue/shared": "3.4.18",
|
||||
"@vue/runtime-core": "3.4.19",
|
||||
"@vue/shared": "3.4.19",
|
||||
"csstype": "^3.1.3"
|
||||
}
|
||||
},
|
||||
|
@ -998,21 +998,21 @@
|
|||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.18.tgz",
|
||||
"integrity": "sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz",
|
||||
"integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/compiler-ssr": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.4.18"
|
||||
"vue": "3.4.19"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.18.tgz",
|
||||
"integrity": "sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q=="
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
|
@ -1934,9 +1934,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.70.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
|
||||
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
|
||||
"version": "1.71.1",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
|
||||
"integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
|
@ -2259,9 +2259,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.0.tgz",
|
||||
"integrity": "sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==",
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz",
|
||||
"integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.19.3",
|
||||
|
@ -2326,15 +2326,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.18.tgz",
|
||||
"integrity": "sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz",
|
||||
"integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.18",
|
||||
"@vue/compiler-sfc": "3.4.18",
|
||||
"@vue/runtime-dom": "3.4.18",
|
||||
"@vue/server-renderer": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/compiler-sfc": "3.4.19",
|
||||
"@vue/runtime-dom": "3.4.19",
|
||||
"@vue/server-renderer": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "*"
|
||||
|
@ -2953,43 +2953,43 @@
|
|||
}
|
||||
},
|
||||
"@vitejs/plugin-vue": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz",
|
||||
"integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==",
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
|
||||
"integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@vue/compiler-core": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.18.tgz",
|
||||
"integrity": "sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
|
||||
"integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.23.9",
|
||||
"@vue/shared": "3.4.18",
|
||||
"@vue/shared": "3.4.19",
|
||||
"entities": "^4.5.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-dom": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.18.tgz",
|
||||
"integrity": "sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
|
||||
"integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
|
||||
"requires": {
|
||||
"@vue/compiler-core": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/compiler-core": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-sfc": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.18.tgz",
|
||||
"integrity": "sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz",
|
||||
"integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==",
|
||||
"requires": {
|
||||
"@babel/parser": "^7.23.9",
|
||||
"@vue/compiler-core": "3.4.18",
|
||||
"@vue/compiler-dom": "3.4.18",
|
||||
"@vue/compiler-ssr": "3.4.18",
|
||||
"@vue/shared": "3.4.18",
|
||||
"@vue/compiler-core": "3.4.19",
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/compiler-ssr": "3.4.19",
|
||||
"@vue/shared": "3.4.19",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.6",
|
||||
"postcss": "^8.4.33",
|
||||
|
@ -2997,12 +2997,12 @@
|
|||
}
|
||||
},
|
||||
"@vue/compiler-ssr": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.18.tgz",
|
||||
"integrity": "sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
|
||||
"integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"@vue/devtools-api": {
|
||||
|
@ -3011,29 +3011,29 @@
|
|||
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
|
||||
},
|
||||
"@vue/reactivity": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.18.tgz",
|
||||
"integrity": "sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz",
|
||||
"integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==",
|
||||
"requires": {
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-core": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.18.tgz",
|
||||
"integrity": "sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz",
|
||||
"integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==",
|
||||
"requires": {
|
||||
"@vue/reactivity": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/reactivity": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-dom": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.18.tgz",
|
||||
"integrity": "sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz",
|
||||
"integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==",
|
||||
"requires": {
|
||||
"@vue/runtime-core": "3.4.18",
|
||||
"@vue/shared": "3.4.18",
|
||||
"@vue/runtime-core": "3.4.19",
|
||||
"@vue/shared": "3.4.19",
|
||||
"csstype": "^3.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -3045,18 +3045,18 @@
|
|||
}
|
||||
},
|
||||
"@vue/server-renderer": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.18.tgz",
|
||||
"integrity": "sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz",
|
||||
"integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==",
|
||||
"requires": {
|
||||
"@vue/compiler-ssr": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/compiler-ssr": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"@vue/shared": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.18.tgz",
|
||||
"integrity": "sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q=="
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
|
||||
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.11.3",
|
||||
|
@ -3736,9 +3736,9 @@
|
|||
}
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.70.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz",
|
||||
"integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==",
|
||||
"version": "1.71.1",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
|
||||
"integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
|
||||
"requires": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
|
@ -3961,9 +3961,9 @@
|
|||
}
|
||||
},
|
||||
"vite": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.0.tgz",
|
||||
"integrity": "sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==",
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz",
|
||||
"integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esbuild": "^0.19.3",
|
||||
|
@ -3982,15 +3982,15 @@
|
|||
}
|
||||
},
|
||||
"vue": {
|
||||
"version": "3.4.18",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.18.tgz",
|
||||
"integrity": "sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A==",
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz",
|
||||
"integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==",
|
||||
"requires": {
|
||||
"@vue/compiler-dom": "3.4.18",
|
||||
"@vue/compiler-sfc": "3.4.18",
|
||||
"@vue/runtime-dom": "3.4.18",
|
||||
"@vue/server-renderer": "3.4.18",
|
||||
"@vue/shared": "3.4.18"
|
||||
"@vue/compiler-dom": "3.4.19",
|
||||
"@vue/compiler-sfc": "3.4.19",
|
||||
"@vue/runtime-dom": "3.4.19",
|
||||
"@vue/server-renderer": "3.4.19",
|
||||
"@vue/shared": "3.4.19"
|
||||
}
|
||||
},
|
||||
"vue-chartjs": {
|
||||
|
|
|
@ -14,20 +14,20 @@
|
|||
"lodash": "^4.17.21",
|
||||
"monaco-editor": "^0.46.0",
|
||||
"pinia": "^2.1.7",
|
||||
"sass": "^1.70.0",
|
||||
"vue": "^3.4.18",
|
||||
"sass": "^1.71.1",
|
||||
"vue": "^3.4.19",
|
||||
"vue-chartjs": "^5.3.0",
|
||||
"vue-i18n": "^9.9.1",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"naive-ui": "^2.37.3",
|
||||
"prettier": "^3.2.5",
|
||||
"unplugin-auto-import": "^0.17.5",
|
||||
"unplugin-icons": "^0.18.5",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.1.0"
|
||||
"vite": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
0e7759f741b2d18b129ae154ce334ae9
|
||||
0d716149856646a7939a157fbfa27913
|
|
@ -1,8 +1,9 @@
|
|||
<script setup>
|
||||
import { computed, h, ref } from 'vue'
|
||||
import { get, isEmpty } from 'lodash'
|
||||
import { get, isEmpty, some } from 'lodash'
|
||||
import { NIcon, NText } from 'naive-ui'
|
||||
import { useRender } from '@/utils/render.js'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
|
@ -13,6 +14,10 @@ const props = defineProps({
|
|||
type: Array,
|
||||
value: () => [],
|
||||
},
|
||||
menuOption: {
|
||||
type: Array,
|
||||
value: () => [],
|
||||
},
|
||||
tooltip: {
|
||||
type: String,
|
||||
},
|
||||
|
@ -21,7 +26,8 @@ const props = defineProps({
|
|||
disabled: Boolean,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:value'])
|
||||
const emit = defineEmits(['update:value', 'menu'])
|
||||
const i18n = useI18n()
|
||||
const render = useRender()
|
||||
|
||||
const renderHeader = () => {
|
||||
|
@ -65,11 +71,28 @@ const dropdownOption = computed(() => {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEmpty(props.menuOption)) {
|
||||
options.push({
|
||||
key: 'header-divider',
|
||||
type: 'divider',
|
||||
})
|
||||
for (const { key, label } of props.menuOption) {
|
||||
options.push({
|
||||
key,
|
||||
label: i18n.t(label),
|
||||
})
|
||||
}
|
||||
}
|
||||
return options
|
||||
})
|
||||
|
||||
const onDropdownSelect = (key) => {
|
||||
emit('update:value', key)
|
||||
if (some(props.menuOption, { key })) {
|
||||
emit('menu', key)
|
||||
} else {
|
||||
emit('update:value', key)
|
||||
}
|
||||
}
|
||||
|
||||
const buttonText = computed(() => {
|
||||
|
@ -88,7 +111,6 @@ const onDropdownShow = (show) => {
|
|||
:options="dropdownOption"
|
||||
:render-label="({ label }) => render.renderLabel(label, { class: 'type-selector-item' })"
|
||||
:show-arrow="true"
|
||||
:title="props.tooltip"
|
||||
:value="props.value"
|
||||
trigger="click"
|
||||
@select="onDropdownSelect"
|
||||
|
|
|
@ -22,6 +22,14 @@ const props = defineProps({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
keyPath: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
rememberScroll: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['reset', 'input', 'save'])
|
||||
|
@ -88,15 +96,17 @@ onMounted(async () => {
|
|||
// add shortcut for save
|
||||
editorNode.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, (event) => {
|
||||
emit('save')
|
||||
});
|
||||
|
||||
editorNode.onDidScrollChange((event) => {
|
||||
// 防止刷新时改变滚动距离
|
||||
if (!event.scrollHeightChanged) {
|
||||
scrollTop.value = event.scrollTop;
|
||||
}
|
||||
})
|
||||
|
||||
if (props.rememberScroll) {
|
||||
editorNode.onDidScrollChange((event) => {
|
||||
// Update scrolltop when scroll height changes, ie. content changes
|
||||
if (!event.scrollHeightChanged) {
|
||||
scrollTop.value = event.scrollTop
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// editorNode.onDidChangeModelLanguageConfiguration(() => {
|
||||
// editorNode?.getAction('editor.action.formatDocument')?.run()
|
||||
// })
|
||||
|
@ -111,19 +121,33 @@ onMounted(async () => {
|
|||
|
||||
watch(
|
||||
() => props.content,
|
||||
async (content) => {
|
||||
async (content, oldContent, onCleanup) => {
|
||||
if (editorNode != null) {
|
||||
editorNode.setValue(content)
|
||||
editorNode.onDidLayoutChange(() => {
|
||||
if (scrollTop.value > 0) {
|
||||
editorNode.setValue(content)
|
||||
|
||||
const disposable = editorNode.onDidLayoutChange(() => {
|
||||
if (props.rememberScroll && scrollTop.value > 0) {
|
||||
editorNode.setScrollTop(scrollTop.value)
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
onCleanup(() => disposable.dispose())
|
||||
|
||||
await nextTick(() => emit('reset', content))
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.keyPath,
|
||||
() => {
|
||||
if (editorNode != null) {
|
||||
scrollTop.value = 0
|
||||
editorNode.setScrollTop(0)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => readonlyValue.value,
|
||||
(readOnly) => {
|
||||
|
|
|
@ -175,6 +175,7 @@ const onSave = () => {
|
|||
:border="true"
|
||||
:content="displayValue"
|
||||
:language="viewLanguage"
|
||||
:key-path="viewAs.field"
|
||||
class="flex-item-expand"
|
||||
@input="onInput"
|
||||
@reset="onInput"
|
||||
|
|
|
@ -149,6 +149,7 @@ defineExpose({
|
|||
v-show="!props.loading"
|
||||
:content="displayValue"
|
||||
:loading="props.loading"
|
||||
:key-path="props.keyPath"
|
||||
class="flex-item-expand"
|
||||
language="json"
|
||||
style="height: 100%"
|
||||
|
|
|
@ -204,6 +204,7 @@ defineExpose({
|
|||
:content="displayValue"
|
||||
:language="viewLanguage"
|
||||
:loading="props.loading"
|
||||
:key-path="props.keyPath"
|
||||
class="flex-item-expand"
|
||||
style="height: 100%"
|
||||
@input="onInput"
|
||||
|
|
|
@ -6,6 +6,7 @@ import DropdownSelector from '@/components/common/DropdownSelector.vue'
|
|||
import { includes, isEmpty, map, pull, some, values } from 'lodash'
|
||||
import { computed } from 'vue'
|
||||
import usePreferencesStore from 'stores/preferences.js'
|
||||
import useDialogStore from 'stores/dialog.js'
|
||||
|
||||
const props = defineProps({
|
||||
decode: {
|
||||
|
@ -20,6 +21,7 @@ const props = defineProps({
|
|||
})
|
||||
|
||||
const prefStore = usePreferencesStore()
|
||||
const dialogStore = useDialogStore()
|
||||
|
||||
const formatTypeOption = computed(() => {
|
||||
return map(formatTypes, (t) => t)
|
||||
|
@ -46,6 +48,15 @@ const decodeTypeOption = computed(() => {
|
|||
return [buildinTypes, customTypes]
|
||||
})
|
||||
|
||||
const decodeMenuOption = computed(() => {
|
||||
return [
|
||||
{
|
||||
key: 'new_rdm_decoder',
|
||||
label: 'interface.custom_decoder',
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const emit = defineEmits(['formatChanged', 'update:decode', 'update:format'])
|
||||
const onFormatChanged = (selDecode, selFormat) => {
|
||||
const [buildin, external] = decodeTypeOption.value
|
||||
|
@ -64,6 +75,14 @@ const onFormatChanged = (selDecode, selFormat) => {
|
|||
emit('update:format', selFormat)
|
||||
}
|
||||
}
|
||||
|
||||
const onDecodeMenu = (key) => {
|
||||
switch (key) {
|
||||
case 'new_rdm_decoder':
|
||||
dialogStore.openPreferencesDialog('decoder')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -81,9 +100,11 @@ const onFormatChanged = (selDecode, selFormat) => {
|
|||
:default="decodeTypes.NONE"
|
||||
:disabled="props.disabled"
|
||||
:icon="Conversion"
|
||||
:menu-option="decodeMenuOption"
|
||||
:options="decodeTypeOption"
|
||||
:tooltip="$t('interface.decode_with')"
|
||||
:value="props.decode"
|
||||
@menu="onDecodeMenu"
|
||||
@update:value="(d) => onFormatChanged(d, '')" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
|
|
@ -205,10 +205,24 @@ const onSaveConnection = async () => {
|
|||
generalForm.value.sentinel = {}
|
||||
}
|
||||
|
||||
// trim cluster data
|
||||
if (!!!generalForm.value.cluster.enable) {
|
||||
generalForm.value.cluster = {}
|
||||
}
|
||||
|
||||
// trim proxy data
|
||||
if (generalForm.value.proxy.type !== 2) {
|
||||
generalForm.value.proxy.schema = ''
|
||||
generalForm.value.proxy.addr = ''
|
||||
generalForm.value.proxy.port = 0
|
||||
generalForm.value.proxy.auth = false
|
||||
generalForm.value.proxy.username = ''
|
||||
generalForm.value.proxy.password = ''
|
||||
} else if (!generalForm.value.proxy.auth) {
|
||||
generalForm.value.proxy.username = ''
|
||||
generalForm.value.proxy.password = ''
|
||||
}
|
||||
|
||||
// store new connection
|
||||
const { success, msg } = await connectionStore.saveConnection(
|
||||
isEditMode.value ? editName.value : null,
|
||||
|
@ -248,6 +262,7 @@ watch(
|
|||
pairs.push({ db: parseInt(db), alias: alias[db] })
|
||||
}
|
||||
aliasPair.value = pairs
|
||||
generalForm.value.proxy.auth = !isEmpty(generalForm.value.proxy.username)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -723,6 +738,69 @@ const pasteFromClipboard = async () => {
|
|||
<!-- label-placement="top">-->
|
||||
<!-- </n-form>-->
|
||||
</n-tab-pane>
|
||||
|
||||
<!-- Proxy pane -->
|
||||
<n-tab-pane :tab="$t('dialogue.connection.proxy.title')" display-directive="show:lazy" name="proxy">
|
||||
<n-radio-group v-model:value="generalForm.proxy.type" name="radiogroup">
|
||||
<n-space size="large" vertical>
|
||||
<n-radio :label="$t('dialogue.connection.proxy.type_none')" :value="0" />
|
||||
<n-radio :label="$t('dialogue.connection.proxy.type_system')" :value="1" />
|
||||
<n-radio :label="$t('dialogue.connection.proxy.type_custom')" :value="2" />
|
||||
<n-form
|
||||
:disabled="generalForm.proxy.type !== 2"
|
||||
:model="generalForm.proxy"
|
||||
:show-require-mark="false"
|
||||
label-placement="top">
|
||||
<n-grid :x-gap="10">
|
||||
<n-form-item-gi :show-label="false" :span="24" path="addr" required>
|
||||
<n-input-group>
|
||||
<n-select
|
||||
v-model:value="generalForm.proxy.schema"
|
||||
:consistent-menu-width="false"
|
||||
:options="[
|
||||
{ value: 'http', label: 'HTTP' },
|
||||
{ value: 'https', label: 'HTTPS' },
|
||||
{ value: 'socks5', label: 'SOCKS5' },
|
||||
{ value: 'socks5h', label: 'SOCKS5H' },
|
||||
]"
|
||||
default-value="http"
|
||||
style="max-width: 100px" />
|
||||
<n-input
|
||||
v-model:value="generalForm.proxy.addr"
|
||||
:placeholder="$t('dialogue.connection.proxy.host')" />
|
||||
<n-text style="width: 40px; text-align: center">:</n-text>
|
||||
<n-input-number
|
||||
v-model:value="generalForm.proxy.port"
|
||||
:max="65535"
|
||||
:min="0"
|
||||
:show-button="false"
|
||||
style="width: 200px" />
|
||||
</n-input-group>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :show-label="false" :span="24" path="auth">
|
||||
<n-checkbox v-model:checked="generalForm.proxy.auth" size="medium">
|
||||
{{ $t('dialogue.connection.proxy.auth') }}
|
||||
</n-checkbox>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="$t('dialogue.connection.usr')" :span="12" path="username">
|
||||
<n-input
|
||||
v-model:value="generalForm.proxy.username"
|
||||
:disabled="!!!generalForm.proxy.auth"
|
||||
:placeholder="$t('dialogue.connection.proxy.usr_tip')" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :label="$t('dialogue.connection.pwd')" :span="12" path="password">
|
||||
<n-input
|
||||
v-model:value="generalForm.proxy.password"
|
||||
:disabled="!!!generalForm.proxy.auth"
|
||||
:placeholder="$t('dialogue.connection.proxy.pwd_tip')"
|
||||
show-password-on="click"
|
||||
type="password" />
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
|
||||
<!-- test result alert-->
|
||||
|
|
|
@ -25,7 +25,7 @@ const loading = ref(false)
|
|||
const initPreferences = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
tab.value = 'general'
|
||||
tab.value = dialogStore.preferencesTag || 'general'
|
||||
await prefStore.loadPreferences()
|
||||
prevPreferences.value = {
|
||||
general: prefStore.general,
|
||||
|
|
|
@ -169,20 +169,23 @@ const exThemeVars = computed(() => {
|
|||
v-if="prefStore.currentLanguage === 'zh'"
|
||||
:icon="QRCode"
|
||||
:size="iconSize"
|
||||
:tooltip-delay="100"
|
||||
class="nav-menu-button"
|
||||
t-tooltip="ribbon.wechat_official"
|
||||
@click="openWechatOfficial" />
|
||||
<icon-button
|
||||
v-else
|
||||
:icon="Twitter"
|
||||
:border="false"
|
||||
:icon="Twitter"
|
||||
:size="iconSize"
|
||||
:tooltip-delay="100"
|
||||
class="nav-menu-button"
|
||||
t-tooltip="ribbon.follow_x"
|
||||
@click="openX" />
|
||||
<icon-button
|
||||
:icon="Github"
|
||||
:size="iconSize"
|
||||
:tooltip-delay="100"
|
||||
class="nav-menu-button"
|
||||
t-tooltip="ribbon.github"
|
||||
@click="openGithub" />
|
||||
|
|
|
@ -127,6 +127,7 @@
|
|||
"memory_usage": "Memory Usage",
|
||||
"view_as": "View As",
|
||||
"decode_with": "Decode / Decompression",
|
||||
"custom_decoder": "New Custom Decoder",
|
||||
"reload": "Reload",
|
||||
"reload_disable": "Reload will enable after full loaded",
|
||||
"auto_refresh": "Auto Refresh",
|
||||
|
@ -268,6 +269,16 @@
|
|||
"cluster": {
|
||||
"title": "Cluster",
|
||||
"enable": "Serve as Cluster Node"
|
||||
},
|
||||
"proxy": {
|
||||
"title": "Proxy",
|
||||
"type_none": "No Proxy",
|
||||
"type_system": "System Proxy Configuration",
|
||||
"type_custom": "Manual Proxy Configuration",
|
||||
"host": "Host name",
|
||||
"auth": "Proxy authentication",
|
||||
"usr_tip": "Username for proxy authentication",
|
||||
"pwd_tip": "Password for proxy authentication"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
|
|
|
@ -127,6 +127,7 @@
|
|||
"memory_usage": "内存占用",
|
||||
"view_as": "查看方式",
|
||||
"decode_with": "解码/解压方式",
|
||||
"custom_decoder": "添加自定义解码",
|
||||
"reload": "重新载入",
|
||||
"reload_disable": "全量加载后可重新载入",
|
||||
"auto_refresh": "自动刷新",
|
||||
|
@ -268,6 +269,16 @@
|
|||
"cluster": {
|
||||
"title": "集群模式",
|
||||
"enable": "当前为集群节点"
|
||||
},
|
||||
"proxy": {
|
||||
"title": "网络代理",
|
||||
"type_none": "不使用代理",
|
||||
"type_system": "使用系统代理设置",
|
||||
"type_custom": "手动配置代理",
|
||||
"host": "主机名",
|
||||
"auth": "使用身份验证",
|
||||
"usr_tip": "代理授权用户名",
|
||||
"pwd_tip": "代理授权密码"
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
|
|
|
@ -196,6 +196,15 @@ const useConnectionStore = defineStore('connections', {
|
|||
cluster: {
|
||||
enable: false,
|
||||
},
|
||||
proxy: {
|
||||
type: 0,
|
||||
schema: 'http',
|
||||
addr: '',
|
||||
port: 0,
|
||||
auth: false,
|
||||
username: '',
|
||||
password: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -102,6 +102,8 @@ const useDialogStore = defineStore('dialog', {
|
|||
},
|
||||
|
||||
preferencesDialogVisible: false,
|
||||
preferencesTag: '',
|
||||
|
||||
aboutDialogVisible: false,
|
||||
}),
|
||||
actions: {
|
||||
|
@ -346,11 +348,13 @@ const useDialogStore = defineStore('dialog', {
|
|||
this.decodeDialogVisible = false
|
||||
},
|
||||
|
||||
openPreferencesDialog() {
|
||||
openPreferencesDialog(tag = '') {
|
||||
this.preferencesDialogVisible = true
|
||||
this.preferencesTag = tag
|
||||
},
|
||||
closePreferencesDialog() {
|
||||
this.preferencesDialogVisible = false
|
||||
this.preferencesTag = ''
|
||||
},
|
||||
|
||||
openAboutDialog() {
|
||||
|
|
6
go.mod
6
go.mod
|
@ -6,12 +6,13 @@ require (
|
|||
github.com/adrg/sysfont v0.1.2
|
||||
github.com/andybalholm/brotli v1.1.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/klauspost/compress v1.17.6
|
||||
github.com/redis/go-redis/v9 v9.4.0
|
||||
github.com/klauspost/compress v1.17.7
|
||||
github.com/redis/go-redis/v9 v9.5.1
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1
|
||||
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
|
||||
github.com/wailsapp/wails/v2 v2.8.0
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/net v0.21.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
|
@ -44,7 +45,6 @@ require (
|
|||
github.com/wailsapp/go-webview2 v1.0.10 // indirect
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
|
|
8
go.sum
8
go.sum
|
@ -30,8 +30,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
||||
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
@ -66,8 +66,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
|
||||
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
|
|
Loading…
Reference in New Issue