2
0

Merge remote-tracking branch 'origin/wfc-modules-user'

This commit is contained in:
lai
2024-11-29 18:32:00 +08:00
70 changed files with 1286 additions and 3960 deletions

View File

@@ -2,7 +2,6 @@
import { computed } from 'vue';
import type { Component } from 'vue';
import { getColorPalette, mixColor } from '@sa/utils';
import { $t } from '@/locales';
import { useThemeStore } from '@/store/modules/theme';
import { loginModuleRecord } from '@/constants/app';
import PwdLogin from './modules/pwd-login.vue';
@@ -10,14 +9,16 @@ import CodeLogin from './modules/code-login.vue';
import Register from './modules/register.vue';
import ResetPwd from './modules/reset-pwd.vue';
import BindWechat from './modules/bind-wechat.vue';
import { WifiOutlined } from '@ant-design/icons-vue';
import {useI18n} from "vue-i18n";
//import { $t } from '@/locales';
interface Props {
/** The login module */
module?: UnionKey.LoginModule;
}
const props = defineProps<Props>();
const {t} = useI18n();
const themeStore = useThemeStore();
interface LoginModule {
@@ -54,12 +55,12 @@ const bgColor = computed(() => {
<ACard class="relative z-4">
<div class="w-400px lt-sm:w-300px">
<header class="flex-y-center justify-between">
<SystemLogo class="text-64px text-primary lt-sm:text-48px" />
<h3 class="text-28px text-primary font-500 lt-sm:text-22px">{{ $t('system.title') }}</h3>
<WifiOutlined class="text-64px text-primary lt-sm:text-48px" style="margin-left: 9px"/>
<h3 class="text-18px text-primary font-medium">{{ t(activeModule.label) }}</h3>
</header>
<main class="pt-24px">
<h3 class="text-18px text-primary font-medium">{{ $t(activeModule.label) }}</h3>
<div class="animation-slide-in-left pt-24px">
<main >
<h3 class="text-28px text-primary font-500 lt-sm:text-22px">{{ "WANFi" }}</h3>
<div class="animation-slide-in-left ">
<Transition :name="themeStore.page.animateMode" mode="out-in" appear>
<component :is="activeModule.component" />
</Transition>

View File

@@ -1,25 +1,27 @@
<script setup lang="ts">
import { $t } from '@/locales';
import { loginModuleRecord } from '@/constants/app';
import { useRouterPush } from '@/hooks/common/router';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
import { useAuthStore } from '@/store/modules/auth';
import {useI18n} from "vue-i18n";
defineOptions({
name: 'PwdLogin'
});
const { t } = useI18n();
const authStore = useAuthStore();
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useAntdForm();
const { patternRules } = useFormRules();
const codeImg = ref('');
getCheckCode();
const model = reactive({
username: 'ryadmin',
password: 'admin123',
username: '123456',
password: '123456',
code: '',
uuid: '',
authType: 'sys'
@@ -31,11 +33,11 @@ const rules = {
};
async function handleSubmit() {
await validate();
await validate();//验证表单内容
await authStore.login({
loginForm: model,
loginForm: model,//发送表单的数据
onError() {
getCheckCode();
getCheckCode();//重新获取验证码
}
});
}
@@ -45,6 +47,9 @@ async function getCheckCode() {
if (!error) {
codeImg.value = `data:image/png;base64,${data.img}`;
model.uuid = data.uuid;
if (data?.text) {
model.code = data.text;
}
}
}
</script>
@@ -73,18 +78,18 @@ async function getCheckCode() {
<ASpace direction="vertical" size="large" class="w-full">
<div class="flex-y-center justify-between">
<ACheckbox>{{ $t('page.login.pwdLogin.rememberMe') }}</ACheckbox>
<AButton type="text" @click="toggleLoginModule('reset-pwd')">{{ $t('page.login.pwdLogin.forgetPassword') }}</AButton>
<AButton type="text" @click="toggleLoginModule('reset-pwd')">{{ t('page.login.pwdLogin.forgetPassword') }}</AButton>
</div>
<AButton type="primary" block size="large" shape="round" :loading="authStore.loginLoading" @click="handleSubmit">
{{ $t('common.confirm') }}
</AButton>
<div class="flex-y-center justify-between">
<AButton class="h-34px flex-1" block @click="toggleLoginModule('code-login')">
{{ $t(loginModuleRecord['code-login']) }}
</AButton>
<!-- <AButton class="h-34px flex-1" block @click="toggleLoginModule('code-login')">-->
<!-- {{ t(loginModuleRecord['code-login']) }}-->
<!-- </AButton>-->
<div class="w-12px"></div>
<AButton class="h-34px flex-1" block @click="toggleLoginModule('register')">
{{ $t(loginModuleRecord.register) }}
{{ t(loginModuleRecord.register) }}
</AButton>
</div>
</ASpace>

View File

@@ -1,86 +1,445 @@
<script setup lang="ts">
import { computed, reactive } from 'vue';
import { $t } from '@/locales';
import { useRouterPush } from '@/hooks/common/router';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
import { useCaptcha } from '@/hooks/business/captcha';
import {computed, reactive, ref} from 'vue';
import {useI18n} from 'vue-i18n'; // 添加这行
import {useAuthStore} from '@/store/modules/auth';
import {useRouterPush} from '@/hooks/common/router';
import {useAntdForm, useFormRules} from '@/hooks/common/form';
import {useCaptcha} from '@/hooks/business/captcha';
import {useWindowSize} from '@vueuse/core';
import {registerTerms} from '@/views/_builtin/login/modules/terms';
import dayjs from 'dayjs';
import type {Rule} from 'ant-design-vue/es/form';
const { t } = useI18n();
const authStore = useAuthStore();
defineOptions({
name: 'CodeLogin'
name: 'Register'
});
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useAntdForm();
const { label, isCounting, loading, getCaptcha } = useCaptcha();
interface FormModel {
const { width } = useWindowSize();
const isMobile = computed(() => width.value <= 640);
// 当前步骤
const currentStep = ref(0);
// 是否同意协议
const agreeTerms = ref(false);
// 定义一个统一的数据模型
interface RegisterModel {
username: string;
password: string;
email: string;
fullName: string;
age:0,
gender: string;
phone: string;
address: string;
code: string;
authType: string;
}
// 使用一个统一的 model
const model = reactive<RegisterModel>({
username: '',
password: '',
email: '',
fullName: '',
age:0,
gender: '',
phone: '',
address: '',
code: '',
authType: 'u'
});
// 第一步表单数据
interface BasicFormModel {
username: string;
fullName: string;
birthDate: string;
gender: string;
phone: string;
email: string;
address: string;
}
const basicModel = reactive<BasicFormModel>({
username: '',
fullName: '',
birthDate: '',
gender: '',
phone: '',
email: '',
address: ''
});
// 第三表单数据
interface SecurityFormModel {
email: string;
code: string;
password: string;
confirmPassword: string;
}
const model: FormModel = reactive({
phone: '',
const securityModel = reactive<SecurityFormModel>({
email: '',
code: '',
password: '',
confirmPassword: ''
});
const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
const { formRules, createConfirmPwdRule } = useFormRules();
// 第一步表单验证规则
const basicRules = computed<Record<string, Rule | Rule[]>>(() => {
const { formRules } = useFormRules();
return {
phone: formRules.phone,
code: formRules.code,
password: formRules.pwd,
confirmPassword: createConfirmPwdRule(model.password)
username: formRules.username,
email: formRules.email,
birthDate: [{ required: true, message: t('page.login.register.birthDateRequired') }],
phone: [{ pattern: /^1[3-9]\d{9}$/, message: t('form.phone.invalid'), trigger: 'blur' }]
};
});
async function handleSubmit() {
await validate();
// request to register
$message?.success($t('page.login.common.validateSuccess'));
// 第三步表单验证规则
const securityRules = computed<Record<string, Rule | Rule[]>>(() => {
const { formRules, createConfirmPwdRule } = useFormRules();
return {
code: formRules.code,
password: formRules.pwd,
confirmPassword: createConfirmPwdRule(securityModel.password)
};
});
// 协议内容
const terms = computed(() => {
const locale = useI18n().locale.value;
return registerTerms[locale === 'zh-CN' ? 'zh-CN' : 'en-US'];
});
// 步骤控制函数
async function nextStep() {
if (currentStep.value === 0) {
await validate();
// 复制电话号码到第三步
securityModel.email = basicModel.email;
}
if (currentStep.value === 1) {
if (!agreeTerms.value) {
window.$message?.error(t('page.login.register.agreeTermsFirst'));
return;
}
}
currentStep.value += 1;
}
function prevStep() {
currentStep.value -= 1;
}
//注册按钮
async function handleSubmit() {
try {
await validate();
// 整合表单数据
model.username = basicModel.username;
model.password = securityModel.password;
model.email = securityModel.email;
model.fullName = basicModel.fullName;
model.gender = basicModel.gender; // 直接使用 gender 值
model.phone = basicModel.phone;
model.address = basicModel.address;
model.code = securityModel.code;
const success = await authStore.register({
...model,
age: dayjs().diff(dayjs(basicModel.birthDate), 'year'),
sex: model.gender, // 直接使用 gender 值,不需要转换
phonenumber: model.phone,
});
if (success) {
window.$message?.success(t('page.login.register.registerSuccess'));
toggleLoginModule('pwd-login');
}
} catch (error) {
console.error('Form validation failed:', error);
}
}
// 在script setup部分添加一个新的计算属性
const showSteps = computed(() => !isMobile.value);
</script>
<template>
<AForm ref="formRef" :model="model" :rules="rules">
<AFormItem name="phone">
<AInput v-model:value="model.phone" size="large" :placeholder="$t('page.login.common.phonePlaceholder')" />
</AFormItem>
<AFormItem name="code">
<div class="w-full flex-y-center gap-16px">
<AInput v-model:value="model.code" size="large" :placeholder="$t('page.login.common.codePlaceholder')" />
<AButton size="large" :disabled="isCounting" :loading="loading" @click="getCaptcha(model.phone)">
{{ label }}
</AButton>
<ASteps
v-if="showSteps"
:current="currentStep"
size="small"
class="max-w-full mb-16px"
direction="horizontal"
:responsive="false"
>
<AStep :title="t('page.login.register.basicInfo')" />
<AStep :title="t('page.login.register.terms')" />
<AStep :title="t('page.login.register.security')" />
</ASteps>
<div v-else class="mobile-step-indicator mb-16px text-center">
{{ currentStep + 1 }}/3: {{
currentStep === 0
? t('page.login.register.basicInfo')
: currentStep === 1
? t('page.login.register.terms')
: t('page.login.register.security')
}}
</div>
<div class="step-content">
<!-- 第一步基本信息 -->
<div v-if="currentStep === 0">
<AForm
ref="formRef"
:model="basicModel"
:rules="basicRules"
:label-wrap="true"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
>
<ARow :gutter="[8,2]">
<ACol :span="24" :lg="24">
<AFormItem name="username" :label="t('page.login.register.username')">
<AInput v-model:value="basicModel.username" />
</AFormItem>
</ACol>
<ACol :span="24" :lg="24">
<AFormItem name="fullName" :label="t('page.login.register.fullName')">
<AInput v-model:value="basicModel.fullName" />
</AFormItem>
</ACol>
<ACol :xs="12" :sm="12" :lg="24">
<AFormItem name="birthDate" :label="t('page.login.register.birthDate')">
<ADatePicker
v-model:value="basicModel.birthDate"
class="!w-full birth-date-picker"
:placeholder="t('page.login.register.birthDatePlaceholder')"
:disabled-date="(current: dayjs.Dayjs) => current && current.isAfter(dayjs())"
/>
</AFormItem>
</ACol>
<ACol :xs="12" :sm="12" :lg="24">
<AFormItem name="gender" :label="t('page.login.register.gender')">
<ASelect v-model:value="basicModel.gender">
<ASelectOption value="male">{{ t('page.login.register.male') }}</ASelectOption>
<ASelectOption value="female">{{ t('page.login.register.female') }}</ASelectOption>
</ASelect>
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<AFormItem name="phone" :label="t('page.login.register.phone')">
<AInput v-model:value="basicModel.phone" />
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<AFormItem name="email" :label="t('page.login.register.email')">
<AInput v-model:value="basicModel.email" />
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<AFormItem name="address" :label="t('page.login.register.address')">
<ATextarea v-model:value="basicModel.address" :rows="2" />
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<ASpace direction="vertical" size="small" class="w-full">
<AButton type="primary" block size="small" @click="nextStep">
{{ t('page.login.register.next') }}
</AButton>
<AButton block size="small" @click="toggleLoginModule('pwd-login')">
{{ t('page.login.common.back') }}
</AButton>
</ASpace>
</ACol>
</ARow>
</AForm>
</div>
<!-- 第二步:协议 -->
<div v-if="currentStep === 1">
<ATextarea
:value="terms"
:rows="12"
readonly
class="mb-16px"
size="small"
:style="{ fontSize: '14px', lineHeight: '1.6' }"
/>
<div class="mb-16px">
<ACheckbox
v-model:checked="agreeTerms"
class="terms-checkbox"
>
{{ t('page.login.register.agreeTerms') }}
</ACheckbox>
</div>
</AFormItem>
<AFormItem name="password">
<AInputPassword
v-model:value="model.password"
size="large"
:placeholder="$t('page.login.common.passwordPlaceholder')"
/>
</AFormItem>
<AFormItem name="confirmPassword">
<AInputPassword
v-model:value="model.confirmPassword"
size="large"
:placeholder="$t('page.login.common.confirmPasswordPlaceholder')"
/>
</AFormItem>
<ASpace direction="vertical" size="large" class="w-full">
<AButton type="primary" block size="large" shape="round" @click="handleSubmit">
{{ $t('common.confirm') }}
</AButton>
<AButton block size="large" shape="round" @click="toggleLoginModule('pwd-login')">
{{ $t('page.login.common.back') }}
</AButton>
</ASpace>
</AForm>
<ASpace direction="vertical" size="small" class="w-full">
<AButton type="primary" block size="small" :disabled="!agreeTerms" @click="nextStep">
{{ t('page.login.register.next') }}
</AButton>
<AButton block size="small" @click="prevStep">
{{ t('page.login.register.prev') }}
</AButton>
</ASpace>
</div>
<!-- 第三步:安全信息 -->
<div v-if="currentStep === 2">
<AForm
ref="formRef"
:model="securityModel"
:rules="securityRules"
:label-wrap="true"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
class="compact-form"
>
<AFormItem name="email" :label="t('page.login.register.email')">
<AInput
v-model:value="securityModel.email"
:placeholder="t('page.login.common.emailPlaceholder')"
/>
</AFormItem>
<AFormItem name="code" :label="t('page.login.register.code')">
<div class="w-full flex-y-center gap-8px">
<AInput
v-model:value="securityModel.code"
:placeholder="t('page.login.common.codePlaceholder')"
/>
<AButton
size="small"
:disabled="isCounting"
:loading="loading"
@click="getCaptcha(securityModel.email)"
>
{{ label }}
</AButton>
</div>
</AFormItem>
<AFormItem name="password" :label="t('page.login.register.password')">
<AInputPassword
v-model:value="securityModel.password"
:placeholder="t('page.login.common.passwordPlaceholder')"
/>
</AFormItem>
<AFormItem name="confirmPassword" :label="t('page.login.register.confirmPassword')">
<AInputPassword
v-model:value="securityModel.confirmPassword"
:placeholder="t('page.login.common.confirmPasswordPlaceholder')"
/>
</AFormItem>
<AFormItem :wrapper-col="{ xs: { span: 24 }, sm: { span: 24 } }">
<ASpace direction="vertical" size="small" class="w-full">
<AButton type="primary" block size="small" @click="handleSubmit">
{{ t('common.confirm') }}
</AButton>
<AButton block size="small" @click="prevStep">
{{ t('page.login.register.prev') }}
</AButton>
</ASpace>
</AFormItem>
</AForm>
</div>
</div>
</template>
<style scoped></style>
<style scoped>
@media (max-width: 640px) {
:deep(.ant-form) {
.ant-form-item {
margin-bottom: 4px !important;
}
.ant-form-item-label {
padding: 0 !important;
line-height: 28px !important;
> label {
font-size: 13px !important;
height: 28px !important;
padding-bottom: 0 !important;
}
}
.ant-input,
.ant-input-password,
.ant-select,
.ant-input-number,
.ant-btn {
font-size: 13px !important;
height: 28px !important;
line-height: 28px !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.ant-select-selector {
height: 28px !important;
padding: 0 8px !important;
.ant-select-selection-item {
line-height: 26px !important;
}
}
.ant-input-number {
height: 32px !important;
input {
height: 30px !important;
padding: 0 8px !important;
}
}
}
.mobile-step-indicator {
font-size: 13px;
padding: 4px 8px;
margin-bottom: 8px;
text-align: center;
width: 100%;
border-radius: 4px;
}
:deep(.ant-space) {
gap: 4px !important;
}
}
:deep(.terms-checkbox) {
.ant-checkbox + span {
font-size: 14px;
color: rgba(0, 0, 0, 0.65);
}
}
:deep(.ant-input[readonly]) {
background-color: #f5f5f5;
cursor: default;
}
:deep(.ant-form-item-label) {
white-space: normal;
text-align: left;
> label {
height: auto !important;
padding-bottom: 4px;
}
}
.birth-date-picker {
margin-top: 2px !important;
}
</style>

View File

@@ -1,25 +1,27 @@
<script setup lang="ts">
import { computed, reactive } from 'vue';
import { $t } from '@/locales';
import { useRouterPush } from '@/hooks/common/router';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
import {useCaptcha} from "@/hooks/business/captcha";
import {useI18n} from "vue-i18n";
defineOptions({
name: 'ResetPwd'
});
const { t } = useI18n();
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useAntdForm();
const { label, isCounting, loading, getCaptcha } = useCaptcha();
interface FormModel {
phone: string;
email: string;
code: string;
password: string;
confirmPassword: string;
}
const model: FormModel = reactive({
phone: '',
email: '',
code: '',
password: '',
confirmPassword: ''
@@ -31,7 +33,9 @@ const rules = computed<RuleRecord>(() => {
const { formRules, createConfirmPwdRule } = useFormRules();
return {
phone: formRules.phone,
//phone: formRules.phone,
email:formRules.email,
code:formRules.code,
password: formRules.pwd,
confirmPassword: createConfirmPwdRule(model.password)
};
@@ -40,38 +44,48 @@ const rules = computed<RuleRecord>(() => {
async function handleSubmit() {
await validate();
// request to reset password
$message?.success($t('page.login.common.validateSuccess'));
$message?.success(t('page.login.common.validateSuccess'));
}
</script>
<template>
<AForm ref="formRef" :model="model" :rules="rules">
<AFormItem name="phone">
<AInput v-model:value="model.phone" size="large" :placeholder="$t('page.login.common.phonePlaceholder')" />
<AFormItem name="email">
<AInput v-model:value="model.email" size="large" :placeholder="t('page.login.common.emailPlaceholder')" />
</AFormItem>
<AFormItem name="code">
<AInput v-model:value="model.code" size="large" :placeholder="$t('page.login.common.codePlaceholder')" />
<AFormItem name="code" >
<div class="w-full flex-y-center gap-8px">
<AInput v-model:value="model.code" size="large" :placeholder="t('page.login.common.codePlaceholder')" />
<AButton
size="small"
:disabled="isCounting"
:loading="loading"
@click="getCaptcha(model.email)"
>
{{ label }}
</AButton>
</div>
</AFormItem>
<AFormItem name="password">
<AInputPassword
v-model:value="model.password"
size="large"
:placeholder="$t('page.login.common.passwordPlaceholder')"
:placeholder="t('page.login.common.passwordPlaceholder')"
/>
</AFormItem>
<AFormItem name="confirmPassword">
<AInputPassword
v-model:value="model.confirmPassword"
size="large"
:placeholder="$t('page.login.common.confirmPasswordPlaceholder')"
:placeholder="t('page.login.common.confirmPasswordPlaceholder')"
/>
</AFormItem>
<ASpace direction="vertical" size="large" class="w-full">
<AButton type="primary" block size="large" shape="round" @click="handleSubmit">
{{ $t('common.confirm') }}
{{ t('common.confirm') }}
</AButton>
<AButton block size="large" shape="round" @click="toggleLoginModule('pwd-login')">
{{ $t('page.login.common.back') }}
{{ t('page.login.common.back') }}
</AButton>
</ASpace>
</AForm>

View File

@@ -0,0 +1,50 @@
export const registerTerms = {
'zh-CN': `
用户协议和隐私政策
1. 总则
本协议是您与我们之间关于使用我们的服务所订立的协议。您注册成为用户,表示您已充分阅读、理解并同意接受本协议的全部内容。
2. 服务内容
2.1 我们将为您提供安全、可靠的服务。
2.2 我们保留随时修改或中断服务的权利。
3. 用户义务
3.1 用户应当遵守相关法律法规。
3.2 用户应当遵守社区规范和道德准则。
3.3 用户应当保护账号安全,不得将账号借给他人使用。
4. 隐私保护
4.1 我们将依法保护您的个人信息安全。
4.2 未经您的同意,我们不会向第三方披露您的个人信息。
4.3 我们采用业界标准的安全技术来保护您的个人信息。
5. 免责声明
5.1 因不可抗力导致的服务中断,本平台不承担责任。
5.2 用户因违反本协议造成的损失由用户自行承担。
`,
'en-US': `
User Agreement and Privacy Policy
1. General Provisions
This agreement is established between you and us regarding the use of our services. By registering as a user, you indicate that you have fully read, understood and agreed to accept all contents of this agreement.
2. Service Content
2.1 We will provide you with secure and reliable services.
2.2 We reserve the right to modify or interrupt services at any time.
3. User Obligations
3.1 Users shall comply with relevant laws and regulations.
3.2 Users shall comply with community standards and ethical guidelines.
3.3 Users shall protect account security and not lend their accounts to others.
4. Privacy Protection
4.1 We will protect your personal information security in accordance with law.
4.2 We will not disclose your personal information to third parties without your consent.
4.3 We use industry-standard security technology to protect your personal information.
5. Disclaimer
5.1 The platform is not liable for service interruptions caused by force majeure.
5.2 Users shall bear losses caused by their violation of this agreement.
`
};

View File

@@ -31,7 +31,7 @@ const pkgJson: PkgJson = {
devDependencies: Object.entries(devDependencies).map(item => transformVersionData(item))
};
const latestBuildTime = BUILD_TIME;
const latestBuildTime = "----";
</script>
<template>

View File

@@ -1,6 +1,4 @@
<script lang="ts" setup>
import type {} from 'ant-design-vue';
import { generatedRoutes } from '@/router/elegant/routes';
import { $t } from '@/locales';
import type { MenuModelType } from './form';
import { formRules, menuStatusOptions, menuTypeOptions, resetAddForm } from './form';
@@ -30,24 +28,6 @@ const title = computed(() => {
return titles[props.operateType];
});
// TODO: 根据菜单类型动态加载组件路径、目录类型只允许选择带有子元素的,菜单类型只允许选择没有子元素的
const componentOptions = computed(() => {
const excludePaths = ['/404', '/403', '/500'];
function transformRoutes(routes: any[]): any[] {
return routes.filter(route => {
if (route.children) {
route.children = transformRoutes(route.children);
return true;
}
if (!route.hideInMenu && !excludePaths.includes(route.path) && !route.path.startsWith('/login')) {
return true;
}
return false;
});
}
return transformRoutes(generatedRoutes);
});
watch(visible, val => {
if (val) {
@@ -95,10 +75,6 @@ async function getTreeData() {
}
}
function handleTreeSelect(node: any) {
model.value.component = node.component;
model.value.name = node.name;
}
getTreeData();
</script>
@@ -139,20 +115,9 @@ getTreeData();
</ARadioGroup>
</AFormItem>
<AFormItem v-if="model.menuType !== 'F'" label="菜单路径" name="path">
<div>
<!-- @vue-ignore -->
<ATreeSelect
v-if="model.isFrame === '1'"
v-model:value="model.path"
show-search
:field-names="{ value: 'path', label: 'path' }"
allow-clear
:tree-data="componentOptions"
tree-node-filter-prop="label"
@select="(_val, node) => handleTreeSelect(node)"
/>
<AInput v-else v-model:value="model.path" />
</div>
path <AInput v-model:value="model.path" />
component <AInput v-model:value="model.component" />
name <AInput v-model:value="model.name" />
</AFormItem>
<AFormItem v-if="model.menuType === 'C'" label="隐藏菜单" name="hideInMenu">
<ASwitch v-model:checked="model.hideInMenu" checked-value="0" un-checked-value="1" />

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>user_vip</div>
</template>
<style scoped></style>