@svadmin/lite 0.26.0 → 0.30.1
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/LiteAbacPolicyMatrix.svelte +128 -0
- package/dist/components/LiteAbacPolicyMatrix.svelte.d.ts +16 -0
- package/dist/components/LiteAnnouncementBanner.svelte +88 -0
- package/dist/components/LiteAnnouncementBanner.svelte.d.ts +12 -0
- package/dist/components/LiteApiPlayground.svelte +103 -0
- package/dist/components/LiteApiPlayground.svelte.d.ts +10 -0
- package/dist/components/LiteApprovalChainBuilder.svelte +120 -0
- package/dist/components/LiteApprovalChainBuilder.svelte.d.ts +15 -0
- package/dist/components/LiteAsyncExportModal.svelte +113 -0
- package/dist/components/LiteAsyncExportModal.svelte.d.ts +14 -0
- package/dist/components/LiteAuditTrailInspector.svelte +166 -0
- package/dist/components/LiteAuditTrailInspector.svelte.d.ts +23 -0
- package/dist/components/LiteBillingInvoiceDrawer.svelte +100 -0
- package/dist/components/LiteBillingInvoiceDrawer.svelte.d.ts +16 -0
- package/dist/components/LiteChartAnnotationOverlay.svelte +101 -0
- package/dist/components/LiteChartAnnotationOverlay.svelte.d.ts +16 -0
- package/dist/components/LiteColumnMappingTable.svelte +105 -0
- package/dist/components/LiteColumnMappingTable.svelte.d.ts +14 -0
- package/dist/components/LiteContextMenu.svelte +114 -0
- package/dist/components/LiteContextMenu.svelte.d.ts +18 -0
- package/dist/components/LiteDashboardGridLayout.svelte +108 -0
- package/dist/components/LiteDashboardGridLayout.svelte.d.ts +16 -0
- package/dist/components/LiteDashboardLayoutStorage.svelte +88 -0
- package/dist/components/LiteDashboardLayoutStorage.svelte.d.ts +16 -0
- package/dist/components/LiteDataImportDropzone.svelte +89 -0
- package/dist/components/LiteDataImportDropzone.svelte.d.ts +10 -0
- package/dist/components/LiteDataLineageGraph.svelte +100 -0
- package/dist/components/LiteDataLineageGraph.svelte.d.ts +15 -0
- package/dist/components/LiteDataMaskPolicyEngine.svelte +131 -0
- package/dist/components/LiteDataMaskPolicyEngine.svelte.d.ts +16 -0
- package/dist/components/LiteDataScreenContainer.svelte +34 -0
- package/dist/components/LiteDataScreenContainer.svelte.d.ts +9 -0
- package/dist/components/LiteDualApprovalGate.svelte +58 -0
- package/dist/components/LiteDualApprovalGate.svelte.d.ts +11 -0
- package/dist/components/LiteElectronicSealStamp.svelte +56 -0
- package/dist/components/LiteElectronicSealStamp.svelte.d.ts +9 -0
- package/dist/components/LiteEnvVariableEditor.svelte +114 -0
- package/dist/components/LiteEnvVariableEditor.svelte.d.ts +15 -0
- package/dist/components/LiteFormulaField.svelte +44 -0
- package/dist/components/LiteFormulaField.svelte.d.ts +11 -0
- package/dist/components/LiteGeoScatterMap.svelte +117 -0
- package/dist/components/LiteGeoScatterMap.svelte.d.ts +16 -0
- package/dist/components/LiteHmacSignatureCalculator.svelte +84 -0
- package/dist/components/LiteHmacSignatureCalculator.svelte.d.ts +10 -0
- package/dist/components/LiteImportPreviewValidator.svelte +136 -0
- package/dist/components/LiteImportPreviewValidator.svelte.d.ts +16 -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/LiteLiveStreamLogViewer.svelte +84 -0
- package/dist/components/LiteLiveStreamLogViewer.svelte.d.ts +14 -0
- package/dist/components/LiteOmnibarCommandPalette.svelte +111 -0
- package/dist/components/LiteOmnibarCommandPalette.svelte.d.ts +15 -0
- package/dist/components/LitePeriodComparisonCard.svelte +60 -0
- package/dist/components/LitePeriodComparisonCard.svelte.d.ts +11 -0
- package/dist/components/LitePermissionSimulator.svelte +90 -0
- package/dist/components/LitePermissionSimulator.svelte.d.ts +14 -0
- package/dist/components/LitePivotMatrix.svelte +138 -0
- package/dist/components/LitePivotMatrix.svelte.d.ts +18 -0
- package/dist/components/LitePlanUsageQuotaBar.svelte +96 -0
- package/dist/components/LitePlanUsageQuotaBar.svelte.d.ts +15 -0
- package/dist/components/LiteQueryAiExplainer.svelte +85 -0
- package/dist/components/LiteQueryAiExplainer.svelte.d.ts +10 -0
- package/dist/components/LiteResultPage.svelte +135 -0
- package/dist/components/LiteResultPage.svelte.d.ts +17 -0
- package/dist/components/LiteScheduledJobManager.svelte +116 -0
- package/dist/components/LiteScheduledJobManager.svelte.d.ts +16 -0
- package/dist/components/LiteScreenAntiLeakGuard.svelte +56 -0
- package/dist/components/LiteScreenAntiLeakGuard.svelte.d.ts +8 -0
- package/dist/components/LiteSecureDataExporter.svelte +101 -0
- package/dist/components/LiteSecureDataExporter.svelte.d.ts +12 -0
- package/dist/components/LiteSensitiveDataMask.svelte +34 -2
- package/dist/components/LiteSensitiveDataMask.svelte.d.ts +1 -2
- package/dist/components/LiteSqlConsole.svelte +128 -0
- package/dist/components/LiteSqlConsole.svelte.d.ts +16 -0
- package/dist/components/LiteStepWizard.svelte +98 -0
- package/dist/components/LiteStepWizard.svelte.d.ts +16 -0
- package/dist/components/LiteSystemCapacityForecastCard.svelte +88 -0
- package/dist/components/LiteSystemCapacityForecastCard.svelte.d.ts +11 -0
- package/dist/components/LiteSystemHealthCheckDashboard.svelte +142 -0
- package/dist/components/LiteSystemHealthCheckDashboard.svelte.d.ts +18 -0
- package/dist/components/LiteTableAggregationFooter.svelte +76 -0
- package/dist/components/LiteTableAggregationFooter.svelte.d.ts +14 -0
- package/dist/components/LiteTenantContextSwitcher.svelte +173 -0
- package/dist/components/LiteTenantContextSwitcher.svelte.d.ts +27 -0
- package/dist/components/LiteTenantQuotasMatrix.svelte +117 -0
- package/dist/components/LiteTenantQuotasMatrix.svelte.d.ts +17 -0
- package/dist/components/LiteTextDiffViewer.svelte +111 -0
- package/dist/components/LiteTextDiffViewer.svelte.d.ts +10 -0
- package/dist/components/LiteThreadedCommentDrawer.svelte +60 -0
- package/dist/components/LiteThreadedCommentDrawer.svelte.d.ts +15 -0
- package/dist/components/LiteTierFeatureComparison.svelte +97 -0
- package/dist/components/LiteTierFeatureComparison.svelte.d.ts +18 -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/LiteWebhookSimulator.svelte +120 -0
- package/dist/components/LiteWebhookSimulator.svelte.d.ts +11 -0
- package/dist/components/LiteWorkflowNodeInspector.svelte +79 -0
- package/dist/components/LiteWorkflowNodeInspector.svelte.d.ts +11 -0
- package/dist/components/LiteWorkflowStepper.svelte +56 -0
- package/dist/components/LiteWorkflowStepper.svelte.d.ts +14 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +61 -0
- package/package.json +2 -2
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface QuotaItem {
|
|
3
|
+
id: string;
|
|
4
|
+
resourceName: string;
|
|
5
|
+
used: number;
|
|
6
|
+
limit: number;
|
|
7
|
+
unit: string;
|
|
8
|
+
status?: 'normal' | 'warning' | 'exhausted';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
tenantName?: string;
|
|
13
|
+
tierName?: string;
|
|
14
|
+
quotas?: QuotaItem[];
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
tenantName = '上海创智多租户研发中心',
|
|
20
|
+
tierName = '旗舰尊享版 (Enterprise Tier)',
|
|
21
|
+
quotas = [
|
|
22
|
+
{ id: 'q1', resourceName: 'API 请求吞吐', used: 840000, limit: 1000000, unit: 'req/天', status: 'normal' },
|
|
23
|
+
{ id: 'q2', resourceName: '分布式对象存储', used: 480, limit: 500, unit: 'GB', status: 'warning' },
|
|
24
|
+
{ id: 'q3', resourceName: '组织席位数', used: 120, limit: 120, unit: '席位', status: 'exhausted' },
|
|
25
|
+
{ id: 'q4', resourceName: 'AI 智能算力点', used: 3200, limit: 10000, unit: '点', status: 'normal' }
|
|
26
|
+
],
|
|
27
|
+
class: className = '',
|
|
28
|
+
}: Props = $props();
|
|
29
|
+
|
|
30
|
+
function calcPct(u: number, l: number): number {
|
|
31
|
+
if (l <= 0) return 0;
|
|
32
|
+
return Math.min(100, Math.round((u / l) * 100));
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<div
|
|
37
|
+
class="lite-tenant-quotas {className}"
|
|
38
|
+
data-svadmin-component="LiteTenantQuotasMatrix"
|
|
39
|
+
role="region"
|
|
40
|
+
aria-label="Tenant Quotas Matrix"
|
|
41
|
+
>
|
|
42
|
+
<header class="lite-quotas-header">
|
|
43
|
+
<div>
|
|
44
|
+
<h4 class="lite-quotas-tenant">{tenantName}</h4>
|
|
45
|
+
<span class="lite-quotas-tier">{tierName}</span>
|
|
46
|
+
</div>
|
|
47
|
+
</header>
|
|
48
|
+
|
|
49
|
+
<div class="lite-quotas-grid">
|
|
50
|
+
{#each quotas as q (q.id)}
|
|
51
|
+
{@const pct = calcPct(q.used, q.limit)}
|
|
52
|
+
<div class="lite-quota-card">
|
|
53
|
+
<div class="lite-card-top">
|
|
54
|
+
<span class="lite-card-name">{q.resourceName}</span>
|
|
55
|
+
<span class="lite-card-usage">{q.used.toLocaleString()} / {q.limit.toLocaleString()} {q.unit}</span>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="lite-card-track">
|
|
58
|
+
<div
|
|
59
|
+
class="lite-card-bar"
|
|
60
|
+
class:lite-bar-warn={pct >= 85 && pct < 100}
|
|
61
|
+
class:lite-bar-danger={pct >= 100}
|
|
62
|
+
style="width: {pct}%;"
|
|
63
|
+
></div>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="lite-card-bottom">
|
|
66
|
+
<span class="lite-card-pct">{pct}% 已用</span>
|
|
67
|
+
{#if pct >= 100}
|
|
68
|
+
<span class="lite-card-state text-danger">已达硬配额上限</span>
|
|
69
|
+
{:else if pct >= 85}
|
|
70
|
+
<span class="lite-card-state text-warn">接近用量预警线</span>
|
|
71
|
+
{:else}
|
|
72
|
+
<span class="lite-card-state text-ok">配额容量充裕</span>
|
|
73
|
+
{/if}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
{/each}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<style>
|
|
81
|
+
.lite-tenant-quotas {
|
|
82
|
+
border: 1px solid #e5e6eb;
|
|
83
|
+
border-radius: 6px;
|
|
84
|
+
background: #ffffff;
|
|
85
|
+
font-size: 12px;
|
|
86
|
+
overflow: hidden;
|
|
87
|
+
}
|
|
88
|
+
.lite-quotas-header {
|
|
89
|
+
padding: 10px 14px;
|
|
90
|
+
background: #f7f8fa;
|
|
91
|
+
border-bottom: 1px solid #e5e6eb;
|
|
92
|
+
}
|
|
93
|
+
.lite-quotas-tenant { margin: 0; font-size: 13px; font-weight: 600; color: #1d2129; }
|
|
94
|
+
.lite-quotas-tier { font-size: 11px; color: #165dff; font-weight: 500; display: block; margin-top: 2px; }
|
|
95
|
+
.lite-quotas-grid { padding: 12px 14px; overflow: hidden; }
|
|
96
|
+
.lite-quotas-grid .lite-quota-card { float: left; width: calc(50% - 6px); margin: 0 12px 12px 0; box-sizing: border-box; }
|
|
97
|
+
.lite-quotas-grid .lite-quota-card:nth-child(2n) { margin-right: 0; }
|
|
98
|
+
@media (max-width: 640px) { .lite-quotas-grid .lite-quota-card { float: none; width: 100%; margin-right: 0; } }
|
|
99
|
+
.lite-quota-card {
|
|
100
|
+
border: 1px solid #f2f3f5;
|
|
101
|
+
border-radius: 4px;
|
|
102
|
+
padding: 10px;
|
|
103
|
+
background: #fafbfc;
|
|
104
|
+
}
|
|
105
|
+
.lite-card-top { display: flex; justify-content: space-between; margin-bottom: 6px; font-size: 11px; }
|
|
106
|
+
.lite-card-name { font-weight: 600; color: #1d2129; }
|
|
107
|
+
.lite-card-usage { font-family: monospace; color: #4e5969; }
|
|
108
|
+
.lite-card-track { height: 6px; background: #e5e6eb; border-radius: 3px; overflow: hidden; margin-bottom: 6px; }
|
|
109
|
+
.lite-card-bar { height: 100%; background: #165dff; border-radius: 3px; }
|
|
110
|
+
.lite-bar-warn { background: #ff7d00; }
|
|
111
|
+
.lite-bar-danger { background: #f53f3f; }
|
|
112
|
+
.lite-card-bottom { display: flex; justify-content: space-between; font-size: 10px; }
|
|
113
|
+
.lite-card-pct { font-family: monospace; font-weight: 600; color: #1d2129; }
|
|
114
|
+
.text-danger { color: #f53f3f; font-weight: 600; }
|
|
115
|
+
.text-warn { color: #ff7d00; font-weight: 600; }
|
|
116
|
+
.text-ok { color: #00b42a; }
|
|
117
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface QuotaItem {
|
|
2
|
+
id: string;
|
|
3
|
+
resourceName: string;
|
|
4
|
+
used: number;
|
|
5
|
+
limit: number;
|
|
6
|
+
unit: string;
|
|
7
|
+
status?: 'normal' | 'warning' | 'exhausted';
|
|
8
|
+
}
|
|
9
|
+
interface Props {
|
|
10
|
+
tenantName?: string;
|
|
11
|
+
tierName?: string;
|
|
12
|
+
quotas?: QuotaItem[];
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const LiteTenantQuotasMatrix: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
type LiteTenantQuotasMatrix = ReturnType<typeof LiteTenantQuotasMatrix>;
|
|
17
|
+
export default LiteTenantQuotasMatrix;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
oldText?: string;
|
|
4
|
+
newText?: string;
|
|
5
|
+
oldTitle?: string;
|
|
6
|
+
newTitle?: string;
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
oldText = '',
|
|
12
|
+
newText = '',
|
|
13
|
+
oldTitle = 'Baseline',
|
|
14
|
+
newTitle = 'Current',
|
|
15
|
+
class: className = '',
|
|
16
|
+
}: Props = $props();
|
|
17
|
+
|
|
18
|
+
interface DiffEntry {
|
|
19
|
+
type: 'same' | 'add' | 'del';
|
|
20
|
+
lineNo: number;
|
|
21
|
+
text: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const diffEntries = $derived.by<DiffEntry[]>(() => {
|
|
25
|
+
const oldLines = oldText.split(/\r?\n/u);
|
|
26
|
+
const newLines = newText.split(/\r?\n/u);
|
|
27
|
+
const result: DiffEntry[] = [];
|
|
28
|
+
|
|
29
|
+
let lineCounter = 1;
|
|
30
|
+
let i = 0;
|
|
31
|
+
let j = 0;
|
|
32
|
+
|
|
33
|
+
while (i < oldLines.length && j < newLines.length) {
|
|
34
|
+
if (oldLines[i] === newLines[j]) {
|
|
35
|
+
result.push({ type: 'same', lineNo: lineCounter++, text: oldLines[i] ?? '' });
|
|
36
|
+
i++;
|
|
37
|
+
j++;
|
|
38
|
+
} else {
|
|
39
|
+
result.push({ type: 'del', lineNo: lineCounter++, text: oldLines[i] ?? '' });
|
|
40
|
+
result.push({ type: 'add', lineNo: lineCounter++, text: newLines[j] ?? '' });
|
|
41
|
+
i++;
|
|
42
|
+
j++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
while (i < oldLines.length) {
|
|
46
|
+
result.push({ type: 'del', lineNo: lineCounter++, text: oldLines[i] ?? '' });
|
|
47
|
+
i++;
|
|
48
|
+
}
|
|
49
|
+
while (j < newLines.length) {
|
|
50
|
+
result.push({ type: 'add', lineNo: lineCounter++, text: newLines[j] ?? '' });
|
|
51
|
+
j++;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return result;
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<div
|
|
59
|
+
class="lite-text-diff {className}"
|
|
60
|
+
data-svadmin-component="LiteTextDiffViewer"
|
|
61
|
+
role="region"
|
|
62
|
+
aria-label="Text Diff"
|
|
63
|
+
>
|
|
64
|
+
<header class="lite-diff-header">
|
|
65
|
+
<span class="lite-diff-title">{oldTitle} ↔ {newTitle}</span>
|
|
66
|
+
</header>
|
|
67
|
+
|
|
68
|
+
<div class="lite-diff-body">
|
|
69
|
+
<table class="lite-diff-table">
|
|
70
|
+
<tbody>
|
|
71
|
+
{#each diffEntries as entry (entry.lineNo)}
|
|
72
|
+
<tr class="lite-diff-row lite-diff-{entry.type}">
|
|
73
|
+
<td class="lite-diff-line-no">{entry.lineNo}</td>
|
|
74
|
+
<td class="lite-diff-sign">
|
|
75
|
+
{#if entry.type === 'add'}+{/if}
|
|
76
|
+
{#if entry.type === 'del'}-{/if}
|
|
77
|
+
</td>
|
|
78
|
+
<td class="lite-diff-content">{entry.text}</td>
|
|
79
|
+
</tr>
|
|
80
|
+
{/each}
|
|
81
|
+
</tbody>
|
|
82
|
+
</table>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<style>
|
|
87
|
+
.lite-text-diff {
|
|
88
|
+
border: 1px solid #e5e6eb;
|
|
89
|
+
border-radius: 6px;
|
|
90
|
+
background: #ffffff;
|
|
91
|
+
font-family: monospace;
|
|
92
|
+
font-size: 12px;
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
}
|
|
95
|
+
.lite-diff-header {
|
|
96
|
+
padding: 8px 12px;
|
|
97
|
+
background: #f7f8fa;
|
|
98
|
+
border-bottom: 1px solid #e5e6eb;
|
|
99
|
+
font-weight: 600;
|
|
100
|
+
color: #4e5969;
|
|
101
|
+
}
|
|
102
|
+
.lite-diff-body { max-height: 400px; overflow: auto; }
|
|
103
|
+
.lite-diff-table { width: 100%; border-collapse: collapse; }
|
|
104
|
+
.lite-diff-row td { padding: 2px 6px; }
|
|
105
|
+
.lite-diff-line-no { width: 40px; text-align: right; color: #86909c; user-select: none; border-right: 1px solid #f2f3f5; }
|
|
106
|
+
.lite-diff-sign { width: 20px; text-align: center; font-weight: 700; user-select: none; }
|
|
107
|
+
.lite-diff-content { white-space: pre-wrap; word-break: break-all; }
|
|
108
|
+
.lite-diff-add { background: #e8ffea; color: #00b42a; }
|
|
109
|
+
.lite-diff-del { background: #ffece8; color: #f53f3f; }
|
|
110
|
+
.lite-diff-same { color: #1d2129; }
|
|
111
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
oldText?: string;
|
|
3
|
+
newText?: string;
|
|
4
|
+
oldTitle?: string;
|
|
5
|
+
newTitle?: string;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const LiteTextDiffViewer: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type LiteTextDiffViewer = ReturnType<typeof LiteTextDiffViewer>;
|
|
10
|
+
export default LiteTextDiffViewer;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export interface LiteComment {
|
|
3
|
+
id: string;
|
|
4
|
+
author: string;
|
|
5
|
+
content: string;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
comments?: LiteComment[];
|
|
11
|
+
title?: string;
|
|
12
|
+
action?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
comments = [],
|
|
18
|
+
title = 'Comments',
|
|
19
|
+
action = '/comments/create',
|
|
20
|
+
class: className = '',
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div
|
|
25
|
+
class="lite-comment-box {className}"
|
|
26
|
+
data-svadmin-component="LiteThreadedCommentDrawer"
|
|
27
|
+
>
|
|
28
|
+
<h4 class="lite-comment-title">{title}</h4>
|
|
29
|
+
<ul class="lite-comment-list">
|
|
30
|
+
{#each comments as c (c.id)}
|
|
31
|
+
<li class="lite-comment-item">
|
|
32
|
+
<div class="lite-comment-head">
|
|
33
|
+
<span class="lite-comment-author">{c.author}</span>
|
|
34
|
+
<span class="lite-comment-time">{c.createdAt}</span>
|
|
35
|
+
</div>
|
|
36
|
+
<p class="lite-comment-content">{c.content}</p>
|
|
37
|
+
</li>
|
|
38
|
+
{/each}
|
|
39
|
+
</ul>
|
|
40
|
+
|
|
41
|
+
<form {action} method="POST" class="lite-comment-form">
|
|
42
|
+
<textarea name="content" required placeholder="Leave a reply..." class="lite-comment-input"></textarea>
|
|
43
|
+
<button type="submit" class="lite-comment-btn">Submit Reply</button>
|
|
44
|
+
</form>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<style>
|
|
48
|
+
.lite-comment-box { border: 1px solid #e5e6eb; border-radius: 8px; background: #ffffff; padding: 16px; font-size: 12px; }
|
|
49
|
+
.lite-comment-title { font-size: 14px; font-weight: 600; margin: 0 0 12px 0; color: #1d2129; }
|
|
50
|
+
.lite-comment-list { list-style: none; margin: 0 0 16px 0; padding: 0; }
|
|
51
|
+
.lite-comment-item { border-bottom: 1px solid #f2f3f5; padding: 8px 0; }
|
|
52
|
+
.lite-comment-head { display: flex; justify-content: space-between; margin-bottom: 4px; }
|
|
53
|
+
.lite-comment-author { font-weight: 600; color: #1d2129; }
|
|
54
|
+
.lite-comment-time { color: #86909c; font-size: 11px; }
|
|
55
|
+
.lite-comment-content { margin: 0; color: #4e5969; }
|
|
56
|
+
.lite-comment-form { display: flex; flex-direction: column; }
|
|
57
|
+
.lite-comment-form > * + * { margin-top: 8px; }
|
|
58
|
+
.lite-comment-input { height: 60px; border: 1px solid #e5e6eb; border-radius: 4px; padding: 6px 10px; font-size: 12px; }
|
|
59
|
+
.lite-comment-btn { height: 32px; background: #165dff; color: #fff; border: none; border-radius: 4px; font-size: 12px; font-weight: 600; cursor: pointer; }
|
|
60
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface LiteComment {
|
|
2
|
+
id: string;
|
|
3
|
+
author: string;
|
|
4
|
+
content: string;
|
|
5
|
+
createdAt: string;
|
|
6
|
+
}
|
|
7
|
+
interface Props {
|
|
8
|
+
comments?: LiteComment[];
|
|
9
|
+
title?: string;
|
|
10
|
+
action?: string;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
declare const LiteThreadedCommentDrawer: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type LiteThreadedCommentDrawer = ReturnType<typeof LiteThreadedCommentDrawer>;
|
|
15
|
+
export default LiteThreadedCommentDrawer;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Tier {
|
|
3
|
+
key: string;
|
|
4
|
+
name: string;
|
|
5
|
+
price: string;
|
|
6
|
+
isCurrent?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Feature {
|
|
10
|
+
name: string;
|
|
11
|
+
values: Record<string, boolean | string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
tiers?: Tier[];
|
|
16
|
+
features?: Feature[];
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
tiers = [
|
|
22
|
+
{ key: 'starter', name: 'Starter', price: '$29/mo', isCurrent: true },
|
|
23
|
+
{ key: 'pro', name: 'Pro', price: '$99/mo' },
|
|
24
|
+
],
|
|
25
|
+
features = [
|
|
26
|
+
{ name: 'Multi-tenant', values: { starter: true, pro: true } },
|
|
27
|
+
{ name: 'Seats', values: { starter: '5', pro: '50' } },
|
|
28
|
+
{ name: 'Dedicated support', values: { starter: false, pro: true } },
|
|
29
|
+
],
|
|
30
|
+
class: className = '',
|
|
31
|
+
}: Props = $props();
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div
|
|
35
|
+
class="lite-tier-comparison {className}"
|
|
36
|
+
data-svadmin-component="LiteTierFeatureComparison"
|
|
37
|
+
role="region"
|
|
38
|
+
aria-label="Tier Feature Comparison"
|
|
39
|
+
>
|
|
40
|
+
<table class="lite-tier-table">
|
|
41
|
+
<thead>
|
|
42
|
+
<tr>
|
|
43
|
+
<th class="lite-tier-feat-th">功能特性</th>
|
|
44
|
+
{#each tiers as t (t.key)}
|
|
45
|
+
<th class="lite-tier-th">
|
|
46
|
+
<span class="lite-tier-name">{t.name}</span>
|
|
47
|
+
<span class="lite-tier-price">{t.price}</span>
|
|
48
|
+
{#if t.isCurrent}
|
|
49
|
+
<span class="lite-tier-current">当前套餐</span>
|
|
50
|
+
{/if}
|
|
51
|
+
</th>
|
|
52
|
+
{/each}
|
|
53
|
+
</tr>
|
|
54
|
+
</thead>
|
|
55
|
+
<tbody>
|
|
56
|
+
{#each features as feat (feat.name)}
|
|
57
|
+
<tr>
|
|
58
|
+
<td class="lite-tier-feat-td">{feat.name}</td>
|
|
59
|
+
{#each tiers as t (t.key)}
|
|
60
|
+
{@const val = feat.values[t.key]}
|
|
61
|
+
<td class="lite-tier-val-td">
|
|
62
|
+
{#if val === true}
|
|
63
|
+
<span class="lite-check">✓</span>
|
|
64
|
+
{:else if val === false}
|
|
65
|
+
<span class="lite-dash">—</span>
|
|
66
|
+
{:else}
|
|
67
|
+
<span class="lite-val-text">{val}</span>
|
|
68
|
+
{/if}
|
|
69
|
+
</td>
|
|
70
|
+
{/each}
|
|
71
|
+
</tr>
|
|
72
|
+
{/each}
|
|
73
|
+
</tbody>
|
|
74
|
+
</table>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<style>
|
|
78
|
+
.lite-tier-comparison {
|
|
79
|
+
border: 1px solid #e5e6eb;
|
|
80
|
+
border-radius: 6px;
|
|
81
|
+
background: #ffffff;
|
|
82
|
+
overflow-x: auto;
|
|
83
|
+
font-size: 12px;
|
|
84
|
+
}
|
|
85
|
+
.lite-tier-table { width: 100%; border-collapse: collapse; }
|
|
86
|
+
.lite-tier-table th, .lite-tier-table td { padding: 8px 12px; border-bottom: 1px solid #e5e6eb; }
|
|
87
|
+
.lite-tier-feat-th { text-align: left; color: #4e5969; font-weight: 600; width: 40%; }
|
|
88
|
+
.lite-tier-th { text-align: center; background: #f7f8fa; width: 30%; }
|
|
89
|
+
.lite-tier-name { display: block; font-weight: 700; color: #1d2129; }
|
|
90
|
+
.lite-tier-price { display: block; font-family: monospace; color: #165dff; font-weight: 600; margin-top: 2px; }
|
|
91
|
+
.lite-tier-current { display: inline-block; font-size: 10px; background: #e5e6eb; color: #4e5969; padding: 1px 6px; border-radius: 3px; margin-top: 4px; }
|
|
92
|
+
.lite-tier-feat-td { color: #1d2129; font-weight: 500; }
|
|
93
|
+
.lite-tier-val-td { text-align: center; }
|
|
94
|
+
.lite-check { color: #00b42a; font-weight: 700; }
|
|
95
|
+
.lite-dash { color: #86909c; }
|
|
96
|
+
.lite-val-text { font-family: monospace; font-weight: 600; color: #1d2129; }
|
|
97
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface Tier {
|
|
2
|
+
key: string;
|
|
3
|
+
name: string;
|
|
4
|
+
price: string;
|
|
5
|
+
isCurrent?: boolean;
|
|
6
|
+
}
|
|
7
|
+
interface Feature {
|
|
8
|
+
name: string;
|
|
9
|
+
values: Record<string, boolean | string>;
|
|
10
|
+
}
|
|
11
|
+
interface Props {
|
|
12
|
+
tiers?: Tier[];
|
|
13
|
+
features?: Feature[];
|
|
14
|
+
class?: string;
|
|
15
|
+
}
|
|
16
|
+
declare const LiteTierFeatureComparison: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type LiteTierFeatureComparison = ReturnType<typeof LiteTierFeatureComparison>;
|
|
18
|
+
export default LiteTierFeatureComparison;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
hasChanges?: boolean;
|
|
6
|
+
message?: string;
|
|
7
|
+
class?: string;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
hasChanges = false,
|
|
13
|
+
message = 'You have unsaved changes that may be lost.',
|
|
14
|
+
class: className = '',
|
|
15
|
+
children,
|
|
16
|
+
}: Props = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div
|
|
20
|
+
class="lite-unsaved-guard {className}"
|
|
21
|
+
data-svadmin-component="LiteUnsavedChangesGuard"
|
|
22
|
+
data-dirty={hasChanges ? 'true' : 'false'}
|
|
23
|
+
>
|
|
24
|
+
{#if hasChanges}
|
|
25
|
+
<aside class="lite-unsaved-banner" role="status">
|
|
26
|
+
<span class="lite-unsaved-icon">⚠️</span>
|
|
27
|
+
<span class="lite-unsaved-text">{message}</span>
|
|
28
|
+
</aside>
|
|
29
|
+
{/if}
|
|
30
|
+
|
|
31
|
+
<div class="lite-unsaved-content">
|
|
32
|
+
{#if children}{@render children()}{/if}
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<style>
|
|
37
|
+
.lite-unsaved-guard { display: block; }
|
|
38
|
+
.lite-unsaved-banner {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
padding: 8px 12px;
|
|
42
|
+
background: #fff7e8;
|
|
43
|
+
border: 1px solid #ffd591;
|
|
44
|
+
border-radius: 6px;
|
|
45
|
+
margin-bottom: 12px;
|
|
46
|
+
font-size: 13px;
|
|
47
|
+
color: #d46b08;
|
|
48
|
+
}
|
|
49
|
+
.lite-unsaved-banner > * + * { margin-left: 8px; }
|
|
50
|
+
.lite-unsaved-icon { font-size: 14px; }
|
|
51
|
+
.lite-unsaved-text { font-weight: 500; }
|
|
52
|
+
.lite-unsaved-content { position: relative; }
|
|
53
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
hasChanges?: boolean;
|
|
4
|
+
message?: string;
|
|
5
|
+
class?: string;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const LiteUnsavedChangesGuard: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type LiteUnsavedChangesGuard = ReturnType<typeof LiteUnsavedChangesGuard>;
|
|
10
|
+
export default LiteUnsavedChangesGuard;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
content?: string | string[];
|
|
6
|
+
class?: string;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
content = 'SVAdmin Lite',
|
|
12
|
+
class: className = '',
|
|
13
|
+
children,
|
|
14
|
+
}: Props = $props();
|
|
15
|
+
|
|
16
|
+
const watermarkText = $derived(
|
|
17
|
+
Array.isArray(content) ? content.join(' ') : String(content)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const encodedSvg = $derived.by(() => {
|
|
21
|
+
const escaped = watermarkText
|
|
22
|
+
.replace(/&/g, '&')
|
|
23
|
+
.replace(/</g, '<')
|
|
24
|
+
.replace(/>/g, '>')
|
|
25
|
+
.replace(/"/g, '"');
|
|
26
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="220" height="120" viewBox="0 0 220 120"><text x="20" y="60" fill="rgba(0,0,0,0.07)" font-family="sans-serif" font-size="12" transform="rotate(-18 110 60)">${escaped}</text></svg>`;
|
|
27
|
+
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
|
|
28
|
+
});
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<div
|
|
32
|
+
class="lite-watermark-container {className}"
|
|
33
|
+
data-svadmin-component="LiteWatermarkContainer"
|
|
34
|
+
style="--lite-watermark-pattern: url('{encodedSvg}');"
|
|
35
|
+
>
|
|
36
|
+
<div class="lite-watermark-overlay" aria-hidden="true" style="background-image: url('{encodedSvg}');"></div>
|
|
37
|
+
<div class="lite-watermark-content">
|
|
38
|
+
{#if children}{@render children()}{/if}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.lite-watermark-container {
|
|
44
|
+
position: relative;
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
}
|
|
47
|
+
.lite-watermark-overlay {
|
|
48
|
+
position: absolute;
|
|
49
|
+
inset: 0;
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
z-index: 9;
|
|
52
|
+
background-repeat: repeat;
|
|
53
|
+
}
|
|
54
|
+
.lite-watermark-content {
|
|
55
|
+
position: relative;
|
|
56
|
+
z-index: 1;
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
content?: string | string[];
|
|
4
|
+
class?: string;
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
}
|
|
7
|
+
declare const LiteWatermarkContainer: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type LiteWatermarkContainer = ReturnType<typeof LiteWatermarkContainer>;
|
|
9
|
+
export default LiteWatermarkContainer;
|