first commit

This commit is contained in:
JaguarJack
2022-12-05 23:01:12 +08:00
commit 0024080c28
322 changed files with 27698 additions and 0 deletions

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>