@ysgroup/ui 0.1.5 → 0.1.7
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/package.json +1 -1
- package/src/components/YsCrudTable.vue +34 -39
- package/src/components/YsPageHeader.vue +80 -0
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -48,8 +48,7 @@ const COLUMN_WIDTHS: Record<string, number> = {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
function columnWidth(field: FieldSpec): number {
|
|
51
|
-
return COLUMN_WIDTHS[field.key]
|
|
52
|
-
?? (field.widget === "switch" ? 64 : field.widget === "enum" ? 100 : 140);
|
|
51
|
+
return COLUMN_WIDTHS[field.key] ?? (field.widget === "switch" ? 64 : field.widget === "enum" ? 100 : 140);
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
const props = defineProps<{
|
|
@@ -65,6 +64,7 @@ const props = defineProps<{
|
|
|
65
64
|
rowDisplay?: (row: Record<string, unknown>, field: string, value: unknown) => unknown;
|
|
66
65
|
saving?: boolean;
|
|
67
66
|
externalEditor?: boolean;
|
|
67
|
+
actionWidth?: number;
|
|
68
68
|
}>();
|
|
69
69
|
|
|
70
70
|
const emit = defineEmits<{
|
|
@@ -81,9 +81,7 @@ const currentPage = ref(1);
|
|
|
81
81
|
const pageSize = ref(20);
|
|
82
82
|
const PAGE_SIZES = [20, 50, 100];
|
|
83
83
|
|
|
84
|
-
const allSpecs = computed<FieldSpec[]>(
|
|
85
|
-
() => (FIELD_SPECS as unknown as Record<string, FieldSpec[]>)[props.table] ?? [],
|
|
86
|
-
);
|
|
84
|
+
const allSpecs = computed<FieldSpec[]>(() => (FIELD_SPECS as unknown as Record<string, FieldSpec[]>)[props.table] ?? []);
|
|
87
85
|
|
|
88
86
|
const displayColumns = computed<FieldSpec[]>(() => {
|
|
89
87
|
if (props.columns?.length) {
|
|
@@ -99,18 +97,25 @@ const filtered = computed(() => {
|
|
|
99
97
|
if (!kw) return props.rows;
|
|
100
98
|
return props.rows.filter((r) =>
|
|
101
99
|
displayColumns.value.some((c) =>
|
|
102
|
-
String(r[c.key] ?? "")
|
|
100
|
+
String(r[c.key] ?? "")
|
|
101
|
+
.toLowerCase()
|
|
102
|
+
.includes(kw),
|
|
103
103
|
),
|
|
104
104
|
);
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
// 搜索词变化时重置到第一页
|
|
108
|
-
watch(keyword, () => {
|
|
109
|
-
|
|
110
|
-
watch(() => filtered.value.length, (len) => {
|
|
111
|
-
const maxPage = Math.max(1, Math.ceil(len / pageSize.value));
|
|
112
|
-
if (currentPage.value > maxPage) currentPage.value = 1;
|
|
108
|
+
watch(keyword, () => {
|
|
109
|
+
currentPage.value = 1;
|
|
113
110
|
});
|
|
111
|
+
// 行数变化(外部数据更新)时若当前页超出则回到第一页
|
|
112
|
+
watch(
|
|
113
|
+
() => filtered.value.length,
|
|
114
|
+
(len) => {
|
|
115
|
+
const maxPage = Math.max(1, Math.ceil(len / pageSize.value));
|
|
116
|
+
if (currentPage.value > maxPage) currentPage.value = 1;
|
|
117
|
+
},
|
|
118
|
+
);
|
|
114
119
|
|
|
115
120
|
const paged = computed(() => {
|
|
116
121
|
const start = (currentPage.value - 1) * pageSize.value;
|
|
@@ -158,33 +163,22 @@ function onSubmit(payload: Record<string, unknown>) {
|
|
|
158
163
|
<template>
|
|
159
164
|
<div class="ys-crud">
|
|
160
165
|
<div class="ys-crud__toolbar">
|
|
161
|
-
<el-input
|
|
162
|
-
v-model="keyword"
|
|
163
|
-
placeholder="搜索"
|
|
164
|
-
clearable
|
|
165
|
-
style="width: 200px"
|
|
166
|
-
/>
|
|
166
|
+
<el-input v-model="keyword" placeholder="搜索" clearable style="width: 200px" />
|
|
167
167
|
<div class="ys-crud__spacer" />
|
|
168
168
|
<el-button v-if="!readonly" type="primary" @click="onCreate">{{ createLabel || "新建" }}</el-button>
|
|
169
169
|
</div>
|
|
170
170
|
|
|
171
171
|
<el-table :data="paged" v-loading="loading" stripe border size="small" table-layout="fixed" row-key="_id">
|
|
172
|
-
<el-table-column
|
|
173
|
-
v-for="c in displayColumns"
|
|
174
|
-
:key="c.key"
|
|
175
|
-
:label="c.title"
|
|
176
|
-
:width="columnWidth(c)"
|
|
177
|
-
show-overflow-tooltip
|
|
178
|
-
>
|
|
172
|
+
<el-table-column v-for="c in displayColumns" :key="c.key" :label="c.title" :width="columnWidth(c)" show-overflow-tooltip>
|
|
179
173
|
<template #default="{ row }">{{ display(row, c) }}</template>
|
|
180
174
|
</el-table-column>
|
|
181
|
-
<el-table-column v-if="!readonly" label="操作" width="
|
|
175
|
+
<el-table-column v-if="!readonly" label="操作" :width="actionWidth ?? 160" fixed="right">
|
|
182
176
|
<template #default="{ row }">
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
</
|
|
177
|
+
<div class="ys-crud__actions">
|
|
178
|
+
<el-button size="small" @click="onEdit(row)">编辑</el-button>
|
|
179
|
+
<slot name="row-actions" :row="row" />
|
|
180
|
+
<el-button size="small" type="danger" plain @click="onRemove(row)">删除</el-button>
|
|
181
|
+
</div>
|
|
188
182
|
</template>
|
|
189
183
|
</el-table-column>
|
|
190
184
|
</el-table>
|
|
@@ -200,15 +194,7 @@ function onSubmit(payload: Record<string, unknown>) {
|
|
|
200
194
|
@size-change="currentPage = 1"
|
|
201
195
|
/>
|
|
202
196
|
|
|
203
|
-
<YsFormDialog
|
|
204
|
-
v-if="!externalEditor"
|
|
205
|
-
v-model="dialogOpen"
|
|
206
|
-
:table="table"
|
|
207
|
-
:record="editing"
|
|
208
|
-
:hidden="hiddenInForm"
|
|
209
|
-
:saving="saving"
|
|
210
|
-
@submit="onSubmit"
|
|
211
|
-
/>
|
|
197
|
+
<YsFormDialog v-if="!externalEditor" v-model="dialogOpen" :table="table" :record="editing" :hidden="hiddenInForm" :saving="saving" @submit="onSubmit" />
|
|
212
198
|
</div>
|
|
213
199
|
</template>
|
|
214
200
|
|
|
@@ -225,4 +211,13 @@ function onSubmit(payload: Record<string, unknown>) {
|
|
|
225
211
|
margin-top: 12px;
|
|
226
212
|
justify-content: flex-end;
|
|
227
213
|
}
|
|
214
|
+
.ys-crud__actions {
|
|
215
|
+
display: flex;
|
|
216
|
+
align-items: center;
|
|
217
|
+
gap: 8px;
|
|
218
|
+
white-space: nowrap;
|
|
219
|
+
}
|
|
220
|
+
.ys-crud__actions :deep(.el-button + .el-button) {
|
|
221
|
+
margin-left: 0;
|
|
222
|
+
}
|
|
228
223
|
</style>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
title: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
meta?: string;
|
|
6
|
+
compact?: boolean;
|
|
7
|
+
}>();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<header class="ys-page-header" :class="{ 'is-compact': compact }">
|
|
12
|
+
<div class="ys-page-header__content">
|
|
13
|
+
<span v-if="meta" class="ys-page-header__meta">{{ meta }}</span>
|
|
14
|
+
<h1>{{ title }}</h1>
|
|
15
|
+
<p v-if="description">{{ description }}</p>
|
|
16
|
+
</div>
|
|
17
|
+
<div v-if="$slots.actions" class="ys-page-header__actions">
|
|
18
|
+
<slot name="actions" />
|
|
19
|
+
</div>
|
|
20
|
+
</header>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<style scoped>
|
|
24
|
+
.ys-page-header {
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: flex-end;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
gap: 24px;
|
|
29
|
+
margin: 0 0 18px;
|
|
30
|
+
}
|
|
31
|
+
.ys-page-header__content {
|
|
32
|
+
min-width: 0;
|
|
33
|
+
}
|
|
34
|
+
.ys-page-header__meta {
|
|
35
|
+
display: block;
|
|
36
|
+
margin-bottom: 5px;
|
|
37
|
+
color: var(--ys-color-primary);
|
|
38
|
+
font-size: 10px;
|
|
39
|
+
font-weight: 700;
|
|
40
|
+
letter-spacing: .08em;
|
|
41
|
+
text-transform: uppercase;
|
|
42
|
+
}
|
|
43
|
+
.ys-page-header h1 {
|
|
44
|
+
margin: 0;
|
|
45
|
+
color: var(--ys-color-text-1);
|
|
46
|
+
font-size: 24px;
|
|
47
|
+
font-weight: 700;
|
|
48
|
+
line-height: 1.25;
|
|
49
|
+
}
|
|
50
|
+
.ys-page-header p {
|
|
51
|
+
max-width: 760px;
|
|
52
|
+
margin: 6px 0 0;
|
|
53
|
+
color: var(--ys-color-text-3);
|
|
54
|
+
font-size: 12px;
|
|
55
|
+
line-height: 1.6;
|
|
56
|
+
}
|
|
57
|
+
.ys-page-header__actions {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex: none;
|
|
60
|
+
align-items: center;
|
|
61
|
+
gap: 8px;
|
|
62
|
+
}
|
|
63
|
+
.ys-page-header.is-compact {
|
|
64
|
+
margin-bottom: 14px;
|
|
65
|
+
}
|
|
66
|
+
.ys-page-header.is-compact h1 {
|
|
67
|
+
font-size: 20px;
|
|
68
|
+
}
|
|
69
|
+
@media (max-width: 720px) {
|
|
70
|
+
.ys-page-header {
|
|
71
|
+
align-items: flex-start;
|
|
72
|
+
flex-direction: column;
|
|
73
|
+
gap: 12px;
|
|
74
|
+
}
|
|
75
|
+
.ys-page-header__actions {
|
|
76
|
+
width: 100%;
|
|
77
|
+
flex-wrap: wrap;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
</style>
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
export { default as YsCrudTable } from "./components/YsCrudTable.vue";
|
|
4
4
|
export { default as YsFormDialog } from "./components/YsFormDialog.vue";
|
|
5
5
|
export { default as YsPageLayout } from "./components/YsPageLayout.vue";
|
|
6
|
+
export { default as YsPageHeader } from "./components/YsPageHeader.vue";
|
|
6
7
|
export { default as YsLoginPage } from "./components/YsLoginPage.vue";
|
|
7
8
|
export { default as YsSettingsCenterDialog } from "./components/YsSettingsCenterDialog.vue";
|
|
8
9
|
export { default as YsPortalGrid } from "./components/YsPortalGrid.vue";
|