@ysgroup/ui 0.1.26 → 0.1.29

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.26",
3
+ "version": "0.1.29",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -21,4 +21,4 @@
21
21
  "publishConfig": {
22
22
  "access": "public"
23
23
  }
24
- }
24
+ }
@@ -0,0 +1,30 @@
1
+ <script setup lang="ts">
2
+ import { CircleCheckFilled } from "@element-plus/icons-vue";
3
+
4
+ defineProps<{
5
+ value: unknown;
6
+ onLabel?: string;
7
+ offLabel?: string;
8
+ }>();
9
+ </script>
10
+
11
+ <template>
12
+ <el-icon class="ys-bool-mark" :class="value ? 'is-yes' : 'is-no'" :size="18" :title="value ? (onLabel ?? '是') : (offLabel ?? '否')">
13
+ <CircleCheckFilled v-if="value" />
14
+ <svg v-else viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
15
+ <circle cx="512" cy="512" r="384" fill="none" stroke="currentColor" stroke-width="80" />
16
+ </svg>
17
+ </el-icon>
18
+ </template>
19
+
20
+ <style scoped>
21
+ .ys-bool-mark {
22
+ vertical-align: middle;
23
+ }
24
+ .ys-bool-mark.is-yes {
25
+ color: #5bb07a;
26
+ }
27
+ .ys-bool-mark.is-no {
28
+ color: #e6e9f0;
29
+ }
30
+ </style>
@@ -6,6 +6,7 @@ import { computed, ref, watch } from "vue";
6
6
  import { FIELD_SPECS, ENUM_META } from "@ysgroup/contracts";
7
7
  import { enumLabel } from "../tokens-runtime";
8
8
  import { columnWidth as lookupColumnWidth } from "../columnWidths";
9
+ import YsBoolMark from "./YsBoolMark.vue";
9
10
  import YsFormDialog from "./YsFormDialog.vue";
10
11
  import YsRowActionMenu from "./YsRowActionMenu.vue";
11
12
  import YsTableToolbar from "./YsTableToolbar.vue";
@@ -86,9 +87,17 @@ const paged = computed(() => {
86
87
  return filtered.value.slice(start, start + pageSize.value);
87
88
  });
88
89
 
90
+ function switchLabels(field: FieldSpec): { on: string; off: string } {
91
+ if (field.key === "enabled") return { on: "启用", off: "停用" };
92
+ return { on: "是", off: "否" };
93
+ }
94
+
89
95
  function display(row: Record<string, unknown>, f: FieldSpec): string {
90
96
  const v = props.rowDisplay ? props.rowDisplay(row, f.key, row[f.key]) : row[f.key];
91
- if (f.widget === "switch") return v ? "是" : "否";
97
+ if (f.widget === "switch") {
98
+ const labels = switchLabels(f);
99
+ return v ? labels.on : labels.off;
100
+ }
92
101
  if (f.widget === "enum" && f.enum && ENUM_META[f.enum as keyof typeof ENUM_META]) {
93
102
  return enumLabel(f.enum as keyof typeof ENUM_META, String(v ?? ""));
94
103
  }
@@ -131,8 +140,18 @@ function onSubmit(payload: Record<string, unknown>) {
131
140
  </YsTableToolbar>
132
141
 
133
142
  <el-table :fit="false" :data="paged" v-loading="loading" stripe border size="small" table-layout="fixed" row-key="_id">
134
- <el-table-column v-for="c in displayColumns" :key="c.key" :label="c.title" :width="columnWidth(c)" show-overflow-tooltip>
135
- <template #default="{ row }">{{ display(row, c) }}</template>
143
+ <el-table-column
144
+ v-for="c in displayColumns"
145
+ :key="c.key"
146
+ :label="c.title"
147
+ :width="columnWidth(c)"
148
+ :show-overflow-tooltip="c.widget !== 'switch'"
149
+ :align="c.widget === 'switch' ? 'center' : undefined"
150
+ >
151
+ <template #default="{ row }">
152
+ <YsBoolMark v-if="c.widget === 'switch'" :value="row[c.key]" :on-label="switchLabels(c).on" :off-label="switchLabels(c).off" />
153
+ <slot v-else name="cell" :row="row" :column="c" :text="display(row, c)">{{ display(row, c) }}</slot>
154
+ </template>
136
155
  </el-table-column>
137
156
  <el-table-column v-if="!readonly" label="操作栏" :width="actionWidth ?? 76" fixed="right" align="center">
138
157
  <template #default="{ row }">
@@ -176,6 +176,29 @@ function submit() {
176
176
  type="textarea"
177
177
  :rows="3"
178
178
  />
179
+ <el-date-picker
180
+ v-else-if="f.widget === 'datetime'"
181
+ v-model="form[f.key] as string"
182
+ type="datetime"
183
+ value-format="YYYY-MM-DD HH:mm:ss"
184
+ format="YYYY-MM-DD HH:mm:ss"
185
+ style="width: 100%"
186
+ />
187
+ <el-date-picker
188
+ v-else-if="f.widget === 'date'"
189
+ v-model="form[f.key] as string"
190
+ type="date"
191
+ value-format="YYYY-MM-DD"
192
+ format="YYYY-MM-DD"
193
+ style="width: 100%"
194
+ />
195
+ <el-time-picker
196
+ v-else-if="f.widget === 'time'"
197
+ v-model="form[f.key] as string"
198
+ value-format="HH:mm:ss"
199
+ format="HH:mm:ss"
200
+ style="width: 100%"
201
+ />
179
202
  <el-input v-else v-model="form[f.key] as string" />
180
203
  </el-form-item>
181
204
  </el-form>
@@ -1,25 +1,25 @@
1
- <!-- YsLoginPage —— 统一登录页。密码经 TLS 直传(不再前端 md5)。 -->
1
+ <!-- YsLoginPage —— 统一登录页。口令由 @ysgroup/core 密封后提交。 -->
2
2
  <script setup lang="ts">
3
3
  import { ref } from "vue";
4
4
 
5
- const props = defineProps<{
5
+ defineProps<{
6
6
  appTitle: string;
7
7
  hint?: string;
8
8
  logoUrl?: string;
9
9
  error?: string;
10
- otpEnabled?: boolean;
11
10
  }>();
12
- const emit = defineEmits<{ login: [username: string, password: string, otp: string] }>();
11
+ const emit = defineEmits<{
12
+ login: [username: string, password: string];
13
+ }>();
13
14
 
14
15
  const username = ref("");
15
16
  const password = ref("");
16
- const otp = ref("");
17
17
  const loading = ref(false);
18
18
 
19
19
  async function submit() {
20
20
  loading.value = true;
21
21
  try {
22
- emit("login", username.value, password.value, otp.value);
22
+ emit("login", username.value, password.value);
23
23
  } finally {
24
24
  loading.value = false;
25
25
  }
@@ -34,7 +34,12 @@ async function submit() {
34
34
  <h2 v-else class="ys-login__title">{{ appTitle }}</h2>
35
35
  <el-form @submit.prevent="submit">
36
36
  <el-form-item>
37
- <el-input v-model="username" placeholder="用户名" size="large" @keyup.enter="submit" />
37
+ <el-input
38
+ v-model="username"
39
+ placeholder="用户名"
40
+ size="large"
41
+ @keyup.enter="submit"
42
+ />
38
43
  </el-form-item>
39
44
  <el-form-item>
40
45
  <el-input
@@ -46,17 +51,13 @@ async function submit() {
46
51
  @keyup.enter="submit"
47
52
  />
48
53
  </el-form-item>
49
- <el-form-item v-if="props.otpEnabled && username.trim().toLowerCase() === 'root'">
50
- <el-input
51
- v-model="otp"
52
- inputmode="numeric"
53
- autocomplete="one-time-code"
54
- placeholder="动态验证码"
55
- size="large"
56
- @keyup.enter="submit"
57
- />
58
- </el-form-item>
59
- <el-alert v-if="error" :title="error" type="error" :closable="false" show-icon />
54
+ <el-alert
55
+ v-if="error"
56
+ :title="error"
57
+ type="error"
58
+ :closable="false"
59
+ show-icon
60
+ />
60
61
  <el-button
61
62
  type="primary"
62
63
  size="large"
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <el-segmented class="ys-segmented" />
3
+ </template>
package/src/index.ts CHANGED
@@ -12,5 +12,7 @@ export { default as YsLoginPage } from "./components/YsLoginPage.vue";
12
12
  export { default as YsSettingsCenterDialog } from "./components/YsSettingsCenterDialog.vue";
13
13
  export { default as YsPortalGrid } from "./components/YsPortalGrid.vue";
14
14
  export { default as YsRoleTag } from "./components/YsRoleTag.vue";
15
+ export { default as YsBoolMark } from "./components/YsBoolMark.vue";
16
+ export { default as YsSegmented } from "./components/YsSegmented.vue";
15
17
  export { enumOptions, enumLabel, type EnumOption } from "./tokens-runtime";
16
18
  export { registerColumnWidths, columnWidth, type ColumnWidthMap } from "./columnWidths";