用户个人中心功能开发v3 - 登录记录静态页开发

pull/1/head
ccongli 1 year ago
parent 0a1af829b6
commit 72eab321f5

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

@ -4,6 +4,7 @@
* { * {
font-family: 'AlibabaPuHuiTi-3-55-Regular'; font-family: 'AlibabaPuHuiTi-3-55-Regular';
box-sizing: border-box;
} }
.reset-margin [class*='el-icon'] + span { .reset-margin [class*='el-icon'] + span {

@ -7,7 +7,9 @@
<el-tab-pane :label="t('profile.tabs.ChangePassword')"> <el-tab-pane :label="t('profile.tabs.ChangePassword')">
<ResetPwd /> <ResetPwd />
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="t('profile.tabs.LoginRecord')">登录记录</el-tab-pane> <el-tab-pane :label="t('profile.tabs.LoginRecord')">
<LoginRecord />
</el-tab-pane>
<el-tab-pane :label="t('profile.tabs.SystemNotice')">系统通知</el-tab-pane> <el-tab-pane :label="t('profile.tabs.SystemNotice')">系统通知</el-tab-pane>
<el-tab-pane :label="t('profile.tabs.MessageSetting')">消息设置</el-tab-pane> <el-tab-pane :label="t('profile.tabs.MessageSetting')">消息设置</el-tab-pane>
<el-tab-pane :label="t('profile.tabs.CompanyList')">公司列表</el-tab-pane> <el-tab-pane :label="t('profile.tabs.CompanyList')">公司列表</el-tab-pane>
@ -17,7 +19,7 @@
<script setup lang="ts" name="Profile"> <script setup lang="ts" name="Profile">
// import { BasicInfo, ProfileUser, ResetPwd, UserSocial } from './components/' // import { BasicInfo, ProfileUser, ResetPwd, UserSocial } from './components/'
import { BaseInfo, ResetPwd } from './components/' import { BaseInfo, ResetPwd, LoginRecord } from './components/'
const { t } = useI18n() const { t } = useI18n()

@ -0,0 +1,298 @@
<template>
<div class="container baseinfo">
<div class="subject">
<div class="lc"></div>
<div class="lh">基本资料</div>
</div>
<div class="formarea">
<el-form :model="form" label-width="120px" size="large">
<div class="item">基础信息</div>
<el-row :gutter="20">
<el-col :span="15">
<el-form-item label="登录账号">
<el-input v-model="form.username" disabled />
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="form.nickname" placeholder="取个昵称吧" />
</el-form-item>
<el-form-item label="所属部门">
<el-input v-model="form.dept_id" readonly />
</el-form-item>
<el-form-item label="员工职务">
<el-input v-model="form.post_id" readonly />
</el-form-item>
<el-form-item label="员工工号">
<el-input v-model="form.job_number" readonly />
</el-form-item>
<el-form-item label="手机号码">
<el-col :span="18" style="padding:0 10px 0 0;">
<el-input v-model="form.mobile" disabled />
</el-col>
<el-col :span="6" style="padding: 0">
<el-button type="primary" plain @click="state.mobileDialogVisible = true" style="width: 100%">更改</el-button>
</el-col>
</el-form-item>
<el-form-item label="微信账号">
<el-input v-model="form.wechat_account" disabled />
</el-form-item>
<el-form-item label="邮箱地址">
<el-input v-model="form.email" />
</el-form-item>
<el-form-item label="出生日期">
<el-date-picker v-model="form.birth_datetime" type="date" size="large" style="width: 100%" />
</el-form-item>
<el-form-item label="性别">
<el-input v-model="form.sex" type="text" v-show="false" />
<el-row class="sex-choose mb-4">
<el-button :type="form.sex == 1 ? 'primary' : 'default'" round @click="form.sex = 1"
style="width: 120px;">
<el-icon class="el-icon--right">
<Male />
</el-icon>
</el-button>
<el-button :type="form.sex == 2 ? 'primary' : 'default'" round @click="form.sex = 2"
style="width: 120px;">
<el-icon class="el-icon--right">
<Female />
</el-icon>
</el-button>
<el-button :type="form.sex == 0 ? 'primary' : 'default'" round @click="form.sex = 0"
style="width: 120px;">
保密
</el-button>
</el-row>
</el-form-item>
<el-form-item label="头像" v-show="false">
<el-input v-model="form.avatar" />
</el-form-item>
</el-col>
<el-col :span="6">
<div class="avatar">
<el-avatar :size="120" :src="form.avatar" />
<div style="margin-top: 5px;">
<el-button @click="state.avatarDialogVisible = true">修改头像</el-button>
</div>
</div>
</el-col>
</el-row>
<div class="item">更多信息</div>
<el-row :gutter="20">
<el-col :span="15">
<el-form-item label="籍贯">
<el-select v-model="form.native_place" clearable placeholder="籍贯" style="width: 100%">
<el-option label="汉族" :value="1" />
<el-option label="其他" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="简介">
<el-input v-model="form.personal_profile" type="textarea" :rows="5" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" @click="onSubmit" style="width: 160px;">保存</el-button>
</el-form-item>
</el-form>
</div>
<el-dialog v-model="state.avatarDialogVisible" title="修改头像" width="450px">
<div style="padding: 0 60px;">
<h3>上传图片文件</h3>
<div class="dialog-tips">图片尺寸需要大于100 * 100像素支持jpgpngjpeg等格式大小不能超过2MB</div>
<div style="padding: 20px;">
<CropperAvatar ref="cropperRef" :btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }" :showBtn="false"
:value="form.avatar" width="120px" @change="handleUpload" />
</div>
</div>
<!-- <template #footer>
<div class="dialog-options">
<el-button type="primary" @click="state.dialogVisible = false;">
上传头像
</el-button>
<el-button @click="state.dialogVisible = false">重新上传</el-button>
</div>
</template> -->
</el-dialog>
<el-dialog v-model="state.mobileDialogVisible" title="修改手机号" width="600px">
<div style="padding: 20px 30px 50px 30px;">
<el-form :model="form2" label-width="100px" size="large">
<el-form-item label="原手机号码">
<el-input v-model="form2.oldPhone" />
</el-form-item>
<el-form-item label="新手机号码">
<el-input v-model="form2.newPhone" />
</el-form-item>
<el-form-item label="验证码">
<el-col :span="18" style="padding-right: 10px;">
<el-input v-model="form2.code" placeholder="输入验证码" />
</el-col>
<el-col :span="6">
<template v-if="mobileCodeTimer <= 0">
<el-button type="primary" @click="getSmsCode" style="width: 100%">
{{ t('login.getSmsCode') }}
</el-button>
</template>
<template v-else>
<el-button type="default" disabled style="width: 100%">{{ mobileCodeTimer }} </el-button>
</template>
</el-col>
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-options2">
<el-button type="primary" @click="state.mobileDialogVisible = false;" style="width: 100px;">确认</el-button>
<el-button @click="state.mobileDialogVisible = false" style="width: 100px;">取消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { CropperAvatar } from '@/components/Cropper'
import { Female, Male } from '@element-plus/icons-vue'
import { uploadAvatar } from '@/api/system/user/profile'
defineOptions({ name: 'BaseInfo' })
const { t } = useI18n()
const message = useMessage()
const state = reactive({
// circleUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
avatarDialogVisible: false,
mobileDialogVisible: false,
});
// const dialogVisible = ref<boolean>(false);
const form = reactive({
username: 'kangning',
nickname: '康师傅红烧牛肉面',
sex: 1,
avatar: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
dept_id: '商品项目部',
post_id: '总经理',
job_number: 'CJYX202300001',
mobile: '13120939311',
email: 'koko@qq.com',
wechat_account: 'wxjgfkhkhuissbk35646',
birth_datetime: undefined,
native_place: 1,
personal_profile: '生而为人,工作很忙~'
});
//
const form2 = reactive({
oldPhone: "",
newPhone: "",
code: ""
});
//
const cropperRef = ref()
//
const handleUpload = async ({ data }) => {
// await uploadAvatar({ avatarFile: data })
console.log(data);
cropperRef.value.close();
state.avatarDialogVisible = false;
}
//
const mobileCodeTimer = ref(0)
const getSmsCode = async () => {
message.success(t('login.SmsSendMsg'))
//
mobileCodeTimer.value = 60
let msgTimer = setInterval(() => {
mobileCodeTimer.value = mobileCodeTimer.value - 1
if (mobileCodeTimer.value <= 0) {
clearInterval(msgTimer)
}
}, 1000)
}
//
const onSubmit = () => {
console.log('submit!')
}
</script>
<style lang="scss" scoped>
.baseinfo {
width: 100%;
padding: 20px;
padding-left: 50px;
box-sizing: border-box;
::v-deep {
.el-form-item {
margin-top: 18px;
margin-bottom: 0;
}
.el-dialog__body {
padding: 0;
text-align: center;
}
.el-dialog__footer {
text-align: center;
}
}
.subject {
height: 60px;
color: #666;
display: flex;
align-items: center;
line-height: 20px;
.lc {
width: 8px;
height: 20px;
background: #409eff;
margin-right: 10px;
}
.lh {
color: #666;
font-size: 20px;
font-weight: 700;
}
}
.formarea {
padding-left: 20px;
.item {
line-height: 50px;
height: 50px;
}
.avatar {
text-align: center;
}
}
.dialog-tips {
color: #666;
padding: 0 10px;
}
.dialog-options {
:deep(.el-button) {
display: block;
margin: 0 auto;
margin-top: 20px;
width: 160px;
height: 40px;
}
}
}
</style>

@ -0,0 +1,252 @@
<template>
<div class="container login-record">
<div class="subject">
<div class="lc"></div>
<div class="lh">登录记录</div>
</div>
<div class="record">
<div class="items" v-for="(record, dateKey) in records" :key="dateKey">
<div class="date">{{ dateKey }}</div>
<div class="item" v-for="(item, index) in record" :key="item.id">
<div class="item-1">
<div class="item-1-left">
<el-image :src="RecordImg" fit="fill" />
</div>
<div class="item-1-right">
<div class="username">{{ item.username }}</div>
<div class="ip">本地 {{ item.ip }}</div>
</div>
</div>
<div class="item-2">
<el-icon>
<Clock />
</el-icon>
<span style="margin-left: 5px;">2023-08-22 12:01:38</span>
</div>
</div>
</div>
</div>
<div class="page">
<el-pagination background layout="prev, pager, next" :total="page.total" hide-on-single-page
v-model:current-page="page.current" v-model:page-size="page.pageSize" @current-change="handleCurrentChange" />
</div>
</div>
</template>
<script lang="ts" setup>
import { Clock } from '@element-plus/icons-vue'
import RecordImg from '@/assets/imgs/system.png';
defineOptions({ name: 'LoginRecord' })
const { t } = useI18n()
const message = useMessage()
interface RecordType {
id: number,
username?: string,
ip?: string,
createTime?: string
}
const logData = ref<RecordType[]>([]);
const page = reactive({
current: 1,
pageSize: 10,
total: 400
});
const records = computed<any>(() => {
let arr = {};
for (let log of logData.value) {
let datePart = log.createTime?.split(" ")[0] || "unknow";
if (!arr[datePart]) {
arr[datePart] = [];
}
arr[datePart].push(log);
}
// console.log(arr);
return arr;
});
onMounted(() => {
getRecordsData();
console.log(123456);
});
const getRecordsData = () => {
logData.value = [
{
'id': 1,
'username': 'Kang-PC',
'ip': '127.0.0.1',
'createTime': '2022-08-22 13:08:31',
},
{
'id': 2,
'username': 'Kang-MB',
'ip': '127.0.0.1',
'createTime': '2022-08-22 12:08:31',
},
{
'id': 3,
'username': 'Kang-MB',
'ip': '127.0.0.1',
'createTime': '2022-08-22 11:08:31',
},
{
'id': 4,
'username': 'Kang-PC',
'ip': '127.0.0.1',
'createTime': '2022-08-22 10:08:31',
},
{
'id': 5,
'username': 'Kang-PC',
'ip': '127.0.0.1',
'createTime': '2022-08-22 09:08:31',
},
{
'id': 6,
'username': 'Kang-PC',
'ip': '127.0.0.1',
'createTime': '2022-08-21 13:08:31',
},
{
'id': 7,
'username': 'Kang-MB',
'ip': '127.0.0.1',
'createTime': '2022-08-21 12:08:31',
},
{
'id': 8,
'username': 'Kang-MB',
'ip': '127.0.0.1',
'createTime': '2022-08-21 11:08:31',
},
{
'id': 9,
'username': 'Kang-PC',
'ip': '127.0.0.1',
'createTime': '2022-08-21 10:08:31',
},
{
'id': 10,
'username': 'Kang-MB',
'ip': '127.0.0.1',
'createTime': '2022-08-21 09:08:31',
},
{
'id': 11,
'username': 'Kang-MB',
'ip': '127.0.0.1',
'createTime': '2022-08-21 08:08:31',
},
{
'id': 12,
'username': 'Kang-PC',
'ip': '127.0.0.1',
'createTime': '2022-08-21 07:08:31',
},
]
};
//
const handleCurrentChange = (val: number) => {
console.log(`current page: ${val}`)
}
</script>
<style lang="scss" scoped>
.login-record {
width: 100%;
padding: 20px;
padding-left: 50px;
box-sizing: border-box;
.subject {
height: 60px;
color: #666;
display: flex;
align-items: center;
line-height: 20px;
.lc {
width: 8px;
height: 20px;
background: #409eff;
margin-right: 10px;
}
.lh {
color: #666;
font-size: 20px;
font-weight: 700;
}
}
.record {
width: 100%;
.date {
height: 60px;
line-height: 60px;
padding-left: 20px;
margin-top: 20px;
font-weight: 700;
color: #999;
}
.item {
height: 100px;
border: 1px solid #ededed;
padding: 20px;
margin-top: 10px;
color: #666;
display: flex;
justify-content: space-between;
align-items: center;
.item-1 {
display: flex;
.item-1-left ::v-deep {
margin-right: 20px;
.el-image {
width: 60px;
height: 60px;
border: 1px solid #e4e4e4;
border-radius: 50%;
}
}
.item-1-right {
.username {
font-size: 20px;
margin-bottom: 10px;
}
.ip {
color: #999;
font-size: 16px;
}
}
}
.item-2 {
font-size: 16px;
display: flex;
align-items: center;
}
}
}
.page {
float: right;
margin-top: 30px;
}
}
</style>

@ -1,7 +1,8 @@
import BaseInfo from './BaseInfo.vue' import BaseInfo from './BaseInfo.vue'
import ProfileUser from './ProfileUser.vue' import LoginRecord from './LoginRecord.vue'
import ResetPwd from './ResetPwd.vue' import ResetPwd from './ResetPwd.vue'
import ProfileUser from './ProfileUser.vue'
import UserAvatarVue from './UserAvatar.vue' import UserAvatarVue from './UserAvatar.vue'
import UserSocial from './UserSocial.vue' import UserSocial from './UserSocial.vue'
export { BaseInfo, ProfileUser, ResetPwd, UserAvatarVue, UserSocial } export { BaseInfo, ResetPwd, LoginRecord,ProfileUser, UserAvatarVue, UserSocial }

Loading…
Cancel
Save