mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-10-12 12:57:12 +08:00
新增:新增上传头像 API,采用本地存储方式存储头像
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
class="form"
|
||||
:label-col-props="{ span: 8 }"
|
||||
:wrapper-col-props="{ span: 16 }"
|
||||
>
|
||||
<a-form-item
|
||||
:label="$t('userCenter.basicInfo.form.label.username')"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: $t('userCenter.form.error.username.required'),
|
||||
},
|
||||
]"
|
||||
disabled
|
||||
>
|
||||
<a-input
|
||||
v-model="formData.username"
|
||||
:placeholder="$t('userCenter.basicInfo.placeholder.username')"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="nickname"
|
||||
:label="$t('userCenter.basicInfo.form.label.nickname')"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: $t('userCenter.form.error.nickname.required'),
|
||||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
v-model="formData.nickname"
|
||||
:placeholder="$t('userCenter.basicInfo.placeholder.nickname')"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="gender"
|
||||
:label="$t('userCenter.basicInfo.form.label.gender')"
|
||||
>
|
||||
<a-radio-group v-model="formData.gender">
|
||||
<a-radio :value="1">男</a-radio>
|
||||
<a-radio :value="2">女</a-radio>
|
||||
<a-radio :value="0" disabled>未知</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="validate">
|
||||
{{ $t('userCenter.save') }}
|
||||
</a-button>
|
||||
<a-button type="secondary" @click="reset">
|
||||
{{ $t('userCenter.reset') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { useLoginStore } from '@/store';
|
||||
import { FormInstance } from '@arco-design/web-vue/es/form';
|
||||
import { BasicInfoModel } from '@/api/system/user-center';
|
||||
|
||||
const loginStore = useLoginStore();
|
||||
const formRef = ref<FormInstance>();
|
||||
const formData = ref<BasicInfoModel>({
|
||||
username: loginStore.username,
|
||||
nickname: loginStore.nickname,
|
||||
gender: loginStore.gender,
|
||||
});
|
||||
|
||||
// 保存
|
||||
const validate = async () => {
|
||||
const res = await formRef.value?.validate();
|
||||
if (!res) {
|
||||
// do some thing
|
||||
// you also can use html-type to submit
|
||||
}
|
||||
};
|
||||
|
||||
// 重置
|
||||
const reset = async () => {
|
||||
await formRef.value?.resetFields();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.form {
|
||||
width: 540px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<a-list :bordered="false">
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template #avatar>
|
||||
<a-typography-paragraph>
|
||||
{{ $t('userCenter.SecuritySettings.form.label.password') }}
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="content">
|
||||
<a-typography-paragraph v-if="loginStore.pwdResetTime">
|
||||
已设置
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph v-else class="tip">
|
||||
{{ $t('userCenter.SecuritySettings.placeholder.password') }}
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
<div class="operation">
|
||||
<a-link>
|
||||
{{ $t('userCenter.SecuritySettings.button.update') }}
|
||||
</a-link>
|
||||
</div>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template #avatar>
|
||||
<a-typography-paragraph>
|
||||
{{ $t('userCenter.SecuritySettings.form.label.phone') }}
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="content">
|
||||
<a-typography-paragraph v-if="loginStore.phone">
|
||||
已绑定:{{ loginStore.phone }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph v-else class="tip">
|
||||
{{ $t('userCenter.SecuritySettings.placeholder.phone') }}
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
<div class="operation">
|
||||
<a-link>
|
||||
{{ $t('userCenter.SecuritySettings.button.update') }}
|
||||
</a-link>
|
||||
</div>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template #avatar>
|
||||
<a-typography-paragraph>
|
||||
{{ $t('userCenter.SecuritySettings.form.label.email') }}
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="content">
|
||||
<a-typography-paragraph v-if="loginStore.email">
|
||||
已绑定:{{ loginStore.email }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph v-else class="tip">
|
||||
{{ $t('userCenter.SecuritySettings.placeholder.email') }}
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
<div class="operation">
|
||||
<a-link>
|
||||
{{ $t('userCenter.SecuritySettings.button.update') }}
|
||||
</a-link>
|
||||
</div>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useLoginStore } from '@/store';
|
||||
|
||||
const loginStore = useLoginStore();
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
:deep(.arco-list-item) {
|
||||
border-bottom: none !important;
|
||||
.arco-typography {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.arco-list-item-meta-avatar {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
.arco-list-item-meta {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
:deep(.arco-list-item-meta-content) {
|
||||
flex: 1;
|
||||
border-bottom: 1px solid var(--color-neutral-3);
|
||||
|
||||
.arco-list-item-meta-description {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
justify-content: space-between;
|
||||
|
||||
.tip {
|
||||
color: rgb(var(--gray-6));
|
||||
}
|
||||
.operation {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<a-card :bordered="false">
|
||||
<a-space :size="54">
|
||||
<a-upload
|
||||
:custom-request="handleUpload"
|
||||
list-type="picture-card"
|
||||
:file-list="avatarList"
|
||||
:show-upload-button="true"
|
||||
:show-file-list="false"
|
||||
@change="changeAvatar"
|
||||
>
|
||||
<template #upload-button>
|
||||
<a-avatar :size="100" class="info-avatar">
|
||||
<template #trigger-icon>
|
||||
<icon-camera />
|
||||
</template>
|
||||
<img v-if="avatarList.length" :src="avatarList[0].url" />
|
||||
</a-avatar>
|
||||
</template>
|
||||
</a-upload>
|
||||
<a-descriptions
|
||||
:data="renderData"
|
||||
:column="2"
|
||||
align="right"
|
||||
layout="inline-horizontal"
|
||||
:label-style="{
|
||||
width: '140px',
|
||||
fontWeight: 'normal',
|
||||
color: 'rgb(var(--gray-8))',
|
||||
}"
|
||||
:value-style="{
|
||||
width: '200px',
|
||||
paddingLeft: '8px',
|
||||
textAlign: 'left',
|
||||
}"
|
||||
>
|
||||
<template #label="{ label }">{{ $t(label) }} :</template>
|
||||
<template #value="{ value, data }">
|
||||
<div v-if="data.label === 'userCenter.label.gender'">
|
||||
<div v-if="loginStore.gender === 1">
|
||||
男
|
||||
<icon-man style="color: #19BBF1" />
|
||||
</div>
|
||||
<div v-else-if="loginStore.gender === 2">
|
||||
女
|
||||
<icon-woman style="color: #FA7FA9" />
|
||||
</div>
|
||||
<div v-else>未知</div>
|
||||
</div>
|
||||
<span v-else>{{ value }}</span>
|
||||
</template>
|
||||
</a-descriptions>
|
||||
</a-space>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import type {
|
||||
FileItem,
|
||||
RequestOption,
|
||||
} from '@arco-design/web-vue/es/upload/interfaces';
|
||||
import { useLoginStore } from '@/store';
|
||||
import { uploadAvatar } from '@/api/system/user-center';
|
||||
import type { DescData } from '@arco-design/web-vue/es/descriptions/interface';
|
||||
import getAvatar from "@/utils/avatar";
|
||||
import { Message } from "@arco-design/web-vue";
|
||||
|
||||
const loginStore = useLoginStore();
|
||||
const avatar = {
|
||||
uid: '-2',
|
||||
name: 'avatar.png',
|
||||
url: getAvatar(loginStore),
|
||||
};
|
||||
const renderData = [
|
||||
{
|
||||
label: 'userCenter.label.nickname',
|
||||
value: loginStore.nickname,
|
||||
},
|
||||
{
|
||||
label: 'userCenter.label.gender',
|
||||
value: loginStore.gender,
|
||||
},
|
||||
{
|
||||
label: 'userCenter.label.phone',
|
||||
value: loginStore.phone,
|
||||
},
|
||||
{
|
||||
label: 'userCenter.label.email',
|
||||
value: loginStore.email,
|
||||
},
|
||||
{
|
||||
label: 'userCenter.label.registrationDate',
|
||||
value: loginStore.registrationDate,
|
||||
},
|
||||
] as DescData[];
|
||||
const avatarList = ref<FileItem[]>([avatar]);
|
||||
|
||||
// 切换头像
|
||||
const changeAvatar = (fileItemList: FileItem[], currentFile: FileItem) => {
|
||||
avatarList.value = [currentFile];
|
||||
};
|
||||
|
||||
// 上传头像
|
||||
const handleUpload = (options: RequestOption) => {
|
||||
const controller = new AbortController();
|
||||
(async function requestWrap() {
|
||||
const {
|
||||
onProgress,
|
||||
onError,
|
||||
onSuccess,
|
||||
fileItem,
|
||||
name = 'avatarFile',
|
||||
} = options;
|
||||
onProgress(20);
|
||||
const formData = new FormData();
|
||||
formData.append(name as string, fileItem.file as Blob);
|
||||
try {
|
||||
const res = await uploadAvatar(formData);
|
||||
onSuccess(res);
|
||||
Message.success({
|
||||
content: res.msg || '网络错误',
|
||||
duration: 3 * 1000,
|
||||
});
|
||||
// 更换头像
|
||||
loginStore.avatar = res.data.avatar;
|
||||
} catch (error) {
|
||||
onError(error);
|
||||
}
|
||||
})();
|
||||
return {
|
||||
abort() {
|
||||
controller.abort();
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.arco-card {
|
||||
padding: 14px 0 4px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
:deep(.arco-avatar-trigger-icon-button) {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
background-color: #e8f3ff;
|
||||
.arco-icon-camera {
|
||||
margin-top: 8px;
|
||||
color: rgb(var(--arcoblue-6));
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
53
continew-admin-ui/src/views/system/user/center/index.vue
Normal file
53
continew-admin-ui/src/views/system/user/center/index.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<Breadcrumb :items="['menu.user.center']" />
|
||||
<a-row style="margin-bottom: 16px">
|
||||
<a-col :span="24">
|
||||
<UserPanel />
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row class="wrapper">
|
||||
<a-col :span="24">
|
||||
<a-tabs default-active-key="1" type="rounded">
|
||||
<a-tab-pane key="1" :title="$t('userCenter.tab.basicInformation')">
|
||||
<BasicInformation />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :title="$t('userCenter.tab.securitySettings')">
|
||||
<SecuritySettings />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import UserPanel from './components/user-panel.vue';
|
||||
import BasicInformation from './components/basic-information.vue';
|
||||
import SecuritySettings from './components/security-settings.vue';
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'UserCenter',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
padding: 20px 0 0 20px;
|
||||
min-height: 580px;
|
||||
background-color: var(--color-bg-2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:deep(.section-title) {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,31 @@
|
||||
export default {
|
||||
'menu.user.center': 'User Center',
|
||||
'userCenter.label.nickname': 'Nick Name',
|
||||
'userCenter.label.gender': 'Gender',
|
||||
'userCenter.label.phone': 'Phone',
|
||||
'userCenter.label.email': 'Email',
|
||||
'userCenter.label.registrationDate': 'Registration Date',
|
||||
|
||||
'userCenter.tab.basicInformation': 'Basic Information',
|
||||
'userCenter.basicInfo.form.label.username': 'Username',
|
||||
'userCenter.basicInfo.placeholder.username': 'Please enter username',
|
||||
'userCenter.form.error.username.required': 'Please enter username',
|
||||
'userCenter.basicInfo.form.label.nickname': 'Nickname',
|
||||
'userCenter.basicInfo.placeholder.nickname': 'Please enter nickname',
|
||||
'userCenter.form.error.nickname.required': 'Please enter nickname',
|
||||
'userCenter.basicInfo.form.label.gender': 'Gender',
|
||||
'userCenter.save': 'Save',
|
||||
'userCenter.reset': 'Reset',
|
||||
|
||||
'userCenter.tab.securitySettings': 'Security Settings',
|
||||
'userCenter.SecuritySettings.form.label.password': 'Login Password',
|
||||
'userCenter.SecuritySettings.placeholder.password':
|
||||
'You have not set a password yet. The password must contain at least six letters, digits, and special characters except Spaces.',
|
||||
'userCenter.SecuritySettings.form.label.phone': 'Phone',
|
||||
'userCenter.SecuritySettings.placeholder.phone':
|
||||
'You have not set a phone yet. The phone binding can be used to retrieve passwords and receive notifications and SMS login.',
|
||||
'userCenter.SecuritySettings.form.label.email': 'Email',
|
||||
'userCenter.SecuritySettings.placeholder.email':
|
||||
'You have not set a mailbox yet. The mailbox binding can be used to retrieve passwords and receive notifications.',
|
||||
'userCenter.SecuritySettings.button.update': 'Update',
|
||||
};
|
@@ -0,0 +1,31 @@
|
||||
export default {
|
||||
'menu.user.center': '个人中心',
|
||||
'userCenter.label.nickname': '昵称',
|
||||
'userCenter.label.gender': '性别',
|
||||
'userCenter.label.phone': '手机号码',
|
||||
'userCenter.label.email': '邮箱',
|
||||
'userCenter.label.registrationDate': '注册日期',
|
||||
|
||||
'userCenter.tab.basicInformation': '基础信息',
|
||||
'userCenter.basicInfo.form.label.username': '用户名',
|
||||
'userCenter.basicInfo.placeholder.username': '请输入您的用户名',
|
||||
'userCenter.form.error.username.required': '请输入用户名',
|
||||
'userCenter.basicInfo.form.label.nickname': '昵称',
|
||||
'userCenter.basicInfo.placeholder.nickname': '请输入您的昵称',
|
||||
'userCenter.form.error.nickname.required': '请输入昵称',
|
||||
'userCenter.basicInfo.form.label.gender': '性别',
|
||||
'userCenter.save': '保存',
|
||||
'userCenter.reset': '重置',
|
||||
|
||||
'userCenter.tab.securitySettings': '安全设置',
|
||||
'userCenter.SecuritySettings.form.label.password': '登录密码',
|
||||
'userCenter.SecuritySettings.placeholder.password':
|
||||
'您暂未设置密码,密码至少6位字符,支持数字、字母和除空格外的特殊字符。',
|
||||
'userCenter.SecuritySettings.form.label.phone': '安全手机',
|
||||
'userCenter.SecuritySettings.placeholder.phone':
|
||||
'您暂未设置手机号,绑定手机号可以用来找回密码、接收通知、短信登录等。',
|
||||
'userCenter.SecuritySettings.form.label.email': '安全邮箱',
|
||||
'userCenter.SecuritySettings.placeholder.email':
|
||||
'您暂未设置邮箱,绑定邮箱可以用来找回密码、接收通知等。',
|
||||
'userCenter.SecuritySettings.button.update': '修改',
|
||||
};
|
42
continew-admin-ui/src/views/system/user/center/mock.ts
Normal file
42
continew-admin-ui/src/views/system/user/center/mock.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import Mock from 'mockjs';
|
||||
import setupMock, { successResponseWrap } from '@/utils/setup-mock';
|
||||
|
||||
setupMock({
|
||||
setup() {
|
||||
Mock.mock(new RegExp('/api/user/save-info'), () => {
|
||||
return successResponseWrap('ok');
|
||||
});
|
||||
Mock.mock(new RegExp('/api/user/certification'), () => {
|
||||
return successResponseWrap({
|
||||
enterpriseInfo: {
|
||||
accountType: '企业账号',
|
||||
status: 0,
|
||||
time: '2022-12-27 20:00:00',
|
||||
legalPerson: '张**',
|
||||
certificateType: '中国身份证',
|
||||
authenticationNumber: '110************123',
|
||||
enterpriseName: '低调有实力的企业',
|
||||
enterpriseCertificateType: '企业营业执照',
|
||||
organizationCode: '7*******9',
|
||||
},
|
||||
record: [
|
||||
{
|
||||
certificationType: 1,
|
||||
certificationContent: '企业实名认证,法人姓名:张**',
|
||||
status: 0,
|
||||
time: '2022-12-27 20:00:00',
|
||||
},
|
||||
{
|
||||
certificationType: 1,
|
||||
certificationContent: '企业实名认证,法人姓名:张**',
|
||||
status: 1,
|
||||
time: '2022-12-27 20:00:00',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
Mock.mock(new RegExp('/api/user/upload'), () => {
|
||||
return successResponseWrap('ok');
|
||||
});
|
||||
},
|
||||
});
|
Reference in New Issue
Block a user