feat: 添加解绑手机号和邮箱功能,更新用户资料页面以支持安全设置
This commit is contained in:
16
README.md
16
README.md
@@ -1,19 +1,3 @@
|
|||||||
<div align="center">
|
|
||||||
|
|
||||||
## 🎉 正在参加 Gitee 2025 最受欢迎开源软件评选
|
|
||||||
|
|
||||||
<a href="https://gitee.com/activity/2025opensource?ident=I6VXEH" target="_blank">
|
|
||||||
<img src="https://img.shields.io/badge/VUE3--ELEMENT--ADMIN-点击投票支持-f97316?style=for-the-badge&logo=gitee&logoColor=white&labelColor=111827" alt="投票" height="50"/>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<strong>📢 投票最后1天!恳请助力一票 🙏 无需重复投,您的支持是我们前行的最大动力!</strong>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img alt="vue3-element-admin" width="80" height="80" src="./src/assets/images/logo.png">
|
<img alt="vue3-element-admin" width="80" height="80" src="./src/assets/images/logo.png">
|
||||||
<h1>vue3-element-admin</h1>
|
<h1>vue3-element-admin</h1>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
UserProfileDetail,
|
UserProfileDetail,
|
||||||
UserProfileForm,
|
UserProfileForm,
|
||||||
PasswordChangeForm,
|
PasswordChangeForm,
|
||||||
|
PasswordVerifyForm,
|
||||||
MobileUpdateForm,
|
MobileUpdateForm,
|
||||||
EmailUpdateForm,
|
EmailUpdateForm,
|
||||||
OptionItem,
|
OptionItem,
|
||||||
@@ -193,6 +194,15 @@ const UserAPI = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 解绑手机号 */
|
||||||
|
unbindMobile(data: PasswordVerifyForm) {
|
||||||
|
return request({
|
||||||
|
url: `${USER_BASE_URL}/mobile`,
|
||||||
|
method: "delete",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/** 发送邮箱验证码(绑定或更换邮箱)*/
|
/** 发送邮箱验证码(绑定或更换邮箱)*/
|
||||||
sendEmailCode(email: string) {
|
sendEmailCode(email: string) {
|
||||||
return request({
|
return request({
|
||||||
@@ -211,6 +221,15 @@ const UserAPI = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 解绑邮箱 */
|
||||||
|
unbindEmail(data: PasswordVerifyForm) {
|
||||||
|
return request({
|
||||||
|
url: `${USER_BASE_URL}/email`,
|
||||||
|
method: "delete",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户下拉列表
|
* 获取用户下拉列表
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -108,20 +108,12 @@ export interface UserProfileDetail {
|
|||||||
|
|
||||||
/** 个人中心用户信息表单 */
|
/** 个人中心用户信息表单 */
|
||||||
export interface UserProfileForm {
|
export interface UserProfileForm {
|
||||||
/** 用户ID */
|
|
||||||
id?: string;
|
|
||||||
/** 用户名 */
|
|
||||||
username?: string;
|
|
||||||
/** 用户昵称 */
|
/** 用户昵称 */
|
||||||
nickname?: string;
|
nickname?: string;
|
||||||
/** 头像URL */
|
/** 头像URL */
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
/** 性别 */
|
/** 性别 */
|
||||||
gender?: number;
|
gender?: number;
|
||||||
/** 手机号 */
|
|
||||||
mobile?: string;
|
|
||||||
/** 邮箱 */
|
|
||||||
email?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改密码表单 */
|
/** 修改密码表单 */
|
||||||
@@ -134,12 +126,20 @@ export interface PasswordChangeForm {
|
|||||||
confirmPassword?: string;
|
confirmPassword?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 密码校验表单 */
|
||||||
|
export interface PasswordVerifyForm {
|
||||||
|
/** 当前密码 */
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/** 修改手机表单 */
|
/** 修改手机表单 */
|
||||||
export interface MobileUpdateForm {
|
export interface MobileUpdateForm {
|
||||||
/** 手机号 */
|
/** 手机号 */
|
||||||
mobile?: string;
|
mobile?: string;
|
||||||
/** 验证码 */
|
/** 验证码 */
|
||||||
code?: string;
|
code?: string;
|
||||||
|
/** 当前密码 */
|
||||||
|
password?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改邮箱表单 */
|
/** 修改邮箱表单 */
|
||||||
@@ -148,4 +148,6 @@ export interface EmailUpdateForm {
|
|||||||
email?: string;
|
email?: string;
|
||||||
/** 验证码 */
|
/** 验证码 */
|
||||||
code?: string;
|
code?: string;
|
||||||
|
/** 当前密码 */
|
||||||
|
password?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,41 +69,9 @@
|
|||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="手机号码">
|
<el-descriptions-item label="手机号码">
|
||||||
{{ userProfile.mobile || "未绑定" }}
|
{{ userProfile.mobile || "未绑定" }}
|
||||||
<el-button
|
|
||||||
v-if="userProfile.mobile"
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="() => handleOpenDialog(DialogType.MOBILE)"
|
|
||||||
>
|
|
||||||
更换
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-else
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="() => handleOpenDialog(DialogType.MOBILE)"
|
|
||||||
>
|
|
||||||
绑定
|
|
||||||
</el-button>
|
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="邮箱">
|
<el-descriptions-item label="邮箱">
|
||||||
{{ userProfile.email || "未绑定" }}
|
{{ userProfile.email || "未绑定" }}
|
||||||
<el-button
|
|
||||||
v-if="userProfile.email"
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="() => handleOpenDialog(DialogType.EMAIL)"
|
|
||||||
>
|
|
||||||
更换
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-else
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="() => handleOpenDialog(DialogType.EMAIL)"
|
|
||||||
>
|
|
||||||
绑定
|
|
||||||
</el-button>
|
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="部门">
|
<el-descriptions-item label="部门">
|
||||||
{{ userProfile.deptName }}
|
{{ userProfile.deptName }}
|
||||||
@@ -129,6 +97,66 @@
|
|||||||
修改
|
修改
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="security-item">
|
||||||
|
<div class="security-info">
|
||||||
|
<div class="security-title">手机号</div>
|
||||||
|
<div class="security-desc">
|
||||||
|
{{ mobileSecurityDesc }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<el-button
|
||||||
|
v-if="userProfile.mobile"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="() => handleOpenDialog(DialogType.MOBILE)"
|
||||||
|
>
|
||||||
|
更换
|
||||||
|
</el-button>
|
||||||
|
<el-button v-if="userProfile.mobile" type="danger" link @click="handleUnbindMobile">
|
||||||
|
解绑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="() => handleOpenDialog(DialogType.MOBILE)"
|
||||||
|
>
|
||||||
|
绑定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="security-item">
|
||||||
|
<div class="security-info">
|
||||||
|
<div class="security-title">邮箱</div>
|
||||||
|
<div class="security-desc">
|
||||||
|
{{ emailSecurityDesc }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<el-button
|
||||||
|
v-if="userProfile.email"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="() => handleOpenDialog(DialogType.EMAIL)"
|
||||||
|
>
|
||||||
|
更换
|
||||||
|
</el-button>
|
||||||
|
<el-button v-if="userProfile.email" type="danger" link @click="handleUnbindEmail">
|
||||||
|
解绑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="() => handleOpenDialog(DialogType.EMAIL)"
|
||||||
|
>
|
||||||
|
绑定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -189,6 +217,14 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="当前密码" prop="password">
|
||||||
|
<el-input
|
||||||
|
v-model="mobileUpdateForm.password"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
style="width: 250px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<!-- 绑定邮箱 -->
|
<!-- 绑定邮箱 -->
|
||||||
@@ -211,6 +247,14 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="当前密码" prop="password">
|
||||||
|
<el-input
|
||||||
|
v-model="emailUpdateForm.password"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
style="width: 250px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -233,9 +277,10 @@ import type {
|
|||||||
UserProfileForm,
|
UserProfileForm,
|
||||||
} from "@/types/api";
|
} from "@/types/api";
|
||||||
|
|
||||||
import { ref, reactive } from "vue";
|
import { computed, onBeforeUnmount, onMounted, reactive, ref } from "vue";
|
||||||
import FileAPI from "@/api/file";
|
import FileAPI from "@/api/file";
|
||||||
import { useUserStoreHook } from "@/store";
|
import { useUserStoreHook } from "@/store";
|
||||||
|
import { redirectToLogin } from "@/utils/auth";
|
||||||
|
|
||||||
import { Camera } from "@element-plus/icons-vue";
|
import { Camera } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
@@ -275,7 +320,19 @@ const emailTimer = ref();
|
|||||||
const passwordChangeRules = {
|
const passwordChangeRules = {
|
||||||
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
||||||
newPassword: [{ required: true, message: "请输入新密码", trigger: "blur" }],
|
newPassword: [{ required: true, message: "请输入新密码", trigger: "blur" }],
|
||||||
confirmPassword: [{ required: true, message: "请再次输入新密码", trigger: "blur" }],
|
confirmPassword: [
|
||||||
|
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||||
|
{
|
||||||
|
validator: (_rule: any, value: string, callback: (error?: Error) => void) => {
|
||||||
|
if (value !== passwordChangeForm.newPassword) {
|
||||||
|
callback(new Error("两次输入的密码不一致"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
trigger: "blur",
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// 手机号校验规则
|
// 手机号校验规则
|
||||||
@@ -289,6 +346,7 @@ const mobileBindingRules = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
|
code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
|
||||||
|
password: [{ required: true, message: "请输入当前密码", trigger: "blur" }],
|
||||||
};
|
};
|
||||||
|
|
||||||
// 邮箱校验规则
|
// 邮箱校验规则
|
||||||
@@ -302,8 +360,32 @@ const emailBindingRules = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
|
code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
|
||||||
|
password: [{ required: true, message: "请输入当前密码", trigger: "blur" }],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function maskMobile(mobile?: string) {
|
||||||
|
if (!mobile) return "";
|
||||||
|
return mobile.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
|
||||||
|
}
|
||||||
|
|
||||||
|
function maskEmail(email?: string) {
|
||||||
|
if (!email) return "";
|
||||||
|
const [name, domain] = email.split("@");
|
||||||
|
if (!domain) return email;
|
||||||
|
if (name.length <= 2) return `${name[0] || ""}***@${domain}`;
|
||||||
|
return `${name.slice(0, 2)}***@${domain}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mobileSecurityDesc = computed(() => {
|
||||||
|
return userProfile.value.mobile
|
||||||
|
? `已绑定:${maskMobile(userProfile.value.mobile)}`
|
||||||
|
: "未绑定手机号";
|
||||||
|
});
|
||||||
|
|
||||||
|
const emailSecurityDesc = computed(() => {
|
||||||
|
return userProfile.value.email ? `已绑定:${maskEmail(userProfile.value.email)}` : "未绑定邮箱";
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开弹窗
|
* 打开弹窗
|
||||||
* @param type 弹窗类型 ACCOUNT: 账号资料 PASSWORD: 修改密码 MOBILE: 绑定手机 EMAIL: 绑定邮箱
|
* @param type 弹窗类型 ACCOUNT: 账号资料 PASSWORD: 修改密码 MOBILE: 绑定手机 EMAIL: 绑定邮箱
|
||||||
@@ -315,22 +397,66 @@ const handleOpenDialog = (type: DialogType) => {
|
|||||||
case DialogType.ACCOUNT:
|
case DialogType.ACCOUNT:
|
||||||
dialog.title = "账号资料";
|
dialog.title = "账号资料";
|
||||||
// 初始化表单数据
|
// 初始化表单数据
|
||||||
userProfileForm.id = userProfile.value.id;
|
|
||||||
userProfileForm.nickname = userProfile.value.nickname;
|
userProfileForm.nickname = userProfile.value.nickname;
|
||||||
|
userProfileForm.avatar = userProfile.value.avatar;
|
||||||
userProfileForm.gender = userProfile.value.gender;
|
userProfileForm.gender = userProfile.value.gender;
|
||||||
break;
|
break;
|
||||||
case DialogType.PASSWORD:
|
case DialogType.PASSWORD:
|
||||||
dialog.title = "修改密码";
|
dialog.title = "修改密码";
|
||||||
break;
|
break;
|
||||||
case DialogType.MOBILE:
|
case DialogType.MOBILE:
|
||||||
dialog.title = "绑定手机";
|
dialog.title = userProfile.value.mobile ? "更换手机号" : "绑定手机号";
|
||||||
|
mobileUpdateForm.mobile = "";
|
||||||
|
mobileUpdateForm.code = "";
|
||||||
|
mobileUpdateForm.password = "";
|
||||||
break;
|
break;
|
||||||
case DialogType.EMAIL:
|
case DialogType.EMAIL:
|
||||||
dialog.title = "绑定邮箱";
|
dialog.title = userProfile.value.email ? "更换邮箱" : "绑定邮箱";
|
||||||
|
emailUpdateForm.email = "";
|
||||||
|
emailUpdateForm.code = "";
|
||||||
|
emailUpdateForm.password = "";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function handleUnbindMobile() {
|
||||||
|
if (!userProfile.value.mobile) return;
|
||||||
|
try {
|
||||||
|
const { value } = await ElMessageBox.prompt("请输入当前密码以解绑手机号", "解绑手机号", {
|
||||||
|
type: "warning",
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
inputType: "password",
|
||||||
|
inputPlaceholder: "当前密码",
|
||||||
|
inputValidator: (val) => !!val || "请输入当前密码",
|
||||||
|
});
|
||||||
|
await UserAPI.unbindMobile({ password: value });
|
||||||
|
ElMessage.success("手机号解绑成功");
|
||||||
|
await loadUserProfile();
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleUnbindEmail() {
|
||||||
|
if (!userProfile.value.email) return;
|
||||||
|
try {
|
||||||
|
const { value } = await ElMessageBox.prompt("请输入当前密码以解绑邮箱", "解绑邮箱", {
|
||||||
|
type: "warning",
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
inputType: "password",
|
||||||
|
inputPlaceholder: "当前密码",
|
||||||
|
inputValidator: (val) => !!val || "请输入当前密码",
|
||||||
|
});
|
||||||
|
await UserAPI.unbindEmail({ password: value });
|
||||||
|
ElMessage.success("邮箱解绑成功");
|
||||||
|
await loadUserProfile();
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送手机验证码
|
* 发送手机验证码
|
||||||
*/
|
*/
|
||||||
@@ -395,33 +521,41 @@ function handleSendEmailCode() {
|
|||||||
* 提交表单
|
* 提交表单
|
||||||
*/
|
*/
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (dialog.type === DialogType.ACCOUNT) {
|
try {
|
||||||
UserAPI.updateProfile(userProfileForm).then(() => {
|
if (dialog.type === DialogType.ACCOUNT) {
|
||||||
|
const valid = await userProfileFormRef.value?.validate();
|
||||||
|
if (!valid) return;
|
||||||
|
|
||||||
|
await UserAPI.updateProfile(userProfileForm);
|
||||||
ElMessage.success("账号资料修改成功");
|
ElMessage.success("账号资料修改成功");
|
||||||
dialog.visible = false;
|
dialog.visible = false;
|
||||||
loadUserProfile();
|
await loadUserProfile();
|
||||||
});
|
} else if (dialog.type === DialogType.PASSWORD) {
|
||||||
} else if (dialog.type === DialogType.PASSWORD) {
|
const valid = await passwordChangeFormRef.value?.validate();
|
||||||
if (passwordChangeForm.newPassword !== passwordChangeForm.confirmPassword) {
|
if (!valid) return;
|
||||||
ElMessage.error("两次输入的密码不一致");
|
|
||||||
return;
|
await UserAPI.changePassword(passwordChangeForm);
|
||||||
|
dialog.visible = false;
|
||||||
|
await redirectToLogin("密码已修改,请重新登录");
|
||||||
|
} else if (dialog.type === DialogType.MOBILE) {
|
||||||
|
const valid = await mobileBindingFormRef.value?.validate();
|
||||||
|
if (!valid) return;
|
||||||
|
|
||||||
|
await UserAPI.bindOrChangeMobile(mobileUpdateForm);
|
||||||
|
ElMessage.success(userProfile.value.mobile ? "手机号更换成功" : "手机号绑定成功");
|
||||||
|
dialog.visible = false;
|
||||||
|
await loadUserProfile();
|
||||||
|
} else if (dialog.type === DialogType.EMAIL) {
|
||||||
|
const valid = await emailBindingFormRef.value?.validate();
|
||||||
|
if (!valid) return;
|
||||||
|
|
||||||
|
await UserAPI.bindOrChangeEmail(emailUpdateForm);
|
||||||
|
ElMessage.success(userProfile.value.email ? "邮箱更换成功" : "邮箱绑定成功");
|
||||||
|
dialog.visible = false;
|
||||||
|
await loadUserProfile();
|
||||||
}
|
}
|
||||||
UserAPI.changePassword(passwordChangeForm).then(() => {
|
} catch {
|
||||||
ElMessage.success("密码修改成功");
|
// ignore
|
||||||
dialog.visible = false;
|
|
||||||
});
|
|
||||||
} else if (dialog.type === DialogType.MOBILE) {
|
|
||||||
UserAPI.bindOrChangeMobile(mobileUpdateForm).then(() => {
|
|
||||||
ElMessage.success("手机号绑定成功");
|
|
||||||
dialog.visible = false;
|
|
||||||
loadUserProfile();
|
|
||||||
});
|
|
||||||
} else if (dialog.type === DialogType.EMAIL) {
|
|
||||||
UserAPI.bindOrChangeEmail(emailUpdateForm).then(() => {
|
|
||||||
ElMessage.success("邮箱绑定成功");
|
|
||||||
dialog.visible = false;
|
|
||||||
loadUserProfile();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -482,6 +616,15 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
await loadUserProfile();
|
await loadUserProfile();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (mobileTimer.value) {
|
||||||
|
clearInterval(mobileTimer.value);
|
||||||
|
}
|
||||||
|
if (emailTimer.value) {
|
||||||
|
clearInterval(emailTimer.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -134,7 +134,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "Dict",
|
name: "Dict",
|
||||||
inherititems: false,
|
inheritAttrs: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
import { ref, reactive } from "vue";
|
import { ref, reactive } from "vue";
|
||||||
|
|||||||
Reference in New Issue
Block a user