Pārlūkot izejas kodu

feat: add profile comps

xingyu4j 7 mēneši atpakaļ
vecāks
revīzija
7dc68ed368

+ 1 - 0
packages/effects/common-ui/src/ui/index.ts

@@ -2,3 +2,4 @@ export * from './about';
 export * from './authentication';
 export * from './dashboard';
 export * from './fallback';
+export * from './profile';

+ 56 - 0
packages/effects/common-ui/src/ui/profile/base-setting.vue

@@ -0,0 +1,56 @@
+<script setup lang="ts">
+import type { Recordable } from '@vben/types';
+
+import type { VbenFormSchema } from '@vben-core/form-ui';
+
+import { computed, reactive } from 'vue';
+
+import { useVbenForm } from '@vben-core/form-ui';
+import { VbenButton } from '@vben-core/shadcn-ui';
+
+interface Props {
+  formSchema?: VbenFormSchema[];
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  formSchema: () => [],
+});
+
+const emit = defineEmits<{
+  submit: [Recordable<any>];
+}>();
+
+const [Form, formApi] = useVbenForm(
+  reactive({
+    commonConfig: {
+      // 所有表单项
+      componentProps: {
+        class: 'w-full',
+      },
+    },
+    layout: 'horizontal',
+    schema: computed(() => props.formSchema),
+    showDefaultActions: false,
+  }),
+);
+
+async function handleSubmit() {
+  const { valid } = await formApi.validate();
+  const values = await formApi.getValues();
+  if (valid) {
+    emit('submit', values);
+  }
+}
+
+defineExpose({
+  getFormApi: () => formApi,
+});
+</script>
+<template>
+  <div @keydown.enter.prevent="handleSubmit">
+    <Form />
+    <VbenButton type="submit" class="mt-4" @click="handleSubmit">
+      更新基本信息
+    </VbenButton>
+  </div>
+</template>

+ 6 - 0
packages/effects/common-ui/src/ui/profile/index.ts

@@ -0,0 +1,6 @@
+export { default as ProfileBaseSetting } from './base-setting.vue';
+export { default as ProfileNotificationSetting } from './notification-setting.vue';
+export { default as ProfilePasswordSetting } from './password-setting.vue';
+export { default as Profile } from './profile.vue';
+export { default as ProfileSecuritySetting } from './security-setting.vue';
+export type * from './types';

+ 53 - 0
packages/effects/common-ui/src/ui/profile/notification-setting.vue

@@ -0,0 +1,53 @@
+<script setup lang="ts">
+import type { Recordable } from '@vben/types';
+
+import type { SettingProps } from './types';
+
+import {
+  Form,
+  FormControl,
+  FormDescription,
+  FormField,
+  FormItem,
+  FormLabel,
+  Switch,
+} from '@vben-core/shadcn-ui';
+
+withDefaults(defineProps<SettingProps>(), {
+  formSchema: () => [],
+});
+
+const emit = defineEmits<{
+  change: [Recordable<any>];
+}>();
+
+function handleChange(fieldName: string, value: boolean) {
+  emit('change', { fieldName, value });
+}
+</script>
+<template>
+  <Form class="space-y-8">
+    <div class="space-y-4">
+      <template v-for="item in formSchema" :key="item.fieldName">
+        <FormField type="checkbox" :name="item.fieldName">
+          <FormItem
+            class="flex flex-row items-center justify-between rounded-lg border p-4"
+          >
+            <div class="space-y-0.5">
+              <FormLabel class="text-base"> {{ item.label }} </FormLabel>
+              <FormDescription>
+                {{ item.description }}
+              </FormDescription>
+            </div>
+            <FormControl>
+              <Switch
+                :model-value="item.value"
+                @update:model-value="handleChange(item.fieldName, $event)"
+              />
+            </FormControl>
+          </FormItem>
+        </FormField>
+      </template>
+    </div>
+  </Form>
+</template>

+ 56 - 0
packages/effects/common-ui/src/ui/profile/password-setting.vue

@@ -0,0 +1,56 @@
+<script setup lang="ts">
+import type { Recordable } from '@vben/types';
+
+import type { VbenFormSchema } from '@vben-core/form-ui';
+
+import { computed, reactive } from 'vue';
+
+import { useVbenForm } from '@vben-core/form-ui';
+import { VbenButton } from '@vben-core/shadcn-ui';
+
+interface Props {
+  formSchema?: VbenFormSchema[];
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  formSchema: () => [],
+});
+
+const emit = defineEmits<{
+  submit: [Recordable<any>];
+}>();
+
+const [Form, formApi] = useVbenForm(
+  reactive({
+    commonConfig: {
+      // 所有表单项
+      componentProps: {
+        class: 'w-full',
+      },
+    },
+    layout: 'horizontal',
+    schema: computed(() => props.formSchema),
+    showDefaultActions: false,
+  }),
+);
+
+async function handleSubmit() {
+  const { valid } = await formApi.validate();
+  const values = await formApi.getValues();
+  if (valid) {
+    emit('submit', values);
+  }
+}
+
+defineExpose({
+  getFormApi: () => formApi,
+});
+</script>
+<template>
+  <div>
+    <Form />
+    <VbenButton type="submit" class="mt-4" @click="handleSubmit">
+      更新密码
+    </VbenButton>
+  </div>
+</template>

