Commit 494f74b4 by qinjianhui

feat: 用户管理开发

parent 5a7fb7c0
VITE_API_BASE=/api
VITE_API_BASE_URL=/
VITE_API_BASE_UPLOAD_URL=/upload/factory
\ No newline at end of file
......@@ -8,6 +8,8 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
ElButton: typeof import('element-plus/es')['ElButton']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDialog: typeof import('element-plus/es')['ElDialog']
......@@ -31,6 +33,7 @@ declare module 'vue' {
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
Icon: typeof import('./src/components/Icon.vue')['default']
ImageView: typeof import('./src/components/ImageView.vue')['default']
LogList: typeof import('./src/components/LogList.vue')['default']
NavMenu: typeof import('./src/components/NavMenu.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
......
import { BaseRespData } from '@/types/api'
import { BasePaginationData, BaseRespData } from '@/types/api'
import axios from './axios'
import { LoginReq, LoginResp } from '@/types/api/auth'
import { UserEditForm, userData, userSearchForm } from '@/types/api/user'
export function loginApi(data: LoginReq) {
return axios.post<never, LoginResp>('/factory/login', data)
}
// 修改密码
export function changePasswordApi(data: {
oldPwd: string
newPwd: string
}) {
export function changePasswordApi(data: { oldPwd: string; newPwd: string }) {
return axios.post<never, BaseRespData<never>>(
'factory/factoryUser/updatePassword',
data,
)
}
// 用户列表
export function getUserList(
data: userSearchForm,
currentPage: number,
pageSize: number,
) {
return axios.post<never, BasePaginationData<userData>>(
'/factory/factoryUser/list_page',
{
...data,
currentPage,
pageSize,
},
)
}
// 新增用户
export function addUserApi(form: UserEditForm) {
return axios.post<never, BaseRespData<never>>('/factory/factoryUser/add', {
...form,
})
}
// 编辑用户
export function updateUserApi(form: UserEditForm) {
return axios.post<never, BaseRespData<never>>('/factory/factoryUser/update', {
...form,
})
}
// 删除用户
export function deleteUserApi(ids: string) {
return axios.get<never, BaseRespData<never>>('/factory/factoryUser/delete', {
params: { id: ids },
})
}
export function getDetailsByIdApi(id: number) {
return axios.get<never, BaseRespData<UserEditForm>>(
'/factory/factoryUser/get',
{
params: {
id,
},
},
)
}
......@@ -44,9 +44,9 @@ axios.interceptors.response.use((response) => {
})
export function getFilePath() {
if (!/(http|https):\/\/([^/]+)/i.test(import.meta.env.BASE_URL)) {
return location.origin + import.meta.env.BASE_URL
return location.origin + import.meta.env.VITE_API_BASE_UPLOAD_URL
}
return import.meta.env.BASE_URL
return import.meta.env.VITE_API_BASE_URL + import.meta.env.VITE_API_BASE_UPLOAD_URL
}
export const filePath = getFilePath()
......
<template>
<div
ref="imageViewRef"
class="image-view"
@mousemove="onMoseMove"
@mouseleave="onMoseLeave"
>
<img :src="src" />
</div>
</template>
<script setup lang="ts">
import useImagePreview from '@/utils/hooks/useImagePreview.'
const props = defineProps({
src: {
type: String,
default: () => '',
},
})
const { onMoseMove, onMoseLeave } = useImagePreview(props.src)
</script>
<style lang="scss" scoped></style>
......@@ -39,7 +39,7 @@
<el-dropdown style="color: #fff; font-size: 14px">
<span class="el-dropdown-link">
<el-icon style="vertical-align: middle"><User /></el-icon>
{{ userInfo.factory.title }}
{{ userInfo.account }}
<el-icon style="vertical-align: middle" class="el-icon--right">
<arrow-down />
</el-icon>
......
......@@ -8,6 +8,7 @@ import Error from '@/views/error/404.vue'
import OrderList from '@/views/order/index.vue'
import ProductionComplete from '@/views/production/complete.vue'
import { getToken} from '@/api/axios'
import UserPage from '@/views/UserPage.vue'
const router = createRouter({
history: createWebHistory(),
......@@ -28,6 +29,10 @@ const router = createRouter({
{
path: '/production/complete',
component: ProductionComplete
},
{
path: '/system/user',
component: UserPage
}
],
},
......
......@@ -19,15 +19,27 @@ const menu: MenuItem[] = [
{
index: '',
id: 3,
label: '生产管理',
label: '系统设置',
children: [
{
index: '/production/complete',
index: '/system/user',
id: 4,
label: '生产完成',
label: '用户管理',
}
]
},
// {
// index: '',
// id: 3,
// label: '生产管理',
// children: [
// {
// index: '/production/complete',
// id: 4,
// label: '生产完成',
// }
// ]
// },
]
export default menu
export interface userData {
id: number
account?: string
password?: string
supperMark?: number
factoryId?: number
factoryCode?: string
status?: number
createTime?: string
}
export interface userSearchForm {
account?: string
status?: number
}
export interface UserEditForm {
password: string
supperMark: number
account: string
status: number
}
// 预览大图
export default function useImagePreview(src: string) {
const imageView = document.createElement('img')
// 鼠标在元素内部移动
const onMoseMove = (e: MouseEvent) => {
imageView.className = 'image-view'
imageView.src = src
imageView.style.backgroundColor = '#eee'
document.body.appendChild(imageView)
const cW: number = document.body.clientWidth
const cH: number = document.body.clientHeight
const cX: number = e.clientX
const cY: number = e.clientY
let x: number = 0
let y: number = 0
if (cY + 380 >= cH) y = cY - 440
else y = cY - 30
if (cX + 440 >= cW) x = cX - 440
else x = cX + 30
imageView.onload = () => {
imageView.style.left = x + 'px'
imageView.style.top = y + 'px'
imageView.style.display = 'block'
}
}
// 鼠标离开元素
const onMoseLeave = () => {
imageView.style.display = 'none'
}
return {
onMoseMove,
onMoseLeave,
}
}
<template>
<div class="user-page flex-column card h-100 overflow-hidden">
<div class="header-filter-form">
<ElForm :model="searchForm" inline>
<ElFormItem label="用户名" prop="account">
<ElInput
v-model="searchForm.account"
placeholder="请输入用户名"
clearable
style="width: 200px"
/>
</ElFormItem>
<ElFormItem label="状态" prop="status">
<ElSelect
v-model="searchForm.status"
clearable
style="width: 200px"
placeholder="请选择状态"
>
<ElOption label="启用" :value="1"></ElOption>
<ElOption label="禁用" :value="0"></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="search">查询</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton @click="resetSearchForm">重置</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton type="success" @click="addUser">新增</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton type="danger" @click="deleteUser">删除</ElButton>
</ElFormItem>
</ElForm>
</div>
<div class="user-content flex-1 flex-column overflow-hidden">
<div class="user-list flex-1 overflow-hidden">
<ElTable
:data="tableData"
default-expand-all
style="width: 100%; height: 100%"
@selection-change="handleSelectionChange"
>
<ElTableColumn
type="selection"
header-align="center"
align="center"
width="55"
/>
<ElTableColumn
type="index"
header-align="center"
align="center"
label="序号"
width="55"
/>
<ElTableColumn prop="account" header-align="center" label="用户名" />
<ElTableColumn
prop="factoryCode"
header-align="center"
label="工厂"
align="center"
/>
<ElTableColumn
prop="status"
header-align="center"
align="center"
label="状态"
>
<template #default="scope">
<ElTag v-if="scope.row.status === 1" type="success">启用</ElTag>
<ElTag v-if="scope.row.status === 0" type="danger">禁用</ElTag>
</template>
</ElTableColumn>
<ElTableColumn
label="操作"
width="130"
header-align="center"
align="center"
>
<template #default="scope">
<el-icon
size="24"
title="编辑"
color="#EF6C00"
style="cursor: pointer; vertical-align: middle"
@click="editUser(scope.row)"
>
<Edit />
</el-icon>
</template>
</ElTableColumn>
</ElTable>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</div>
</div>
<ElDialog
v-model="dialogVisible"
:title="editId ? '编辑用户' : '新增用户'"
width="600px"
:close-on-click-modal="false"
@opened="onOpenedUserForm"
>
<div class="dialog-form">
<ElForm
ref="editFormRef"
:model="editForm"
:rules="rules"
label-width="90px"
>
<ElFormItem label="用户名" prop="account">
<ElInput
v-model="editForm.account"
placeholder="请输入用户名"
clearable
/>
</ElFormItem>
<ElFormItem label="密码" prop="password">
<ElInput
v-model="editForm.password"
placeholder="请输入密码"
clearable
type="password"
autocomplete="off"
:show-password="true"
/>
</ElFormItem>
<ElFormItem label="状态" prop="status">
<ElCheckbox
v-model="editForm.status"
label="启用"
:true-value="1"
:false-value="0"
></ElCheckbox>
</ElFormItem>
<ElFormItem label="角色" prop="supperMark">
<ElCheckbox
v-model="editForm.supperMark"
label="超级管理员"
:true-value="1"
:false-value="0"
></ElCheckbox>
</ElFormItem>
</ElForm>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false"> 取消 </ElButton>
<ElButton type="primary" @click="save">保存</ElButton>
</div>
</template>
</ElDialog>
</template>
<script setup lang="ts">
import {
getUserList,
addUserApi,
updateUserApi,
deleteUserApi,
getDetailsByIdApi,
} from '@/api/auth'
import { UserEditForm, userData, userSearchForm } from '@/types/api/user'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue'
import { showConfirm, showError } from '@/utils/ui'
import { Edit } from '@element-plus/icons-vue'
import type { FormRules } from 'element-plus'
import { reactive, ref } from 'vue'
const [searchForm, resetSearchForm] = useValue<userSearchForm>({})
const [editForm, resetEditForm] = useValue<UserEditForm>({
account: '',
password: '',
supperMark: 0,
status: 1,
})
const {
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
getUserList(searchForm.value, page, pageSize).then((res) => res.data),
})
const dialogVisible = ref(false)
const editFormRef = ref()
const selection = ref<userData[]>([])
const rules = reactive<FormRules<UserEditForm>>({
account: [
{
required: true,
message: '请输入用户名',
},
],
password: [
{
required: true,
message: '请输入密码',
},
],
})
const editId = ref<number | undefined>(undefined)
const addUser = () => {
editId.value = undefined
dialogVisible.value = true
resetEditForm()
}
const deleteUser = async () => {
if (!selection.value.length) {
return ElMessage({
message: '请选择用户',
type: 'warning',
offset: window.innerHeight / 2,
})
}
try {
await showConfirm('是否删除用户', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const ids = selection.value.map((item) => item.id).join(',')
const res = await deleteUserApi(ids)
ElMessage.success(res.message)
search()
} catch (e) {
showError(e)
}
}
const editUser = async (item: userData) => {
editId.value = item.id
try {
const res = await getDetailsByIdApi(item.id)
editForm.value = res.data
dialogVisible.value = true
} catch (e) {
showError(e)
}
}
const save = async () => {
try {
await editFormRef.value.validate()
} catch {
return
}
try {
if (!editId.value) {
await addUserApi(editForm.value)
} else {
await updateUserApi(editForm.value)
}
ElMessage({
message: '保存成功',
type: 'success',
offset: window.innerHeight / 2,
})
dialogVisible.value = false
search()
} catch (e) {
return
}
}
const onOpenedUserForm = async () => {
editFormRef.value?.clearValidate()
}
const handleSelectionChange = (s: userData[]) => {
selection.value = s
}
</script>
<style lang="scss" scoped>
.header-filter-form {
margin-bottom: 20px;
:deep(.el-form-item) {
margin-right: 14px;
margin-bottom: 10px;
}
}
.user-operate-btn {
margin-bottom: 10px;
}
.dialog-footer {
text-align: center;
}
</style>
......@@ -5,9 +5,9 @@
<div class="error-code">{{ code }}</div>
</slot>
<div class="text-lg text-tx-secondary mt-7 mb-7">{{ title }}</div>
<el-button v-if="showBtn" type="primary" @click="router.go(-1)">
<!-- <el-button v-if="showBtn" type="primary" @click="router.go(-1)">
{{ second }} 秒后返回上一页
</el-button>
</el-button> -->
</div>
</div>
</template>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment