更新个人中心页面
This commit is contained in:
parent
e072be8dcb
commit
a42a307a81
|
@ -22,10 +22,6 @@
|
||||||
"elLoading": true,
|
"elLoading": true,
|
||||||
"elMessage": true,
|
"elMessage": true,
|
||||||
"elNotify": true,
|
"elNotify": true,
|
||||||
"filterAsyncRouter": true,
|
|
||||||
"filterAsyncRouterByCodes": true,
|
|
||||||
"filterAsyncRoutesByMenuList": true,
|
|
||||||
"filterAsyncRoutesByRoles": true,
|
|
||||||
"freshRouter": true,
|
"freshRouter": true,
|
||||||
"getCurrentInstance": true,
|
"getCurrentInstance": true,
|
||||||
"getCurrentScope": true,
|
"getCurrentScope": true,
|
||||||
|
@ -74,6 +70,7 @@
|
||||||
"routerBack": true,
|
"routerBack": true,
|
||||||
"routerPush": true,
|
"routerPush": true,
|
||||||
"routerReplace": true,
|
"routerReplace": true,
|
||||||
|
"setRouterFromDatabase": true,
|
||||||
"shallowReactive": true,
|
"shallowReactive": true,
|
||||||
"shallowReadonly": true,
|
"shallowReadonly": true,
|
||||||
"shallowRef": true,
|
"shallowRef": true,
|
||||||
|
|
|
@ -47,13 +47,17 @@ export async function getUser(idList): Promise<IUser[]> {
|
||||||
return post('/user/get', { idList })
|
return post('/user/get', { idList })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function editUser(data: IUser) {
|
||||||
|
return post('/user/edit', data)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按照文本搜索前十条用户
|
* 按照文本搜索前十条用户
|
||||||
* @param keys
|
* @param keys
|
||||||
* @return {Promise<any[]>}
|
* @return {Promise<any[]>}
|
||||||
*/
|
*/
|
||||||
export function userSearching(keys) {
|
export function userSearching(keys) {
|
||||||
return post('user/searching', { keys })
|
return post('/user/searching', { keys })
|
||||||
}
|
}
|
||||||
|
|
||||||
//修改密码
|
//修改密码
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { defineStore } from 'pinia'
|
||||||
import type { RouterTypes } from '~/basic'
|
import type { RouterTypes } from '~/basic'
|
||||||
import defaultSettings from '@/settings'
|
import defaultSettings from '@/settings'
|
||||||
import router, { constantRoutes } from '@/router'
|
import router, { constantRoutes } from '@/router'
|
||||||
|
import type { IUser } from '@/api/user'
|
||||||
|
|
||||||
export const useBasicStore = defineStore('basic', {
|
export const useBasicStore = defineStore('basic', {
|
||||||
state: () => {
|
state: () => {
|
||||||
|
@ -129,3 +130,23 @@ export const useBasicStore = defineStore('basic', {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export interface IUserInfo {
|
||||||
|
userName: string
|
||||||
|
realName: string
|
||||||
|
avatar: string
|
||||||
|
phone: string
|
||||||
|
id: string
|
||||||
|
isUse: boolean
|
||||||
|
team: string
|
||||||
|
iGameKey: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDatabaseType {
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
createdBy?: string
|
||||||
|
updatedBy?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUnionUserInfo extends IUserInfo, IUser, IDatabaseType {}
|
||||||
|
|
180
src/views/user/profile/components/UserDetail.vue
Normal file
180
src/views/user/profile/components/UserDetail.vue
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
<template>
|
||||||
|
<el-card class="user-detail el-descriptions">
|
||||||
|
<template #header>
|
||||||
|
<div class="user-detail-header">
|
||||||
|
<span class="user-detail-title">个人信息</span>
|
||||||
|
<el-button v-if="!editing" type="primary" @click="editing=true">修改</el-button>
|
||||||
|
<template v-else>
|
||||||
|
<el-button type="warning" @click="cancel">取消</el-button>
|
||||||
|
<el-button type="primary" @click="save">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<table class="user-table" :class="{'edit-mode': editing}">
|
||||||
|
<user-detail-body ref="body">
|
||||||
|
<user-detail-avatar-item />
|
||||||
|
<user-detail-item label="用户名" prop="realName" />
|
||||||
|
<user-detail-item label="账号" prop="userName" :editable="false" />
|
||||||
|
<user-detail-item label="手机号" prop="phone" />
|
||||||
|
<user-detail-password-item />
|
||||||
|
<user-detail-item label="UID" prop="id" :editable="false" />
|
||||||
|
<user-detail-item label="注册日期" prop="createdAt" :editable="false" />
|
||||||
|
<user-detail-item label="上次更新" prop="updatedAt" :editable="false" />
|
||||||
|
</user-detail-body>
|
||||||
|
</table>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import UserDetailItem from "@/views/user/profile/components/UserDetailItem.vue";
|
||||||
|
import UserDetailAvatarItem from "@/views/user/profile/components/UserDetailAvatarItem.vue";
|
||||||
|
import UserDetailPasswordItem from "@/views/user/profile/components/UserDetailPasswordItem.vue";
|
||||||
|
import { editUser } from "@/api/user";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import type { VNode } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
UserDetailBody: {
|
||||||
|
render() {
|
||||||
|
return h("tbody", this.$slots.default());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UserDetailItem,
|
||||||
|
UserDetailAvatarItem,
|
||||||
|
UserDetailPasswordItem
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const userInfo = toRef(useBasicStore(), "userInfo");
|
||||||
|
const editing = ref(false);
|
||||||
|
const copy = ref(JSON.parse(JSON.stringify(userInfo.value)));
|
||||||
|
|
||||||
|
provide("userDetail:editing", editing);
|
||||||
|
provide("userDetail:user", copy);
|
||||||
|
|
||||||
|
const body = ref();
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
try {
|
||||||
|
const vNodes = body.value.$.subTree.children as VNode[];
|
||||||
|
const component = vNodes.map(it => it.component!);
|
||||||
|
for (const c of component) {
|
||||||
|
const rs = c.exposed?.ok?.();
|
||||||
|
if (rs === false) {
|
||||||
|
ElMessage.error("请检查输入是否正确");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Error | any) {
|
||||||
|
ElMessage.error(e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
await editUser({
|
||||||
|
...copy.value,
|
||||||
|
avatar: undefined
|
||||||
|
});
|
||||||
|
ElMessage.success("操作成功");
|
||||||
|
userInfo.value = copy.value;
|
||||||
|
delete copy.password;
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
copy.value = JSON.parse(JSON.stringify(userInfo.value));
|
||||||
|
editing.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
cancel, save, editing, body
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.user-detail {
|
||||||
|
.user-detail-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-detail-title {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.edit-mode :deep(.item-content) {
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.edit-mode :deep(tr:not(.editing) .item-title) {
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.edit-mode :deep(tr.editing .item-content) {
|
||||||
|
box-shadow: none;
|
||||||
|
transition-property: box-shadow;
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
border-radius: 7px;
|
||||||
|
|
||||||
|
&:has(input) {
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:focus-within):hover {
|
||||||
|
box-shadow: 0 0 3px var(--el-color-primary) inset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.item-title) {
|
||||||
|
width: 128px;
|
||||||
|
background: var(--el-descriptions-item-bordered-label-background);
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(td) {
|
||||||
|
padding: 0.8em;
|
||||||
|
border: var(--el-descriptions-table-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.item-input) {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
/*开始时候下划线的宽度为100%*/
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -3px;
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
/*通过transform的缩放scale来让初始时x轴为0*/
|
||||||
|
transform: scale3d(0, 1, 1);
|
||||||
|
/*将坐标原点移到元素的中间,以原点为中心进行缩放*/
|
||||||
|
transform-origin: 50% 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within::after {
|
||||||
|
/*内部取得焦点时显示下划线*/
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
80
src/views/user/profile/components/UserDetailAvatarItem.vue
Normal file
80
src/views/user/profile/components/UserDetailAvatarItem.vue
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<tr :class="{editing}">
|
||||||
|
<td class="item-title">头像</td>
|
||||||
|
<td class="item-content">
|
||||||
|
<div v-if="!editing" class="avatar-frame">
|
||||||
|
<img :src="user.avatar || UserImage" class="avatar" alt="用户头像" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="avatar-frame">
|
||||||
|
<img :src="user.avatar || UserImage" class="avatar" alt="用户头像" />
|
||||||
|
<div class="avatar-tip" @click="showAvatarEditor = true">
|
||||||
|
<i class="bi bi-pen-fill" />
|
||||||
|
<span>修改头像</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<avatar-edit v-model="showAvatarEditor" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import UserImage from "@/assets/layout/user.png";
|
||||||
|
import type { IUnionUserInfo } from "@/store/basic";
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import AvatarEdit from "@/views/user/profile/components/AvatarEditor.vue";
|
||||||
|
|
||||||
|
const showAvatarEditor = ref(false);
|
||||||
|
const user: Ref<IUnionUserInfo> = inject("userDetail:user") as any;
|
||||||
|
const editing: Ref<boolean> = inject("userDetail:editing") as any;
|
||||||
|
|
||||||
|
const userInfo = toRef(useBasicStore(), "userInfo");
|
||||||
|
|
||||||
|
watch(() => userInfo.value.avatar, it => {
|
||||||
|
user.value.avatar = it || "";
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
ok: () => true
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.avatar-frame {
|
||||||
|
position: relative;
|
||||||
|
height: 96px;
|
||||||
|
width: 96px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 24%;
|
||||||
|
|
||||||
|
&:not(:hover) .avatar-tip {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
height: 96px;
|
||||||
|
width: 96px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-tip {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #88888888;
|
||||||
|
color: white;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.bi {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
36
src/views/user/profile/components/UserDetailItem.vue
Normal file
36
src/views/user/profile/components/UserDetailItem.vue
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<template>
|
||||||
|
<tr :class="{editing: editing && props.editable}">
|
||||||
|
<td class="item-title">{{ props.label ?? props.prop }}</td>
|
||||||
|
<td class="item-content">
|
||||||
|
<div v-if="editing && props.editable" class="item-input">
|
||||||
|
<input v-model="user[props.prop]" />
|
||||||
|
</div>
|
||||||
|
<template v-else>{{ user[props.prop] }}</template>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
import type { IUnionUserInfo } from "@/store/basic";
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
prop: keyof IUnionUserInfo,
|
||||||
|
editable?: boolean
|
||||||
|
label?: string
|
||||||
|
}>(), {
|
||||||
|
editable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const user: Ref<IUnionUserInfo> = inject("userDetail:user") as any;
|
||||||
|
const editing: Ref<boolean> = inject("userDetail:editing") as any;
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
ok: () => true
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
66
src/views/user/profile/components/UserDetailPasswordItem.vue
Normal file
66
src/views/user/profile/components/UserDetailPasswordItem.vue
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<tr v-if="editing" :class="{editing}">
|
||||||
|
<td class="item-title">密码</td>
|
||||||
|
<td class="item-content">
|
||||||
|
<div class="item-input">
|
||||||
|
<input v-model="passwords.a" type="password" placeholder="若要修改密码,请在此处输入" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr v-if="editing && passwords.a" :class="{editing}">
|
||||||
|
<td class="item-title">再输入一遍密码</td>
|
||||||
|
<td class="item-content">
|
||||||
|
<div class="item-input" :class="{error}">
|
||||||
|
<input v-model="passwords.b" type="password" />
|
||||||
|
</div>
|
||||||
|
<span v-if="error" class="tip">两次输入的密码不一致</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
import type { IUnionUserInfo } from "@/store/basic";
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
|
||||||
|
const user: Ref<IUnionUserInfo> = inject("userDetail:user") as any;
|
||||||
|
const editing: Ref<boolean> = inject("userDetail:editing") as any;
|
||||||
|
|
||||||
|
const passwords = reactive({
|
||||||
|
a: "",
|
||||||
|
b: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
const error = computed(() => {
|
||||||
|
return passwords.a && passwords.b && (passwords.a !== passwords.b);
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
ok() {
|
||||||
|
if (passwords.a) {
|
||||||
|
if (passwords.a != passwords.b) {
|
||||||
|
throw new Error("两次输入的密码不一致");
|
||||||
|
}
|
||||||
|
user.value.password = passwords.a;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.item-content {
|
||||||
|
.error::after {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
font-size: 0.8em;
|
||||||
|
bottom: 3px;
|
||||||
|
color: red;
|
||||||
|
//text-shadow: 1px 1px 1px darkred;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,47 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="user-profile">
|
<div class="user-profile">
|
||||||
<el-card header="个人信息">
|
<user-detail />
|
||||||
<el-descriptions label-width="120px" size="default" border :column="1">
|
|
||||||
<el-descriptions-item label="头像">
|
|
||||||
<div class="avatar-frame">
|
|
||||||
<img :src="userInfo.avatar || UserImage" class="avatar" alt="用户头像" @click="showAvatarEditor = true" />
|
|
||||||
<div class="avatar-tip">
|
|
||||||
<i class="bi bi-pen-fill" />
|
|
||||||
<span>修改头像</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="用户名">
|
|
||||||
{{ userInfo.realName }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="账号">
|
|
||||||
{{ userInfo.userName }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="手机号">
|
|
||||||
{{ userInfo.phone }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="UID">
|
|
||||||
{{ userInfo.id }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="注册日期">
|
|
||||||
{{ userInfo.createdAt }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="上次更新">
|
|
||||||
{{ userInfo.updatedAt }}
|
|
||||||
</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-card>
|
|
||||||
<avatar-edit v-model="showAvatarEditor" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AvatarEdit from "@/views/user/profile/components/AvatarEditor.vue";
|
import UserDetail from "@/views/user/profile/components/UserDetail.vue";
|
||||||
import UserImage from "@/assets/layout/user.png";
|
|
||||||
|
|
||||||
const showAvatarEditor = ref(false);
|
|
||||||
const userInfo = toRef(useBasicStore(), "userInfo");
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
5
typings/auto-imports.d.ts
vendored
5
typings/auto-imports.d.ts
vendored
|
@ -23,10 +23,6 @@ declare global {
|
||||||
const elLoading: typeof import('../src/hooks/use-element')['elLoading']
|
const elLoading: typeof import('../src/hooks/use-element')['elLoading']
|
||||||
const elMessage: typeof import('../src/hooks/use-element')['elMessage']
|
const elMessage: typeof import('../src/hooks/use-element')['elMessage']
|
||||||
const elNotify: typeof import('../src/hooks/use-element')['elNotify']
|
const elNotify: typeof import('../src/hooks/use-element')['elNotify']
|
||||||
const filterAsyncRouter: typeof import('../src/hooks/use-permission')['filterAsyncRouter']
|
|
||||||
const filterAsyncRouterByCodes: typeof import('../src/hooks/use-permission')['filterAsyncRouterByCodes']
|
|
||||||
const filterAsyncRoutesByMenuList: typeof import('../src/hooks/use-permission')['filterAsyncRoutesByMenuList']
|
|
||||||
const filterAsyncRoutesByRoles: typeof import('../src/hooks/use-permission')['filterAsyncRoutesByRoles']
|
|
||||||
const freshRouter: typeof import('../src/hooks/use-permission')['freshRouter']
|
const freshRouter: typeof import('../src/hooks/use-permission')['freshRouter']
|
||||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
|
@ -75,6 +71,7 @@ declare global {
|
||||||
const routerBack: typeof import('../src/hooks/use-self-router')['routerBack']
|
const routerBack: typeof import('../src/hooks/use-self-router')['routerBack']
|
||||||
const routerPush: typeof import('../src/hooks/use-self-router')['routerPush']
|
const routerPush: typeof import('../src/hooks/use-self-router')['routerPush']
|
||||||
const routerReplace: typeof import('../src/hooks/use-self-router')['routerReplace']
|
const routerReplace: typeof import('../src/hooks/use-self-router')['routerReplace']
|
||||||
|
const setRouterFromDatabase: typeof import('../src/hooks/use-permission')['setRouterFromDatabase']
|
||||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||||
const shallowRef: typeof import('vue')['shallowRef']
|
const shallowRef: typeof import('vue')['shallowRef']
|
||||||
|
|
Loading…
Reference in New Issue
Block a user