@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ysgroup/ui",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -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] ?? "").toLowerCase().includes(kw),
100
+ String(r[c.key] ?? "")
101
+ .toLowerCase()
102
+ .includes(kw),
104
103
  ),
105
104
  );
106
105
  });
107
106
 
108
107
  // 搜索词变化时重置到第一页
109
- watch(keyword, () => { currentPage.value = 1; });
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
- <el-button size="small" @click="onEdit(row)">编辑</el-button>
185
- <slot name="row-actions" :row="row" />
186
- <el-button size="small" type="danger" plain @click="onRemove(row)">
187
- 删除
188
- </el-button>
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";