@ysgroup/ui 0.1.6 → 0.1.8
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 +33 -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<{
|
|
@@ -82,9 +81,7 @@ const currentPage = ref(1);
|
|
|
82
81
|
const pageSize = ref(20);
|
|
83
82
|
const PAGE_SIZES = [20, 50, 100];
|
|
84
83
|
|
|
85
|
-
const allSpecs = computed<FieldSpec[]>(
|
|
86
|
-
() => (FIELD_SPECS as unknown as Record<string, FieldSpec[]>)[props.table] ?? [],
|
|
87
|
-
);
|
|
84
|
+
const allSpecs = computed<FieldSpec[]>(() => (FIELD_SPECS as unknown as Record<string, FieldSpec[]>)[props.table] ?? []);
|
|
88
85
|
|
|
89
86
|
const displayColumns = computed<FieldSpec[]>(() => {
|
|
90
87
|
if (props.columns?.length) {
|
|
@@ -100,18 +97,25 @@ const filtered = computed(() => {
|
|
|
100
97
|
if (!kw) return props.rows;
|
|
101
98
|
return props.rows.filter((r) =>
|
|
102
99
|
displayColumns.value.some((c) =>
|
|
103
|
-
String(r[c.key] ?? "")
|
|
100
|
+
String(r[c.key] ?? "")
|
|
101
|
+
.toLowerCase()
|
|
102
|
+
.includes(kw),
|
|
104
103
|
),
|
|
105
104
|
);
|
|
106
105
|
});
|
|
107
106
|
|
|
108
107
|
// 搜索词变化时重置到第一页
|
|
109
|
-
watch(keyword, () => {
|
|
110
|
-
|
|
111
|
-
watch(() => filtered.value.length, (len) => {
|
|
112
|
-
const maxPage = Math.max(1, Math.ceil(len / pageSize.value));
|
|
113
|
-
if (currentPage.value > maxPage) currentPage.value = 1;
|
|
108
|
+
watch(keyword, () => {
|
|
109
|
+
currentPage.value = 1;
|
|
114
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
|
+
);
|
|
115
119
|
|
|
116
120
|
const paged = computed(() => {
|
|
117
121
|
const start = (currentPage.value - 1) * pageSize.value;
|
|
@@ -159,33 +163,22 @@ function onSubmit(payload: Record<string, unknown>) {
|
|
|
159
163
|
<template>
|
|
160
164
|
<div class="ys-crud">
|
|
161
165
|
<div class="ys-crud__toolbar">
|
|
162
|
-
<el-input
|
|
163
|
-
v-model="keyword"
|
|
164
|
-
placeholder="搜索"
|
|
165
|
-
clearable
|
|
166
|
-
style="width: 200px"
|
|
167
|
-
/>
|
|
166
|
+
<el-input v-model="keyword" placeholder="搜索" clearable style="width: 200px" />
|
|
168
167
|
<div class="ys-crud__spacer" />
|
|
169
168
|
<el-button v-if="!readonly" type="primary" @click="onCreate">{{ createLabel || "新建" }}</el-button>
|
|
170
169
|
</div>
|
|
171
170
|
|
|
172
171
|
<el-table :data="paged" v-loading="loading" stripe border size="small" table-layout="fixed" row-key="_id">
|
|
173
|
-
<el-table-column
|
|
174
|
-
v-for="c in displayColumns"
|
|
175
|
-
:key="c.key"
|
|
176
|
-
:label="c.title"
|
|
177
|
-
:width="columnWidth(c)"
|
|
178
|
-
show-overflow-tooltip
|
|
179
|
-
>
|
|
172
|
+
<el-table-column v-for="c in displayColumns" :key="c.key" :label="c.title" :width="columnWidth(c)" show-overflow-tooltip>
|
|
180
173
|
<template #default="{ row }">{{ display(row, c) }}</template>
|
|
181
174
|
</el-table-column>
|
|
182
|
-
<el-table-column v-if="!readonly" label="操作" :width="actionWidth ?? 260">
|
|
175
|
+
<el-table-column v-if="!readonly" label="操作" :width="actionWidth ?? 260" fixed="right">
|
|
183
176
|
<template #default="{ row }">
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
</
|
|
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>
|
|
189
182
|
</template>
|
|
190
183
|
</el-table-column>
|
|
191
184
|
</el-table>
|
|
@@ -201,15 +194,7 @@ function onSubmit(payload: Record<string, unknown>) {
|
|
|
201
194
|
@size-change="currentPage = 1"
|
|
202
195
|
/>
|
|
203
196
|
|
|
204
|
-
<YsFormDialog
|
|
205
|
-
v-if="!externalEditor"
|
|
206
|
-
v-model="dialogOpen"
|
|
207
|
-
:table="table"
|
|
208
|
-
:record="editing"
|
|
209
|
-
:hidden="hiddenInForm"
|
|
210
|
-
:saving="saving"
|
|
211
|
-
@submit="onSubmit"
|
|
212
|
-
/>
|
|
197
|
+
<YsFormDialog v-if="!externalEditor" v-model="dialogOpen" :table="table" :record="editing" :hidden="hiddenInForm" :saving="saving" @submit="onSubmit" />
|
|
213
198
|
</div>
|
|
214
199
|
</template>
|
|
215
200
|
|
|
@@ -226,4 +211,13 @@ function onSubmit(payload: Record<string, unknown>) {
|
|
|
226
211
|
margin-top: 12px;
|
|
227
212
|
justify-content: flex-end;
|
|
228
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
|
+
}
|
|
229
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";
|