@svadmin/lite 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/LiteAnnouncementBanner.svelte +88 -0
- package/dist/components/LiteAnnouncementBanner.svelte.d.ts +12 -0
- package/dist/components/LiteAsyncExportModal.svelte +113 -0
- package/dist/components/LiteAsyncExportModal.svelte.d.ts +14 -0
- package/dist/components/LiteContextMenu.svelte +114 -0
- package/dist/components/LiteContextMenu.svelte.d.ts +18 -0
- package/dist/components/LiteDualApprovalGate.svelte +58 -0
- package/dist/components/LiteDualApprovalGate.svelte.d.ts +11 -0
- package/dist/components/LiteJsonViewer.svelte +88 -0
- package/dist/components/LiteJsonViewer.svelte.d.ts +9 -0
- package/dist/components/LiteKeyboardShortcutsModal.svelte +56 -0
- package/dist/components/LiteKeyboardShortcutsModal.svelte.d.ts +13 -0
- package/dist/components/LiteKpiCard.svelte +169 -0
- package/dist/components/LiteKpiCard.svelte.d.ts +25 -0
- package/dist/components/LiteResultPage.svelte +135 -0
- package/dist/components/LiteResultPage.svelte.d.ts +17 -0
- package/dist/components/LiteSensitiveDataMask.svelte +34 -2
- package/dist/components/LiteSensitiveDataMask.svelte.d.ts +1 -2
- package/dist/components/LiteStepWizard.svelte +98 -0
- package/dist/components/LiteStepWizard.svelte.d.ts +16 -0
- package/dist/components/LiteTextDiffViewer.svelte +111 -0
- package/dist/components/LiteTextDiffViewer.svelte.d.ts +10 -0
- package/dist/components/LiteUnsavedChangesGuard.svelte +53 -0
- package/dist/components/LiteUnsavedChangesGuard.svelte.d.ts +10 -0
- package/dist/components/LiteWatermarkContainer.svelte +58 -0
- package/dist/components/LiteWatermarkContainer.svelte.d.ts +9 -0
- package/dist/components/LiteWorkflowStepper.svelte +56 -0
- package/dist/components/LiteWorkflowStepper.svelte.d.ts +14 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
type BannerTone = 'info' | 'warning' | 'error' | 'success';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
message: string;
|
|
6
|
+
tone?: BannerTone;
|
|
7
|
+
countdownText?: string;
|
|
8
|
+
actionText?: string;
|
|
9
|
+
actionHref?: string;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
message,
|
|
15
|
+
tone = 'warning',
|
|
16
|
+
countdownText,
|
|
17
|
+
actionText,
|
|
18
|
+
actionHref,
|
|
19
|
+
class: className = '',
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
|
|
22
|
+
const iconChar = $derived.by(() => {
|
|
23
|
+
switch (tone) {
|
|
24
|
+
case 'info':
|
|
25
|
+
return 'ℹ';
|
|
26
|
+
case 'success':
|
|
27
|
+
return '✓';
|
|
28
|
+
case 'error':
|
|
29
|
+
return '✕';
|
|
30
|
+
case 'warning':
|
|
31
|
+
default:
|
|
32
|
+
return '⚠';
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<aside
|
|
38
|
+
class="lite-announcement-banner lite-banner-{tone} {className}"
|
|
39
|
+
data-svadmin-component="LiteAnnouncementBanner"
|
|
40
|
+
role="alert"
|
|
41
|
+
>
|
|
42
|
+
<div class="lite-banner-body">
|
|
43
|
+
<span class="lite-banner-icon" aria-hidden="true">{iconChar}</span>
|
|
44
|
+
<span class="lite-banner-message">{message}</span>
|
|
45
|
+
|
|
46
|
+
{#if countdownText}
|
|
47
|
+
<span class="lite-banner-countdown">{countdownText}</span>
|
|
48
|
+
{/if}
|
|
49
|
+
|
|
50
|
+
{#if actionText}
|
|
51
|
+
{#if actionHref}
|
|
52
|
+
<a href={actionHref} class="lite-banner-action">{actionText}</a>
|
|
53
|
+
{:else}
|
|
54
|
+
<span class="lite-banner-action">{actionText}</span>
|
|
55
|
+
{/if}
|
|
56
|
+
{/if}
|
|
57
|
+
</div>
|
|
58
|
+
</aside>
|
|
59
|
+
|
|
60
|
+
<style>
|
|
61
|
+
.lite-announcement-banner {
|
|
62
|
+
padding: 8px 16px;
|
|
63
|
+
border-bottom: 1px solid transparent;
|
|
64
|
+
font-size: 13px;
|
|
65
|
+
box-sizing: border-box;
|
|
66
|
+
}
|
|
67
|
+
.lite-banner-info { background: #e8f3ff; color: #165dff; border-color: #bedaff; }
|
|
68
|
+
.lite-banner-warning { background: #fff7e8; color: #d46b08; border-color: #ffd591; }
|
|
69
|
+
.lite-banner-error { background: #fff1f0; color: #cf1322; border-color: #ffa39e; }
|
|
70
|
+
.lite-banner-success { background: #e8ffea; color: #00b42a; border-color: #aff0b5; }
|
|
71
|
+
.lite-banner-body { display: flex; align-items: center; flex-wrap: wrap; }
|
|
72
|
+
.lite-banner-body > * + * { margin-left: 8px; }
|
|
73
|
+
.lite-banner-icon { font-weight: 700; font-size: 14px; }
|
|
74
|
+
.lite-banner-message { font-weight: 500; flex: 1; min-width: 200px; }
|
|
75
|
+
.lite-banner-countdown {
|
|
76
|
+
font-family: monospace;
|
|
77
|
+
font-size: 11px;
|
|
78
|
+
padding: 2px 6px;
|
|
79
|
+
border-radius: 4px;
|
|
80
|
+
background: rgba(0, 0, 0, 0.05);
|
|
81
|
+
font-weight: 600;
|
|
82
|
+
}
|
|
83
|
+
.lite-banner-action {
|
|
84
|
+
text-decoration: underline;
|
|
85
|
+
font-weight: 600;
|
|
86
|
+
color: inherit;
|
|
87
|
+
}
|
|
88
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type BannerTone = 'info' | 'warning' | 'error' | 'success';
|
|
2
|
+
interface Props {
|
|
3
|
+
message: string;
|
|
4
|
+
tone?: BannerTone;
|
|
5
|
+
countdownText?: string;
|
|
6
|
+
actionText?: string;
|
|
7
|
+
actionHref?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const LiteAnnouncementBanner: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type LiteAnnouncementBanner = ReturnType<typeof LiteAnnouncementBanner>;
|
|
12
|
+
export default LiteAnnouncementBanner;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface ExportCol {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
open?: boolean;
|
|
9
|
+
action?: string;
|
|
10
|
+
resourceName?: string;
|
|
11
|
+
columns?: ExportCol[];
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
open = true,
|
|
17
|
+
action = '/api/export',
|
|
18
|
+
resourceName = 'Records',
|
|
19
|
+
columns = [],
|
|
20
|
+
class: className = '',
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
{#if open}
|
|
25
|
+
<section
|
|
26
|
+
class="lite-export-modal {className}"
|
|
27
|
+
data-svadmin-component="LiteAsyncExportModal"
|
|
28
|
+
aria-label="Export {resourceName}"
|
|
29
|
+
>
|
|
30
|
+
<header class="lite-export-header">
|
|
31
|
+
<h3 class="lite-export-title">Export {resourceName}</h3>
|
|
32
|
+
</header>
|
|
33
|
+
|
|
34
|
+
<form method="POST" action={action} class="lite-export-form">
|
|
35
|
+
<div class="lite-export-group">
|
|
36
|
+
<label for="lite-export-filename">File Name</label>
|
|
37
|
+
<input type="text" id="lite-export-filename" name="filename" value="{resourceName}-Export" />
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="lite-export-group">
|
|
41
|
+
<span class="lite-export-label">Format</span>
|
|
42
|
+
<div class="lite-export-radios">
|
|
43
|
+
<label><input type="radio" name="format" value="xlsx" checked /> Excel (.xlsx)</label>
|
|
44
|
+
<label><input type="radio" name="format" value="csv" /> CSV (.csv)</label>
|
|
45
|
+
<label><input type="radio" name="format" value="json" /> JSON (.json)</label>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
{#if columns.length > 0}
|
|
50
|
+
<div class="lite-export-group">
|
|
51
|
+
<span class="lite-export-label">Columns</span>
|
|
52
|
+
<div class="lite-export-columns">
|
|
53
|
+
{#each columns as col (col.key)}
|
|
54
|
+
<label class="lite-export-col-item">
|
|
55
|
+
<input type="checkbox" name="columns" value={col.key} checked />
|
|
56
|
+
<span>{col.label}</span>
|
|
57
|
+
</label>
|
|
58
|
+
{/each}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
{/if}
|
|
62
|
+
|
|
63
|
+
<div class="lite-export-actions">
|
|
64
|
+
<button type="submit" class="lite-export-submit">Start Export</button>
|
|
65
|
+
</div>
|
|
66
|
+
</form>
|
|
67
|
+
</section>
|
|
68
|
+
{/if}
|
|
69
|
+
|
|
70
|
+
<style>
|
|
71
|
+
.lite-export-modal {
|
|
72
|
+
border: 1px solid #e5e6eb;
|
|
73
|
+
border-radius: 8px;
|
|
74
|
+
padding: 16px;
|
|
75
|
+
background: #ffffff;
|
|
76
|
+
max-width: 480px;
|
|
77
|
+
box-sizing: border-box;
|
|
78
|
+
}
|
|
79
|
+
.lite-export-header { margin-bottom: 12px; }
|
|
80
|
+
.lite-export-title { margin: 0; font-size: 15px; color: #1d2129; font-weight: 600; }
|
|
81
|
+
.lite-export-group { margin-bottom: 12px; font-size: 13px; }
|
|
82
|
+
.lite-export-group label, .lite-export-label { display: block; margin-bottom: 4px; font-weight: 500; color: #4e5969; }
|
|
83
|
+
.lite-export-group input[type="text"] {
|
|
84
|
+
width: 100%;
|
|
85
|
+
box-sizing: border-box;
|
|
86
|
+
padding: 6px 10px;
|
|
87
|
+
border: 1px solid #e5e6eb;
|
|
88
|
+
border-radius: 4px;
|
|
89
|
+
}
|
|
90
|
+
.lite-export-radios { display: flex; margin-top: 4px; }
|
|
91
|
+
.lite-export-radios > * + * { margin-left: 12px; }
|
|
92
|
+
.lite-export-columns {
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-wrap: wrap;
|
|
95
|
+
max-height: 140px;
|
|
96
|
+
overflow-y: auto;
|
|
97
|
+
padding: 6px;
|
|
98
|
+
background: #f7f8fa;
|
|
99
|
+
border-radius: 4px;
|
|
100
|
+
}
|
|
101
|
+
.lite-export-col-item { display: flex; align-items: center; width: 50%; font-size: 12px; }
|
|
102
|
+
.lite-export-col-item > * + * { margin-left: 6px; }
|
|
103
|
+
.lite-export-actions { margin-top: 16px; display: flex; justify-content: flex-end; }
|
|
104
|
+
.lite-export-submit {
|
|
105
|
+
padding: 7px 16px;
|
|
106
|
+
background: #165dff;
|
|
107
|
+
color: #ffffff;
|
|
108
|
+
border: 0;
|
|
109
|
+
border-radius: 4px;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
font-weight: 500;
|
|
112
|
+
}
|
|
113
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface ExportCol {
|
|
2
|
+
key: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
interface Props {
|
|
6
|
+
open?: boolean;
|
|
7
|
+
action?: string;
|
|
8
|
+
resourceName?: string;
|
|
9
|
+
columns?: ExportCol[];
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const LiteAsyncExportModal: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type LiteAsyncExportModal = ReturnType<typeof LiteAsyncExportModal>;
|
|
14
|
+
export default LiteAsyncExportModal;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
export interface LiteContextMenuItem {
|
|
5
|
+
key: string;
|
|
6
|
+
label: string;
|
|
7
|
+
shortcut?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
danger?: boolean;
|
|
10
|
+
divider?: boolean;
|
|
11
|
+
href?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
items: LiteContextMenuItem[];
|
|
16
|
+
class?: string;
|
|
17
|
+
children?: Snippet;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
items = [],
|
|
22
|
+
class: className = '',
|
|
23
|
+
children,
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<div
|
|
28
|
+
class="lite-context-menu-wrapper {className}"
|
|
29
|
+
data-svadmin-component="LiteContextMenu"
|
|
30
|
+
>
|
|
31
|
+
<div class="lite-context-target">
|
|
32
|
+
{#if children}{@render children()}{/if}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
{#if items.length > 0}
|
|
36
|
+
<ul class="lite-context-menu-list" role="menu">
|
|
37
|
+
{#each items as item (item.key)}
|
|
38
|
+
{#if item.divider}
|
|
39
|
+
<li class="lite-context-divider" role="separator"></li>
|
|
40
|
+
{:else}
|
|
41
|
+
<li
|
|
42
|
+
class="lite-context-item"
|
|
43
|
+
class:lite-context-danger={item.danger}
|
|
44
|
+
class:lite-context-disabled={item.disabled}
|
|
45
|
+
role="menuitem"
|
|
46
|
+
>
|
|
47
|
+
{#if item.href && !item.disabled}
|
|
48
|
+
<a href={item.href} class="lite-context-link">
|
|
49
|
+
<span>{item.label}</span>
|
|
50
|
+
{#if item.shortcut}<kbd>{item.shortcut}</kbd>{/if}
|
|
51
|
+
</a>
|
|
52
|
+
{:else}
|
|
53
|
+
<span class="lite-context-label">{item.label}</span>
|
|
54
|
+
{#if item.shortcut}<kbd>{item.shortcut}</kbd>{/if}
|
|
55
|
+
{/if}
|
|
56
|
+
</li>
|
|
57
|
+
{/if}
|
|
58
|
+
{/each}
|
|
59
|
+
</ul>
|
|
60
|
+
{/if}
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
.lite-context-menu-wrapper {
|
|
65
|
+
position: relative;
|
|
66
|
+
display: block;
|
|
67
|
+
}
|
|
68
|
+
.lite-context-menu-list {
|
|
69
|
+
margin: 8px 0 0;
|
|
70
|
+
padding: 4px;
|
|
71
|
+
list-style: none;
|
|
72
|
+
border: 1px solid #e5e6eb;
|
|
73
|
+
border-radius: 6px;
|
|
74
|
+
background: #ffffff;
|
|
75
|
+
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
|
|
76
|
+
display: inline-flex;
|
|
77
|
+
flex-wrap: wrap;
|
|
78
|
+
}
|
|
79
|
+
.lite-context-menu-list > * + * { margin-left: 4px; }
|
|
80
|
+
.lite-context-divider {
|
|
81
|
+
width: 1px;
|
|
82
|
+
height: 16px;
|
|
83
|
+
background: #e5e6eb;
|
|
84
|
+
align-self: center;
|
|
85
|
+
margin: 0 4px;
|
|
86
|
+
}
|
|
87
|
+
.lite-context-item {
|
|
88
|
+
font-size: 12px;
|
|
89
|
+
border-radius: 4px;
|
|
90
|
+
display: inline-flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
padding: 4px 8px;
|
|
93
|
+
color: #1d2129;
|
|
94
|
+
background: #f7f8fa;
|
|
95
|
+
}
|
|
96
|
+
.lite-context-item > * + * { margin-left: 6px; }
|
|
97
|
+
.lite-context-link {
|
|
98
|
+
color: inherit;
|
|
99
|
+
text-decoration: none;
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
}
|
|
103
|
+
.lite-context-link > * + * { margin-left: 6px; }
|
|
104
|
+
.lite-context-danger { color: #f53f3f; background: #ffece8; }
|
|
105
|
+
.lite-context-disabled { opacity: 0.5; pointer-events: none; }
|
|
106
|
+
kbd {
|
|
107
|
+
font-size: 10px;
|
|
108
|
+
font-family: monospace;
|
|
109
|
+
background: #e5e6eb;
|
|
110
|
+
padding: 1px 4px;
|
|
111
|
+
border-radius: 3px;
|
|
112
|
+
color: #4e5969;
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export interface LiteContextMenuItem {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
shortcut?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
danger?: boolean;
|
|
8
|
+
divider?: boolean;
|
|
9
|
+
href?: string;
|
|
10
|
+
}
|
|
11
|
+
interface Props {
|
|
12
|
+
items: LiteContextMenuItem[];
|
|
13
|
+
class?: string;
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
}
|
|
16
|
+
declare const LiteContextMenu: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type LiteContextMenu = ReturnType<typeof LiteContextMenu>;
|
|
18
|
+
export default LiteContextMenu;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
actionTitle: string;
|
|
6
|
+
requiredRole?: string;
|
|
7
|
+
formAction?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
actionTitle,
|
|
14
|
+
requiredRole = '合规主管',
|
|
15
|
+
formAction = '/approval/verify',
|
|
16
|
+
class: className = '',
|
|
17
|
+
children,
|
|
18
|
+
}: Props = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<div
|
|
22
|
+
class="lite-dual-approval {className}"
|
|
23
|
+
data-svadmin-component="LiteDualApprovalGate"
|
|
24
|
+
>
|
|
25
|
+
<div class="lite-approval-header">
|
|
26
|
+
<span class="lite-approval-badge">双人联签 (Two-Person Rule)</span>
|
|
27
|
+
<h3 class="lite-approval-title">{actionTitle}</h3>
|
|
28
|
+
<p class="lite-approval-desc">特权高危操作,须由第二位具备 [{requiredRole}] 权限的主管输入口令核验。</p>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
{#if children}{@render children()}{/if}
|
|
32
|
+
|
|
33
|
+
<form action={formAction} method="POST" class="lite-approval-form">
|
|
34
|
+
<div class="lite-form-row">
|
|
35
|
+
<label for="lite-rev-id" class="lite-label">复核人工号</label>
|
|
36
|
+
<input id="lite-rev-id" name="reviewerId" type="text" required class="lite-input" placeholder="输入工号" />
|
|
37
|
+
</div>
|
|
38
|
+
<div class="lite-form-row">
|
|
39
|
+
<label for="lite-rev-sec" class="lite-label">复核人口令/2FA</label>
|
|
40
|
+
<input id="lite-rev-sec" name="secret" type="password" required class="lite-input" placeholder="输入独立核验口令" />
|
|
41
|
+
</div>
|
|
42
|
+
<button type="submit" class="lite-approval-btn">确认双人联签并执行</button>
|
|
43
|
+
</form>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<style>
|
|
47
|
+
.lite-dual-approval { border: 1px solid #fbbbb0; background: #fffdfc; border-radius: 8px; padding: 20px; }
|
|
48
|
+
.lite-approval-badge { display: inline-block; font-size: 11px; font-weight: 600; color: #f53f3f; background: #ffece8; padding: 2px 8px; border-radius: 4px; margin-bottom: 8px; }
|
|
49
|
+
.lite-approval-title { font-size: 16px; font-weight: 700; color: #1d2129; margin: 0 0 6px 0; }
|
|
50
|
+
.lite-approval-desc { font-size: 12px; color: #86909c; margin: 0 0 16px 0; }
|
|
51
|
+
.lite-approval-form { display: flex; flex-direction: column; }
|
|
52
|
+
.lite-approval-form > * + * { margin-top: 12px; }
|
|
53
|
+
.lite-form-row { display: flex; flex-direction: column; }
|
|
54
|
+
.lite-form-row > * + * { margin-top: 4px; }
|
|
55
|
+
.lite-label { font-size: 12px; font-weight: 600; color: #4e5969; }
|
|
56
|
+
.lite-input { height: 32px; border: 1px solid #e5e6eb; border-radius: 4px; padding: 0 10px; font-size: 12px; }
|
|
57
|
+
.lite-approval-btn { height: 34px; background: #f53f3f; color: #fff; border: none; border-radius: 4px; font-size: 12px; font-weight: 600; cursor: pointer; }
|
|
58
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
actionTitle: string;
|
|
4
|
+
requiredRole?: string;
|
|
5
|
+
formAction?: string;
|
|
6
|
+
class?: string;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
}
|
|
9
|
+
declare const LiteDualApprovalGate: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type LiteDualApprovalGate = ReturnType<typeof LiteDualApprovalGate>;
|
|
11
|
+
export default LiteDualApprovalGate;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
data: unknown;
|
|
4
|
+
initialDepth?: number;
|
|
5
|
+
title?: string;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
data,
|
|
11
|
+
initialDepth = 2,
|
|
12
|
+
title,
|
|
13
|
+
class: className = '',
|
|
14
|
+
}: Props = $props();
|
|
15
|
+
|
|
16
|
+
const formattedJson = $derived.by(() => {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.stringify(data, null, 2);
|
|
19
|
+
} catch {
|
|
20
|
+
return String(data);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const itemCount = $derived.by(() => {
|
|
25
|
+
if (typeof data === 'object' && data !== null) {
|
|
26
|
+
return Array.isArray(data) ? `${data.length} items` : `${Object.keys(data).length} keys`;
|
|
27
|
+
}
|
|
28
|
+
return typeof data;
|
|
29
|
+
});
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div
|
|
33
|
+
class="lite-json-viewer {className}"
|
|
34
|
+
data-svadmin-component="LiteJsonViewer"
|
|
35
|
+
data-initial-depth={initialDepth}
|
|
36
|
+
role="region"
|
|
37
|
+
aria-label={title ?? 'JSON Viewer'}
|
|
38
|
+
>
|
|
39
|
+
{#if title}
|
|
40
|
+
<header class="lite-json-header">
|
|
41
|
+
<h4 class="lite-json-title">{title}</h4>
|
|
42
|
+
<span class="lite-json-meta">{itemCount}</span>
|
|
43
|
+
</header>
|
|
44
|
+
{/if}
|
|
45
|
+
|
|
46
|
+
<div class="lite-json-body">
|
|
47
|
+
<div class="lite-json-details">
|
|
48
|
+
<div class="lite-json-summary">
|
|
49
|
+
<span>Payload Tree ({itemCount})</span>
|
|
50
|
+
</div>
|
|
51
|
+
<pre class="lite-json-code"><code>{formattedJson}</code></pre>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<style>
|
|
57
|
+
.lite-json-viewer {
|
|
58
|
+
border: 1px solid #e5e6eb;
|
|
59
|
+
border-radius: 6px;
|
|
60
|
+
background: #f7f8fa;
|
|
61
|
+
font-family: monospace;
|
|
62
|
+
font-size: 12px;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
}
|
|
65
|
+
.lite-json-header {
|
|
66
|
+
display: flex;
|
|
67
|
+
justify-content: space-between;
|
|
68
|
+
align-items: center;
|
|
69
|
+
padding: 8px 12px;
|
|
70
|
+
border-bottom: 1px solid #e5e6eb;
|
|
71
|
+
background: #ffffff;
|
|
72
|
+
}
|
|
73
|
+
.lite-json-title { margin: 0; font-size: 13px; font-weight: 600; color: #1d2129; }
|
|
74
|
+
.lite-json-meta { font-size: 11px; color: #86909c; }
|
|
75
|
+
.lite-json-body { padding: 10px 12px; max-height: 400px; overflow: auto; }
|
|
76
|
+
.lite-json-summary { cursor: pointer; color: #165dff; font-weight: 500; margin-bottom: 6px; outline: none; }
|
|
77
|
+
.lite-json-code {
|
|
78
|
+
margin: 0;
|
|
79
|
+
padding: 8px;
|
|
80
|
+
background: #ffffff;
|
|
81
|
+
border: 1px solid #e5e6eb;
|
|
82
|
+
border-radius: 4px;
|
|
83
|
+
color: #1d2129;
|
|
84
|
+
white-space: pre-wrap;
|
|
85
|
+
word-break: break-all;
|
|
86
|
+
line-height: 1.5;
|
|
87
|
+
}
|
|
88
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
data: unknown;
|
|
3
|
+
initialDepth?: number;
|
|
4
|
+
title?: string;
|
|
5
|
+
class?: string;
|
|
6
|
+
}
|
|
7
|
+
declare const LiteJsonViewer: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type LiteJsonViewer = ReturnType<typeof LiteJsonViewer>;
|
|
9
|
+
export default LiteJsonViewer;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface LiteShortcutItem {
|
|
3
|
+
label: string;
|
|
4
|
+
keys: string[];
|
|
5
|
+
category?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
shortcuts?: LiteShortcutItem[];
|
|
10
|
+
title?: string;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
shortcuts = [
|
|
16
|
+
{ label: '全局搜索 / 命令面板', keys: ['Ctrl', 'K'], category: '导航' },
|
|
17
|
+
{ label: '保存当前表单', keys: ['Ctrl', 'S'], category: '编辑' },
|
|
18
|
+
{ label: '打开快捷键向导', keys: ['?'], category: '系统' },
|
|
19
|
+
],
|
|
20
|
+
title = '快捷键总览',
|
|
21
|
+
class: className = '',
|
|
22
|
+
}: Props = $props();
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<div
|
|
26
|
+
class="lite-shortcuts-card {className}"
|
|
27
|
+
data-svadmin-component="LiteKeyboardShortcutsModal"
|
|
28
|
+
role="region"
|
|
29
|
+
aria-label={title}
|
|
30
|
+
>
|
|
31
|
+
<h4 class="lite-shortcuts-title">{title}</h4>
|
|
32
|
+
<table class="lite-shortcuts-table">
|
|
33
|
+
<tbody>
|
|
34
|
+
{#each shortcuts as item, idx (idx)}
|
|
35
|
+
<tr class="lite-shortcut-row">
|
|
36
|
+
<td class="lite-shortcut-label">{item.label}</td>
|
|
37
|
+
<td class="lite-shortcut-keys">
|
|
38
|
+
{#each item.keys as k, kIdx (kIdx)}
|
|
39
|
+
<kbd class="lite-kbd">{k}</kbd>
|
|
40
|
+
{/each}
|
|
41
|
+
</td>
|
|
42
|
+
</tr>
|
|
43
|
+
{/each}
|
|
44
|
+
</tbody>
|
|
45
|
+
</table>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
.lite-shortcuts-card { border: 1px solid #e5e6eb; border-radius: 8px; background: #ffffff; padding: 16px; }
|
|
50
|
+
.lite-shortcuts-title { font-size: 14px; font-weight: 600; margin: 0 0 12px 0; color: #1d2129; }
|
|
51
|
+
.lite-shortcuts-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
52
|
+
.lite-shortcut-row td { padding: 6px 8px; border-bottom: 1px solid #f2f3f5; }
|
|
53
|
+
.lite-shortcut-label { color: #1d2129; font-weight: 500; }
|
|
54
|
+
.lite-shortcut-keys { text-align: right; }
|
|
55
|
+
.lite-kbd { font-family: monospace; font-size: 11px; background: #f2f3f5; border: 1px solid #e5e6eb; border-radius: 4px; padding: 2px 6px; color: #4e5969; margin-left: 4px; }
|
|
56
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface LiteShortcutItem {
|
|
2
|
+
label: string;
|
|
3
|
+
keys: string[];
|
|
4
|
+
category?: string;
|
|
5
|
+
}
|
|
6
|
+
interface Props {
|
|
7
|
+
shortcuts?: LiteShortcutItem[];
|
|
8
|
+
title?: string;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const LiteKeyboardShortcutsModal: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type LiteKeyboardShortcutsModal = ReturnType<typeof LiteKeyboardShortcutsModal>;
|
|
13
|
+
export default LiteKeyboardShortcutsModal;
|