+ 62 - 0
packages/effects/common-ui/src/ui/profile/profile.vue

@@ -0,0 +1,62 @@
+<script setup lang="ts">
+import type { Props } from './types';
+
+import { preferences } from '@vben-core/preferences';
+import {
+  Card,
+  Separator,
+  Tabs,
+  TabsList,
+  TabsTrigger,
+  VbenAvatar,
+} from '@vben-core/shadcn-ui';
+
+import { Page } from '../../components';
+
+defineOptions({
+  name: 'ProfileUI',
+});
+
+withDefaults(defineProps<Props>(), {
+  title: '关于项目',
+  tabs: () => [],
+});
+
+const tabsValue = defineModel<string>('modelValue');
+</script>
+<template>
+  <Page auto-content-height>
+    <div class="flex h-full w-full">
+      <Card class="w-1/6 flex-none">
+        <div class="mt-4 flex h-40 flex-col items-center justify-center gap-4">
+          <VbenAvatar
+            :src="userInfo?.avatar ?? preferences.app.defaultAvatar"
+            class="size-20"
+          />
+          <span class="text-lg font-semibold">
+            {{ userInfo?.realName ?? '' }}
+          </span>
+          <span class="text-foreground/80 text-sm">
+            {{ userInfo?.username ?? '' }}
+          </span>
+        </div>
+        <Separator class="my-4" />
+        <Tabs v-model="tabsValue" orientation="vertical" class="m-4">
+          <TabsList class="bg-card grid w-full grid-cols-1">
+            <TabsTrigger
+              v-for="tab in tabs"
+              :key="tab.value"
+              :value="tab.value"
+              class="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground h-12 justify-start"
+            >
+              {{ tab.label }}
+            </TabsTrigger>
+          </TabsList>
+        </Tabs>
+      </Card>
+      <Card class="ml-4 w-5/6 flex-auto p-8">
+        <slot name="content"></slot>
+      </Card>
+    </div>
+  </Page>
+</template>

+ 53 - 0
packages/effects/common-ui/src/ui/profile/security-setting.vue

@@ -0,0 +1,53 @@
+<script setup lang="ts">
+import type { Recordable } from '@vben/types';
+
+import type { SettingProps } from './types';
+
+import {
+  Form,
+  FormControl,
+  FormDescription,
+  FormField,
+  FormItem,
+  FormLabel,
+  Switch,
+} from '@vben-core/shadcn-ui';
+
+withDefaults(defineProps<SettingProps>(), {
+  formSchema: () => [],
+});
+
+const emit = defineEmits<{
+  change: [Recordable<any>];
+}>();
+
+function handleChange(fieldName: string, value: boolean) {
+  emit('change', { fieldName, value });
+}
+</script>
+<template>
+  <Form class="space-y-8">
+    <div class="space-y-4">
+      <template v-for="item in formSchema" :key="item.fieldName">
+        <FormField type="checkbox" :name="item.fieldName">
+          <FormItem
+            class="flex flex-row items-center justify-between rounded-lg border p-4"
+          >
+            <div class="space-y-0.5">
+              <FormLabel class="text-base"> {{ item.label }} </FormLabel>
+              <FormDescription>
+                {{ item.description }}
+              </FormDescription>
+            </div>
+            <FormControl>
+              <Switch
+                :model-value="item.value"
+                @update:model-value="handleChange(item.fieldName, $event)"
+              />
+            </FormControl>
+          </FormItem>
+        </FormField>
+      </template>
+    </div>
+  </Form>
+</template>

+ 22 - 0
packages/effects/common-ui/src/ui/profile/types.ts

@@ -0,0 +1,22 @@
+import type { BasicUserInfo } from '@vben/types';
+
+export interface Props {
+  title?: string;
+  userInfo: BasicUserInfo | null;
+  tabs: {
+    label: string;
+    value: string;
+  }[];
+}
+
+export interface FormSchemaItem {
+  description: string;
+  fieldName: string;
+  label: string;
+  name: string;
+  value: boolean;
+}
+
+export interface SettingProps {
+  formSchema: FormSchemaItem[];
+}