first commit
This commit is contained in:
44
modules/User/views/user/center.vue
Normal file
44
modules/User/views/user/center.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="flex flex-col sm:flex-row dark:bg-regal-dark w-full">
|
||||
<el-card shadow="never" class="w-full sm:w-[35rem]">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>个人资料</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="w-full">
|
||||
<Profile />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-tabs v-model="activeName" class="pl-3 pr-3 bg-white dark:bg-regal-dark mt-2 sm:mt-0 w-full ml-2">
|
||||
<el-tab-pane label="登录日志" name="login_log">
|
||||
<LoginLog />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作日志" name="operation_log">
|
||||
<OperateLog />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import Profile from './components/profile.vue'
|
||||
import LoginLog from './components/loginLog.vue'
|
||||
import OperateLog from './components/operateLog.vue'
|
||||
|
||||
const activeName = ref('login_log')
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tabs {
|
||||
--el-tabs-header-height: 62px !important;
|
||||
}
|
||||
|
||||
.el-tabs .el-tabs__item {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
</style>
|
45
modules/User/views/user/components/loginLog.vue
Normal file
45
modules/User/views/user/components/loginLog.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<el-table :data="tableData" class="mt-3" v-loading="loading">
|
||||
<el-table-column prop="account" label="账户" width="150px" />
|
||||
<el-table-column prop="browser" label="浏览器" width="100px" />
|
||||
<el-table-column prop="platform" label="平台" width="100px" />
|
||||
<el-table-column prop="login_ip" label="IP" width="120px" />
|
||||
<el-table-column prop="status" label="状态" width="100px">
|
||||
<template #default="scope">
|
||||
<el-tag type="success" v-if="scope.row.status === 1">成功</el-tag>
|
||||
<el-tag type="danger" v-else>失败</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="login_at" label="登录时间" />
|
||||
</el-table>
|
||||
|
||||
<div class="pt-2 pb-2 flex justify-end">
|
||||
<el-pagination
|
||||
background
|
||||
v-if="total > query.limit"
|
||||
layout="total,sizes,prev, pager,next"
|
||||
:current-page="query.page"
|
||||
:page-size="query.limit"
|
||||
@current-change="changePage"
|
||||
@size-change="changeLimit"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useGetList } from '/admin/composables/curd/useGetList'
|
||||
|
||||
const api = 'user/login/log'
|
||||
|
||||
const { data, query, search, changePage, changeLimit, loading } = useGetList(api)
|
||||
|
||||
onMounted(() => search())
|
||||
|
||||
const tableData = computed(() => data.value?.data)
|
||||
const total = computed(() => data.value?.total)
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
43
modules/User/views/user/components/operateLog.vue
Normal file
43
modules/User/views/user/components/operateLog.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<el-table :data="tableData" class="mt-3" v-loading="loading">
|
||||
<el-table-column prop="username" label="用户名" width="180" />
|
||||
<el-table-column prop="avatar" label="头像" width="180" />
|
||||
<el-table-column prop="email" label="邮箱" />
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="scope">
|
||||
<Status v-model="scope.row.status" :id="scope.row.id" :api="api" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" />
|
||||
</el-table>
|
||||
|
||||
<div class="pt-2 pb-2 flex justify-end">
|
||||
<el-pagination
|
||||
background
|
||||
v-if="total > query.limit"
|
||||
layout="total,sizes,prev, pager,next"
|
||||
:current-page="query.page"
|
||||
:page-size="query.limit"
|
||||
@current-change="changePage"
|
||||
@size-change="changeLimit"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { useGetList } from '/admin/composables/curd/useGetList'
|
||||
|
||||
const api = 'users'
|
||||
|
||||
const { data, query, search, reset, changePage, changeLimit, loading } = useGetList(api)
|
||||
|
||||
onMounted(() => search())
|
||||
|
||||
const tableData = computed(() => data.value?.data)
|
||||
const total = computed(() => data.value?.total)
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
88
modules/User/views/user/components/profile.vue
Normal file
88
modules/User/views/user/components/profile.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<el-form :model="profile" ref="form" v-loading="loading" label-position="top">
|
||||
<el-upload
|
||||
class="w-28 h-28 rounded-full mx-auto"
|
||||
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
>
|
||||
<img src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" class="h-28 rounded-full" />
|
||||
</el-upload>
|
||||
<el-form-item
|
||||
label="昵称"
|
||||
prop="username"
|
||||
class="mt-2"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '昵称必须填写',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-input v-model="profile.username" placeholder="请填写昵称" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="邮箱"
|
||||
prop="email"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '邮箱必须填写',
|
||||
},
|
||||
{
|
||||
type: 'email',
|
||||
message: '邮箱格式不正确',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-input v-model="profile.email" placeholder="请填写邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="密码"
|
||||
prop="password"
|
||||
:rules="[
|
||||
{
|
||||
pattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/,
|
||||
message: '必须包含大小写字母和数字的组合,可以使用特殊字符,长度在6-20之间',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-input v-model="profile.password" type="password" show-password placeholder="请输入密码" />
|
||||
</el-form-item>
|
||||
<div class="flex justify-center">
|
||||
<el-button type="primary" @click="submitForm(form)">{{ $t('system.update') }}</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, unref } from 'vue'
|
||||
import { useCreate } from '/admin/composables/curd/useCreate'
|
||||
import http from '/admin/support/http'
|
||||
|
||||
interface profile {
|
||||
avatar: string
|
||||
username: string
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
const profile = ref<profile>(
|
||||
Object.assign({
|
||||
avatar: '',
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
}),
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
http.get('user/online').then(r => {
|
||||
profile.value.username = r.data.data.username
|
||||
profile.value.avatar = r.data.data.avatar
|
||||
profile.value.email = r.data.data.email
|
||||
})
|
||||
})
|
||||
const { form, loading, submitForm } = useCreate('user/online', null, profile)
|
||||
</script>
|
82
modules/User/views/user/create.vue
Normal file
82
modules/User/views/user/create.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<el-form :model="formData" label-width="120px" ref="form" v-loading="loading" class="pr-4">
|
||||
<el-form-item
|
||||
label="昵称"
|
||||
prop="username"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '昵称必须填写',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-input v-model="formData.username" placeholder="请填写昵称" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="邮箱"
|
||||
prop="email"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '邮箱必须填写',
|
||||
},
|
||||
{
|
||||
type: 'email',
|
||||
message: '邮箱格式不正确',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-input v-model="formData.email" placeholder="请填写邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password" :rules="passwordRules">
|
||||
<el-input v-model="formData.password" type="password" show-password placeholder="请输入密码" />
|
||||
</el-form-item>
|
||||
<div class="flex justify-end">
|
||||
<el-button type="primary" @click="submitForm(form)">{{ $t('system.confirm') }}</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useCreate } from '/admin/composables/curd/useCreate'
|
||||
import { useShow } from '/admin/composables/curd/useShow'
|
||||
|
||||
import { onMounted, watch, ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
primary: String | Number,
|
||||
api: String,
|
||||
})
|
||||
|
||||
const passwordRules = [
|
||||
{
|
||||
required: true,
|
||||
message: '密码必须填写',
|
||||
},
|
||||
{
|
||||
pattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,20}$/,
|
||||
message: '必须包含大小写字母和数字的组合,可以使用特殊字符,长度在6-20之间',
|
||||
},
|
||||
]
|
||||
|
||||
if (props.primary) {
|
||||
passwordRules.shift()
|
||||
}
|
||||
|
||||
const { formData, form, loading, submitForm, isClose } = useCreate(props.api, props.primary)
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
watch(isClose, function (value) {
|
||||
if (value) {
|
||||
emit('close')
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (props.primary) {
|
||||
useShow(props.api, props.primary).then(r => {
|
||||
formData.value = r.data
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
104
modules/User/views/user/index.vue
Normal file
104
modules/User/views/user/index.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="w-full min-h-0 bg-white dark:bg-regal-dark pl-5 pt-5 pr-5 rounded-lg">
|
||||
<el-form :inline="true">
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="query.username" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="query.email" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<Select v-model="query.status" clearable api="status" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="search()">
|
||||
<Icon name="magnifying-glass" class="w-4 mr-1 -ml-1" />
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="reset()">
|
||||
<Icon name="arrow-path" class="w-4 mr-1 -ml-1" />
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="pl-2 pr-2 bg-white dark:bg-regal-dark rounded-lg mt-4">
|
||||
<div class="pt-5 pl-2">
|
||||
<Add @click="show(null)" />
|
||||
</div>
|
||||
<el-table :data="tableData" class="mt-3" v-loading="loading">
|
||||
<el-table-column prop="username" label="用户名" width="180" />
|
||||
<el-table-column prop="avatar" label="头像" width="180" />
|
||||
<el-table-column prop="email" label="邮箱" />
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="scope">
|
||||
<Status v-model="scope.row.status" :id="scope.row.id" :api="api" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" />
|
||||
<el-table-column label="操作" width="200">
|
||||
<template #default="scope">
|
||||
<Update @click="show(scope.row.id)" />
|
||||
<Destroy @click="destroy(api, scope.row.id)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pt-2 pb-2 flex justify-end">
|
||||
<el-pagination
|
||||
background
|
||||
layout="total,sizes,prev, pager,next"
|
||||
:current-page="query.page"
|
||||
:page-size="query.limit"
|
||||
@current-change="changePage"
|
||||
@size-change="changeLimit"
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog v-model="visible" :title="title" destroy-on-close>
|
||||
<Create @close="close" :primary="id" :api="api" />
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import Create from './create.vue'
|
||||
import { useGetList } from '/admin/composables/curd/useGetList'
|
||||
import { useDestroy } from '/admin/composables/curd/useDestroy'
|
||||
import { useEnabled } from '/admin/composables/curd/useEnabled'
|
||||
import { t } from '/admin/support/helper'
|
||||
|
||||
const visible = ref<boolean>(false)
|
||||
const id = ref(null)
|
||||
const api = 'users'
|
||||
const title = ref<string>('')
|
||||
|
||||
const { data, query, search, reset, changePage, changeLimit, loading } = useGetList(api)
|
||||
const { destroy, isDeleted } = useDestroy()
|
||||
|
||||
onMounted(() => search())
|
||||
|
||||
const tableData = computed(() => data.value?.data)
|
||||
const total = computed(() => data.value?.total)
|
||||
|
||||
const close = () => {
|
||||
visible.value = false
|
||||
reset()
|
||||
}
|
||||
|
||||
const show = primary => {
|
||||
title.value = primary ? t('system.edit') : t('system.add')
|
||||
id.value = primary
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
watch(isDeleted, function () {
|
||||
isDeleted.value = false
|
||||
reset()
|
||||
})
|
||||
</script>
|
Reference in New Issue
Block a user