首次提交
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
<!--
|
||||
- MineAdmin is committed to providing solutions for quickly building web applications
|
||||
- Please view the LICENSE file that was distributed with this source code,
|
||||
- For the full copyright and license information.
|
||||
- Thank you very much for using MineAdmin.
|
||||
-
|
||||
- @Author X.Mo<root@imoi.cn>
|
||||
- @Link https://gitee.com/xmo/mineadmin-vue
|
||||
-->
|
||||
<template>
|
||||
<a-form class="w-full md:w-full mt-3" :model="password" @submit="modifyPassword">
|
||||
<a-form-item
|
||||
label="旧密码"
|
||||
field="oldPassword"
|
||||
label-col-flex="80px"
|
||||
:rules="[{ required: true, message: '旧密码必填' }]"
|
||||
>
|
||||
<a-input-password v-model="password.oldPassword" allow-clear autocomplete="off" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="新密码"
|
||||
field="newPassword"
|
||||
label-col-flex="80px"
|
||||
:rules="[{ required: true, message: '新密码必填' }]"
|
||||
>
|
||||
<a-input-password
|
||||
v-model="password.newPassword"
|
||||
@input="checkSafe"
|
||||
@clear="() => (passwordSafePercent = 0)"
|
||||
autocomplete="off"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="密码安全" label-col-flex="80px">
|
||||
<a-progress :steps="3" status="success" :percent="passwordSafePercent" animation :show-text="false" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="确认密码"
|
||||
field="newPassword_confirmation"
|
||||
label-col-flex="80px"
|
||||
:rules="[{ required: true, message: '确认密码必填' }]"
|
||||
>
|
||||
<a-input-password allow-clear v-model="password.newPassword_confirmation" autocomplete="off" />
|
||||
</a-form-item>
|
||||
<a-form-item label-col-flex="80px">
|
||||
<a-button html-type="submit" type="primary">保存</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-modal v-model:visible="visible" @ok="resetLogin">
|
||||
<template #title>提示</template>
|
||||
密码已经修改成功,需要重新登录系统,点击确定跳转登录页面。
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from "vue"
|
||||
import { Message } from "@arco-design/web-vue"
|
||||
import user from "@/api/system/user"
|
||||
import tool from "@/utils/tool"
|
||||
|
||||
const password = reactive({
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
newPassword_confirmation: ""
|
||||
})
|
||||
|
||||
const visible = ref(false)
|
||||
const passwordSafePercent = ref(0)
|
||||
|
||||
const resetLogin = () => {
|
||||
window.location.href = "/"
|
||||
}
|
||||
|
||||
const modifyPassword = async (data) => {
|
||||
if (!data.errors) {
|
||||
if (data.values.newPassword !== data.values.newPassword_confirmation) {
|
||||
Message.error("确认密码与新密码不一致")
|
||||
return
|
||||
}
|
||||
const response = await user.modifyPassword(data.values)
|
||||
if (response.success) {
|
||||
tool.local.clear()
|
||||
visible.value = true
|
||||
} else {
|
||||
Message.error(response.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const checkSafe = (password) => {
|
||||
if (password.length < 1) {
|
||||
passwordSafePercent.value = 0
|
||||
return
|
||||
}
|
||||
|
||||
if (!(password.length >= 6)) {
|
||||
passwordSafePercent.value = 0
|
||||
return
|
||||
}
|
||||
|
||||
passwordSafePercent.value = 0.1
|
||||
|
||||
if (/\d/.test(password)) {
|
||||
passwordSafePercent.value += 0.1
|
||||
}
|
||||
|
||||
if (/[a-z]/.test(password)) {
|
||||
passwordSafePercent.value += 0.1
|
||||
}
|
||||
|
||||
if (/[A-Z]/.test(password)) {
|
||||
passwordSafePercent.value += 0.3
|
||||
}
|
||||
|
||||
if (/[`~!@#$%^&*()_+<>?:"{},./;'[\]]/.test(password)) {
|
||||
passwordSafePercent.value += 0.4
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,54 @@
|
||||
<!--
|
||||
- MineAdmin is committed to providing solutions for quickly building web applications
|
||||
- Please view the LICENSE file that was distributed with this source code,
|
||||
- For the full copyright and license information.
|
||||
- Thank you very much for using MineAdmin.
|
||||
-
|
||||
- @Author X.Mo<root@imoi.cn>
|
||||
- @Link https://gitee.com/xmo/mineadmin-vue
|
||||
-->
|
||||
<template>
|
||||
<a-form class="w-full md:w-full mt-3" :model="userInfo" @submit="modifyInfo">
|
||||
<a-form-item label="账户名" label-col-flex="80px">
|
||||
<a-input disabled :default-value="userInfo.username" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="昵称" label-col-flex="80px">
|
||||
<a-input v-model="userInfo.nickname" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="手机" label-col-flex="80px">
|
||||
<a-input v-model="userInfo.phone" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="邮箱" label-col-flex="80px">
|
||||
<a-input v-model="userInfo.email" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label="个人签名" label-col-flex="80px">
|
||||
<a-textarea v-model="userInfo.signed" :max-length="255" class="h-28" show-word-limit allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item label-col-flex="80px">
|
||||
<a-button html-type="submit" type="primary">保存</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from "vue"
|
||||
import { useUserStore } from "@/store"
|
||||
import { Message } from "@arco-design/web-vue"
|
||||
import user from "@/api/system/user"
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const userInfo = reactive({
|
||||
...userStore.user
|
||||
})
|
||||
|
||||
const modifyInfo = async (data) => {
|
||||
data.values.avatar = userStore.user.avatar
|
||||
const response = await user.updateInfo(data.values)
|
||||
if (response.success) {
|
||||
Message.success(response.message)
|
||||
userStore.user = data.values
|
||||
return
|
||||
}
|
||||
}
|
||||
</script>
|
||||
132
chengduTestPlant/src/views/userCenter/index.vue
Normal file
132
chengduTestPlant/src/views/userCenter/index.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<!--
|
||||
- MineAdmin is committed to providing solutions for quickly building web applications
|
||||
- Please view the LICENSE file that was distributed with this source code,
|
||||
- For the full copyright and license information.
|
||||
- Thank you very much for using MineAdmin.
|
||||
-
|
||||
- @Author X.Mo<root@imoi.cn>
|
||||
- @Link https://gitee.com/xmo/mineadmin-vue
|
||||
-->
|
||||
<template>
|
||||
<div class="block">
|
||||
<div class="user-header rounded-sm text-center">
|
||||
<div class="pt-3 mx-auto avatar-box">
|
||||
<ma-upload v-model="userInfo.avatar" rounded />
|
||||
</div>
|
||||
<div>
|
||||
<a-tag size="large" class="mt-3 rounded-full" color="#165dff">
|
||||
{{ (userStore.user && userStore.user.nickname) || (userStore.user && userStore.user.username) }}
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-layout-content class="block lg:flex lg:justify-between">
|
||||
<div class="ma-content-block w-full lg:w-6/12 mt-3 p-4">
|
||||
<a-tabs type="rounded">
|
||||
<a-tab-pane key="info" title="个人资料">
|
||||
<user-infomation />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="safe" title="安全设置">
|
||||
<modify-password />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
<div class="ma-content-block w-full lg:w-6/12 mt-3 p-4 ml-0 lg:ml-3">
|
||||
<a-tabs type="rounded">
|
||||
<a-tab-pane key="login-log" title="登录日志">
|
||||
<a-timeline class="pl-5 mt-3">
|
||||
<a-timeline-item
|
||||
:label="`地理位置;${item.ip_location},操作系统:${item.os}`"
|
||||
v-for="(item, idx) in loginLogList"
|
||||
:key="idx"
|
||||
>
|
||||
您于 {{ item.login_time }} 登录系统,{{ item.message }}
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="operation-log" title="操作日志">
|
||||
<a-timeline class="pl-5 mt-3">
|
||||
<a-timeline-item
|
||||
:label="`地理位置;${item.ip_location},方式:${item.method},路由:${item.router}`"
|
||||
v-for="(item, idx) in operationLogList"
|
||||
:key="idx"
|
||||
>
|
||||
您于 {{ item.created_at }} 执行了 {{ item.service_name }}
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</a-layout-content>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch } from "vue"
|
||||
import MaUpload from "@cps/ma-upload/index.vue"
|
||||
import { useUserStore } from "@/store"
|
||||
import { Message } from "@arco-design/web-vue"
|
||||
import user from "@/api/system/user"
|
||||
import commonApi from "@/api/common"
|
||||
|
||||
import ModifyPassword from "./components/modifyPassword.vue"
|
||||
import UserInfomation from "./components/userInfomation.vue"
|
||||
|
||||
const userStore = useUserStore()
|
||||
const userInfo = reactive({
|
||||
...userStore.user
|
||||
})
|
||||
|
||||
const loginLogList = ref([])
|
||||
const operationLogList = ref([])
|
||||
|
||||
const requestParams = reactive({
|
||||
username: userStore.user.username,
|
||||
pageSize: 5
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
commonApi
|
||||
.getLoginLogList(Object.assign(requestParams, { orderBy: "login_time", orderType: "desc" }))
|
||||
.then((res) => {
|
||||
loginLogList.value = res.data.items
|
||||
})
|
||||
|
||||
commonApi
|
||||
.getOperationLogList(Object.assign(requestParams, { orderBy: "created_at", orderType: "desc" }))
|
||||
.then((res) => {
|
||||
operationLogList.value = res.data.items
|
||||
})
|
||||
})
|
||||
|
||||
userInfo.avatar =
|
||||
userStore.user && userStore.user.avatar ? userStore.user.avatar : `${import.meta.env.VITE_APP_BASE}avatar.jpg`
|
||||
|
||||
watch(
|
||||
() => userInfo.avatar,
|
||||
async (newAvatar) => {
|
||||
if (newAvatar) {
|
||||
const response = await user.updateInfo({ id: userInfo.id, avatar: newAvatar })
|
||||
if (response.success) {
|
||||
Message.success("头像修改成功")
|
||||
userStore.user.avatar = newAvatar
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<script>
|
||||
export default { name: "userCenter" }
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.avatar-box {
|
||||
width: 130px;
|
||||
}
|
||||
.user-header {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background: url("@/assets/userBanner.jpg") no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
258
chengduTestPlant/src/views/userCenter/message.vue
Normal file
258
chengduTestPlant/src/views/userCenter/message.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<!--
|
||||
- MineAdmin is committed to providing solutions for quickly building web applications
|
||||
- Please view the LICENSE file that was distributed with this source code,
|
||||
- For the full copyright and license information.
|
||||
- Thank you very much for using MineAdmin.
|
||||
-
|
||||
- @Author X.Mo<root@imoi.cn>
|
||||
- @Link https://gitee.com/xmo/mineadmin-vue
|
||||
-->
|
||||
<template>
|
||||
<div class="ma-content-block p-3 lg:h-full block lg:border-0 lg:flex justify-between">
|
||||
<ul class="w-full lg:w-52 msg-menu p-2 shadow" ref="msgMenuRef">
|
||||
<li v-for="(item, index) in msgType" :key="item" @click="getMessageList(item.key, index)">
|
||||
<Component :is="typeIcon[item.key] ? typeIcon[item.key] : 'icon-message'" />
|
||||
<span class="pl-3">{{ item.title }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="h-hull w-full lg:ml-3 lg:mr-2 pt-2">
|
||||
<ma-crud :options="crud" :columns="columns" ref="crudRef">
|
||||
<template #tableAfterButtons>
|
||||
<a-radio-group
|
||||
class="mt-2 lg:mt-0"
|
||||
type="button"
|
||||
default-value="all"
|
||||
@change="changeReadStatus"
|
||||
v-if="currentKey !== 'send_box'"
|
||||
>
|
||||
<a-radio value="all">全部</a-radio>
|
||||
<a-radio value="1">未读</a-radio>
|
||||
<a-radio value="2">已读</a-radio>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
|
||||
<template #operationBeforeExtend="{ record }">
|
||||
<a-link @click="showReceiveList(record.id)" v-if="currentKey === 'send_box'">
|
||||
<icon-user-group /> 接收用户
|
||||
</a-link>
|
||||
|
||||
<a-link @click="showDetail(record)"> <icon-eye /> 详细 </a-link>
|
||||
</template>
|
||||
</ma-crud>
|
||||
</div>
|
||||
|
||||
<a-drawer v-model:visible="detailVisible" width="1000px" :footer="false">
|
||||
<template #title>消息详情</template>
|
||||
<a-spin :loading="detailLoading" tip="数据加载中..." class="block">
|
||||
<a-typography :style="{ marginTop: '-30px' }">
|
||||
<a-typography-title>
|
||||
{{ record?.title }}
|
||||
</a-typography-title>
|
||||
<a-typography-paragraph class="text-right" style="font-size: 13px; color: var(--color-text-3)">
|
||||
<a-space size="large">
|
||||
<span>创建时间:{{ record?.created_at }}</span>
|
||||
</a-space>
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph>
|
||||
<div v-html="record?.content"></div>
|
||||
</a-typography-paragraph>
|
||||
</a-typography>
|
||||
</a-spin>
|
||||
</a-drawer>
|
||||
|
||||
<a-modal v-model:visible="receiveListVisible" width="1000px">
|
||||
<template #title>接收用户列表</template>
|
||||
<ma-crud :options="receiveCrud" :columns="receiveColumns" ref="receiveCrudRef" />
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { nextTick, onMounted, ref } from "vue"
|
||||
import commonApi from "@/api/common"
|
||||
import queueMessage from "@/api/system/queueMessage"
|
||||
|
||||
const typeIcon = ref({
|
||||
send_box: "icon-send",
|
||||
receive_box: "icon-email",
|
||||
carbon_copy_mine: "icon-copy",
|
||||
todo: "icon-calendar",
|
||||
announcement: "icon-mobile",
|
||||
notice: "icon-notification",
|
||||
private_message: "icon-message"
|
||||
})
|
||||
|
||||
const msgType = ref([])
|
||||
const msgMenuRef = ref()
|
||||
const crudRef = ref()
|
||||
const receiveCrudRef = ref()
|
||||
const currentKey = ref()
|
||||
const detailVisible = ref(false)
|
||||
const detailLoading = ref(true)
|
||||
const receiveListVisible = ref(false)
|
||||
const record = ref({})
|
||||
|
||||
onMounted(async () => {
|
||||
const response = await commonApi.getDict("queue_msg_type")
|
||||
msgType.value = response.data
|
||||
msgType.value.unshift(
|
||||
...[
|
||||
{ key: "receive_box", title: "收件箱" },
|
||||
{ key: "send_box", title: "已发送" }
|
||||
]
|
||||
)
|
||||
await getMessageList("receive_box", 0)
|
||||
})
|
||||
|
||||
const getMessageList = async (key, index) => {
|
||||
nextTick(() => {
|
||||
const children = msgMenuRef.value.children
|
||||
if (children && children[index].className.indexOf("active") === -1) {
|
||||
for (let i = 0; i < children.length; i++) children[i].className = ""
|
||||
children[index].className = "active"
|
||||
if (!["send_box", "receive_box"].includes(key)) {
|
||||
crud.value.requestParams.content_type = key
|
||||
} else {
|
||||
crud.value.requestParams.content_type = "all"
|
||||
}
|
||||
currentKey.value = key
|
||||
loadData(key)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const loadData = (key) => {
|
||||
crud.value.api = key === "send_box" ? queueMessage.getSendList : queueMessage.getReceiveList
|
||||
const sendBy = {
|
||||
title: "发送人",
|
||||
dataIndex: "send_user.nickname",
|
||||
width: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false
|
||||
}
|
||||
if (key === "send_box" && columns.value[0].title === "发送人") {
|
||||
columns.value.splice(0, 1)
|
||||
} else if (key !== "send_box" && columns.value[0].title !== "发送人") {
|
||||
columns.value.unshift(sendBy)
|
||||
}
|
||||
nextTick(() => crudRef.value.requestData())
|
||||
}
|
||||
|
||||
const changeReadStatus = (key) => {
|
||||
crud.value.requestParams.read_status = key
|
||||
crudRef.value.requestData()
|
||||
}
|
||||
|
||||
const showDetail = async (row) => {
|
||||
detailVisible.value = true
|
||||
detailLoading.value = true
|
||||
await queueMessage.updateReadStatus({ ids: row.id })
|
||||
record.value = row
|
||||
detailLoading.value = false
|
||||
}
|
||||
|
||||
const showReceiveList = (id) => {
|
||||
receiveListVisible.value = true
|
||||
receiveCrud.value.requestParams.id = id
|
||||
receiveCrudRef.value.requestData()
|
||||
}
|
||||
|
||||
const receiveCrud = ref({
|
||||
api: queueMessage.getReceiveUser,
|
||||
requestParams: {},
|
||||
autoRequest: false
|
||||
})
|
||||
|
||||
const receiveColumns = ref([
|
||||
{ title: "用户名", dataIndex: "username" },
|
||||
{ title: "昵称", dataIndex: "nickname" },
|
||||
{ title: "查看状态", dataIndex: "read_status" }
|
||||
])
|
||||
|
||||
const crud = ref({
|
||||
autoRequest: false,
|
||||
showIndex: false,
|
||||
requestParams: { read_status: "all" },
|
||||
rowSelection: { showCheckedAll: true },
|
||||
add: { show: true, text: "发私信", api: queueMessage.sendPrivateMessage },
|
||||
delete: { show: true, api: queueMessage.deletes },
|
||||
operationColumn: true,
|
||||
operationWidth: 240,
|
||||
formOption: { width: 800 },
|
||||
api: () => {}
|
||||
})
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: "消息标题",
|
||||
dataIndex: "title",
|
||||
search: true,
|
||||
commonRules: [
|
||||
{
|
||||
required: true,
|
||||
message: "消息标题必填"
|
||||
}
|
||||
],
|
||||
validateTrigger: "blur"
|
||||
},
|
||||
{
|
||||
title: "消息类型",
|
||||
dataIndex: "content_type",
|
||||
formType: "select",
|
||||
width: 150,
|
||||
dict: { name: "queue_msg_type", translation: true, props: { label: "title", value: "key" } },
|
||||
addDisplay: false,
|
||||
editDisplay: false
|
||||
},
|
||||
{
|
||||
title: "接收用户",
|
||||
dataIndex: "users",
|
||||
formType: "user-select",
|
||||
isEcho: true,
|
||||
commonRules: [
|
||||
{
|
||||
required: true,
|
||||
message: "请选择接收用户"
|
||||
}
|
||||
],
|
||||
hide: true
|
||||
},
|
||||
{
|
||||
title: "消息内容",
|
||||
dataIndex: "content",
|
||||
formType: "editor",
|
||||
hide: true
|
||||
},
|
||||
{
|
||||
title: "发送时间",
|
||||
dataIndex: "created_at",
|
||||
width: 180,
|
||||
search: true,
|
||||
formType: "range",
|
||||
addDisplay: false,
|
||||
editDisplay: false
|
||||
}
|
||||
])
|
||||
</script>
|
||||
<script>
|
||||
export default { name: "message" }
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.msg-menu {
|
||||
// border-right: 1px solid var(--color-border-2);
|
||||
& li {
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 5px;
|
||||
padding: 10px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
& li:hover,
|
||||
& li.active {
|
||||
background: var(--color-fill-2);
|
||||
color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user