This commit is contained in:
陈红丽 2024-07-09 15:59:04 +08:00
parent 8c8b5a01c3
commit 31f3d32e77
8 changed files with 578 additions and 55 deletions

59
src/api/system/dept.ts Normal file
View File

@ -0,0 +1,59 @@
import { http } from '@/utils/http/axios';
/**
* @description:
*/
export function getDeptList(params?) {
return http.request({
url: '/dept/list',
method: 'GET',
params,
});
}
/**
* @description: ID获取详情
*/
export function getDeptDetail(userId) {
return http.request({
url: '/dept/detail/'+userId,
method: 'get',
});
}
/**
* @description:
*/
export function deptAdd(data:any) {
return http.request({
url: '/dept/add',
method: 'POST',
data,
});
}
/**
* @description:
*/
export function deptpdate(data:any) {
return http.request({
url: '/dept/update',
method: 'PUT',
data
});
}
/**
* @description:
*/
export function deptDelete(userId) {
return http.request({
url: '/dept/delete/'+userId,
method: 'DELETE',
});
}
/**
* @description:
*/
export function deptBatchDelete(userId) {
return http.request({
url: '/dept/batchDelete/'+userId,
method: 'DELETE',
});
}

66
src/api/system/level.ts Normal file
View File

@ -0,0 +1,66 @@
import { http } from '@/utils/http/axios';
/**
* @description:
*/
export function getLevelList(params?) {
return http.request({
url: '/level/page',
method: 'GET',
params,
});
}
export function getLevelAllList(params?) {
return http.request({
url: '/level/list',
method: 'GET',
params,
});
}
/**
* @description: ID获取详情
*/
export function getLevelDetail(userId) {
return http.request({
url: '/level/detail/'+userId,
method: 'get',
});
}
/**
* @description:
*/
export function levelAdd(data:any) {
return http.request({
url: '/level/add',
method: 'POST',
data,
});
}
/**
* @description:
*/
export function levelUpdate(data:any) {
return http.request({
url: '/level/update',
method: 'PUT',
data
});
}
/**
* @description:
*/
export function levelDelete(userId) {
return http.request({
url: '/level/delete/'+userId,
method: 'DELETE',
});
}
/**
* @description:
*/
export function levelBatchDelete(userId) {
return http.request({
url: '/level/batchDelete/'+userId,
method: 'DELETE',
});
}

View File

@ -0,0 +1,66 @@
import { http } from '@/utils/http/axios';
/**
* @description:
*/
export function getPositionList(params?) {
return http.request({
url: '/position/page',
method: 'GET',
params,
});
}
export function getPositionAllList(params?) {
return http.request({
url: '/position/list',
method: 'GET',
params,
});
}
/**
* @description: ID获取详情
*/
export function getPositionDetail(userId) {
return http.request({
url: '/position/detail/'+userId,
method: 'get',
});
}
/**
* @description:
*/
export function positionAdd(data:any) {
return http.request({
url: '/position/add',
method: 'POST',
data,
});
}
/**
* @description:
*/
export function positionUpdate(data:any) {
return http.request({
url: '/position/update',
method: 'PUT',
data
});
}
/**
* @description:
*/
export function positionDelete(userId) {
return http.request({
url: '/position/delete/'+userId,
method: 'DELETE',
});
}
/**
* @description:
*/
export function positionBatchDelete(userId) {
return http.request({
url: '/role/batchDelete/'+userId,
method: 'DELETE',
});
}

View File

@ -133,4 +133,26 @@ export function userBatchDelete(userId) {
url: '/user/batchDelete/'+userId, url: '/user/batchDelete/'+userId,
method: 'DELETE', method: 'DELETE',
}); });
}
/**
* @description:
*/
export function userExport() {
return http.request({
url: '/user/export',
method: 'GET',
responseType: 'blob'
});
}
/**
* @description:
*/
export function getCityByList(params:any) {
return http.request({
url: '/city/list',
method: 'get',
params:{
...params
}
});
} }

View File

@ -0,0 +1,96 @@
<template>
<el-cascader :filterable="false" :props="cascaderProps" clearable :disabled="disabled" v-model="selectedOptions" @change="handleChange"/>
</template>
<script setup lang="ts" name="china-area">
import { ref,onMounted,computed } from 'vue'
import type { CascaderProps } from 'element-plus'
import {getCityByList} from "@/api/system/user";
const emit = defineEmits(['update:modelValue', 'change']);
const optionsData = ref();
const mapData=ref({})
const props = defineProps({
//
modelValue: [String,Array],
// ()
type: {
type: Number,
default: 3,
},
//
disabled: {
type: Boolean,
default: () => false,
},
});
const cascaderProps: CascaderProps = {
value: 'areaCode',
label: 'name',
leaf: 'hasChild',
lazy: true,
lazyLoad: function (node, resolve) {
let level = node.level
let value = node.value
getCityList(value ? value : 0, level, resolve)
}
}
const getCityList= (pid:any, level:any, resolve:any)=> {
getCityByList({pid}).then(data => {
for (let i = 0; i < data.length; i++) {
data[i].areaCode = parseInt(data[i].areaCode)
data[i].hasChild = level >= props.type-1 ? true : false
}
mapData.value[pid] = data
resolve(data ? data : [])
}).catch(e => {
resolve([])
});
}
const selectedOptions = computed({
get: () => {
return props.modelValue;
},
set: (val) => {
emit('update:modelValue', val);
},
});
const findListChild= (list:any, value:any)=>{
let child = null
for (let i = 0; i < list.length; i++) {
if (list[i].areaCode == value) {
child = list[i]
break;
}
}
return child
}
const handleChange = (value: String[]) => {
let receiveText = ''
if (value&&value.length>0) {
if (mapData.value[0]) {
for (let i = 0; i < value.length; i++) {
if (i === 0) {
let child = findListChild(mapData.value[0], value[i])
receiveText += child ? child.name : ''
} else {
let child = findListChild(mapData.value[value[i - 1]], value[i])
receiveText += child ? child.name : ''
}
}
}
}
emit('change', receiveText);
};
onMounted(() => {
});
</script>
<style lang="scss">
.el-cascader-menu{
background-color: #ffffff;
}
</style>

View File

@ -41,7 +41,6 @@ const transform: AxiosTransform = {
isTransformResponse, isTransformResponse,
isReturnNativeResponse, isReturnNativeResponse,
} = options; } = options;
// 是否返回原生响应头 比如:需要获取响应头时使用该属性 // 是否返回原生响应头 比如:需要获取响应头时使用该属性
if (isReturnNativeResponse) { if (isReturnNativeResponse) {
return res; return res;
@ -52,11 +51,13 @@ const transform: AxiosTransform = {
return res.data; return res.data;
} }
if (!res.data) { if (!res.data) {
// return '[HTTP] Request has no return value'; // return '[HTTP] Request has no return value';
throw new Error('请求出错,请稍候重试'); throw new Error('请求出错,请稍候重试');
} }
if(res.config.responseType=="blob"){
return res.data
}
// 这里 coderesultmessage为 后台统一的字段,需要修改为项目自己的接口返回格式 // 这里 coderesultmessage为 后台统一的字段,需要修改为项目自己的接口返回格式
const { code, data, msg } = res.data; const { code, data, msg } = res.data;
// 请求成功 // 请求成功

View File

@ -2,7 +2,7 @@
<el-dialog <el-dialog
v-model="props.visible" v-model="props.visible"
:title="props.userId?'编辑':'新增'" :title="props.userId?'编辑':'新增'"
width="500" width="700"
:close-on-click-modal="false" :close-on-click-modal="false"
:before-close="dialogClose" :before-close="dialogClose"
> >
@ -11,9 +11,11 @@
:model="formData" :model="formData"
label-width="84px" label-width="84px"
> >
<div class="flex">
<el-form-item <el-form-item
label="账号" label="账号"
prop="username" prop="username"
class="flex-1"
:rules="{ required: true, message: '请输入账号', trigger: 'blur' }" :rules="{ required: true, message: '请输入账号', trigger: 'blur' }"
> >
<el-input <el-input
@ -25,29 +27,31 @@
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="名称" label="名称"
prop="nickname" prop="realname"
class="flex-1"
:rules="{ required: true, message: '请输入名称', trigger: 'blur' }" :rules="{ required: true, message: '请输入名称', trigger: 'blur' }"
> >
<el-input <el-input
v-model="formData.nickname" v-model="formData.realname"
placeholder="请输入名称" placeholder="请输入名称"
clearable clearable
/> />
</el-form-item> </el-form-item>
<el-form-item </div>
label="邮箱地址" <div class="flex">
prop="email" <el-form-item label="性别" prop="sex" class="flex-1">
:rules="{ required: true, message: '请输入邮箱地址', trigger: 'blur' }" <el-radio-group v-model="formData.gender" name="gender">
> <el-space>
<el-input <el-radio :value="1"></el-radio>
v-model="formData.email" <el-radio :value="2"></el-radio>
placeholder="请输入邮箱地址" <el-radio :value="3">保密</el-radio>
clearable </el-space>
/> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="角色" label="角色"
prop="roleIds" prop="roleIds"
class="flex-1"
:rules="{ required: true, message: '请选择角色', trigger: 'change' }" :rules="{ required: true, message: '请选择角色', trigger: 'change' }"
> >
<el-select <el-select
@ -60,16 +64,129 @@
> >
<el-option v-if="isRoot" label="系统管理员" value="0" /> <el-option v-if="isRoot" label="系统管理员" value="0" />
<el-option <el-option
v-for="(item, index) in optionData.role" v-for="(item, index) in optionData.roleList"
:key="index"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</div>
<div class="flex">
<el-form-item
label="手机号码"
prop="mobile"
class="flex-1"
:rules="{ required: true, message: '请输入手机号码', trigger: 'blur' }"
>
<el-input
v-model="formData.mobile"
placeholder="请输入手机号码"
clearable
/>
</el-form-item>
<el-form-item
label="邮箱地址"
prop="email"
class="flex-1"
:rules="{ required: true, message: '请输入邮箱地址', trigger: 'blur' }"
>
<el-input
v-model="formData.email"
placeholder="请输入邮箱地址"
clearable
/>
</el-form-item>
</div>
<div class="flex">
<el-form-item
label="部门"
prop="deptId"
class="flex-1"
>
<!-- <el-tree-select
v-model="formData.deptId"
:data="optionData.deptList"
:render-after-expand="false"
/> -->
<el-select
v-model="formData.deptId"
multiple
class="flex-1"
clearable
placeholder="请选择角色"
>
<el-option
v-for="(item, index) in optionData.deptList"
:key="index" :key="index"
:label="item.name" :label="item.name"
:value="item.id" :value="item.id"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item
label="职级"
prop="levelId"
class="flex-1"
>
<el-select
v-model="formData.levelId"
multiple
class="flex-1"
clearable
placeholder="请选择角色"
>
<el-option
v-for="(item, index) in optionData.levelList"
:key="index"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</div>
<div class="flex">
<el-form-item
label="岗位"
prop="positionId"
class="flex-1"
>
<el-select
v-model="formData.positionId"
multiple
class="flex-1"
clearable
placeholder="请选择角色"
>
<el-option
v-for="(item, index) in optionData.positionList"
:key="index"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</div>
<div class="flex">
<el-form-item label="所属区域" class="flex-1">
<chinaArea style="width: 100%" :type="4" v-model="formData.cityCode"></chinaArea>
</el-form-item>
<el-form-item label="详细地址" class="flex-1">
<el-input
v-model="formData.address"
placeholder="请输入详细地址"
clearable
/>
</el-form-item>
</div>
<div class="flex">
<el-form-item <el-form-item
label="密码" label="密码"
prop="password" prop="password"
class="flex-1"
:rules="{ required: props.userId?false:true, message: '请输入密码', trigger: 'blur' }" :rules="{ required: props.userId?false:true, message: '请输入密码', trigger: 'blur' }"
> >
<el-input <el-input
@ -84,6 +201,7 @@
<el-form-item <el-form-item
label="确认密码" label="确认密码"
prop="passwordConfirm" prop="passwordConfirm"
class="flex-1"
:rules="[{ required: props.userId?false:true, message: '请输入密码', trigger: 'blur' },{validator: props.userId?void(0):passwordConfirmValidator,trigger: 'blur'}]" :rules="[{ required: props.userId?false:true, message: '请输入密码', trigger: 'blur' },{validator: props.userId?void(0):passwordConfirmValidator,trigger: 'blur'}]"
> >
<el-input <el-input
@ -94,7 +212,8 @@
placeholder="请输入确认密码" placeholder="请输入确认密码"
/> />
</el-form-item> </el-form-item>
</div>
<div class="flex">
<el-form-item label="管理员状态" v-if="!isRoot"> <el-form-item label="管理员状态" v-if="!isRoot">
<el-switch <el-switch
v-model="formData.userStatus" v-model="formData.userStatus"
@ -102,7 +221,18 @@
inactive-value="disable" inactive-value="disable"
/> />
</el-form-item> </el-form-item>
</div>
<div class="flex"></div>
<el-form-item label="头像" prop="images">
<BasicUpload
:action="`${uploadUrl}/api/upload/uploadFile`"
v-model:list="fileList"
:headers="uploadHeaders"
:data="{ name: 'avatar' }"
:limit="1"
@upload-change="uploadChange"
/>
</el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
@ -117,12 +247,22 @@
<script lang="ts" setup> <script lang="ts" setup>
import {getUserDetail,userAdd,userUpdate} from '@/api/system/user'; import {getUserDetail,userAdd,userUpdate} from '@/api/system/user';
import {computed, onMounted, reactive, shallowRef} from "vue"; import { BasicUpload } from '@/components/Upload';
import chinaArea from '@/components/ChinaArea/index.vue';
import {computed, onMounted, reactive, shallowRef,ref,unref} from "vue";
import { getRoleAllList } from '@/api/system/role';
import { getDeptList } from '@/api/system/dept';
import { getLevelAllList } from '@/api/system/level';
import { getPositionAllList } from '@/api/system/position';
import {message} from "@/utils/auth"; import {message} from "@/utils/auth";
import {FormInstance} from "element-plus"; import {FormInstance} from "element-plus";
const formRef = shallowRef<FormInstance>(); const formRef = shallowRef<FormInstance>();
import {useLockFn} from "@/utils/useLockFn"; import {useLockFn} from "@/utils/useLockFn";
import { useGlobSetting } from '@/hooks/setting';
import { useUserStore } from '@/store/modules/user';
const globSetting = useGlobSetting();
const { uploadUrl } = globSetting;
const props = defineProps({ const props = defineProps({
visible: { visible: {
type: Boolean, type: Boolean,
@ -133,27 +273,32 @@ const props = defineProps({
type: Number, type: Number,
required: true, required: true,
default: 0 default: 0
},
roleList: {
type: Array,
default: []
} }
}); });
const emit = defineEmits(["success", "update:visible"]); const emit = defineEmits(["success", "update:visible"]);
const uploadHeaders = reactive({
authorization:useUserStore().getToken
});
const formData = reactive({ const formData = reactive({
userId: 0, userId: 0,
email:'', email:'',
username: "", username: "",
nickname: "", realname: "",
mobile:'',
gender:1,
roleIds: [], roleIds: [],
deptId:'',
levelId:'',
positionId:'',
address:'',
cityCode:'',
avatar: "", avatar: "",
password: "", password: "",
passwordConfirm: "", passwordConfirm: "",
userStatus: 'enable' userStatus: 'enable'
}); });
const fileList = ref([]);
const passwordConfirmValidator = ( const passwordConfirmValidator = (
rule: object, rule: object,
value: string, value: string,
@ -193,11 +338,29 @@ const handleSubmit = async () => {
const { isLock:subLoading,lockFn: submit } = useLockFn(handleSubmit); const { isLock:subLoading,lockFn: submit } = useLockFn(handleSubmit);
const optionData = reactive({ const optionData = reactive({
role:props.roleList roleList:[],
deptList:[],
levelList:[],
positionList:[]
}); });
function uploadChange(list: string[]) {
console.log('🚀 ~ file: index.vue ~ line 118 ~ uploadChange ~ list', list);
formData.avatar = unref(list);
}
const getAllDict = async () => {
let list = await getRoleAllList();
optionData.roleList = list ? list : [];
list = await getDeptList();
optionData.deptList = list ? list : [];
list = await getLevelAllList();
optionData.levelList = list ? list : [];
list = await getPositionAllList();
optionData.positionList = list ? list : [];
};
onMounted(() => { onMounted(() => {
getAllDict()
if (props.userId) { if (props.userId) {
setFormData({userId: props.userId}); setFormData({userId: props.userId});
} }

View File

@ -37,6 +37,32 @@
</template> </template>
删除 删除
</el-button> </el-button>
<el-upload
ref="upload"
action="/api/user/import"
:headers="uploadHeaders"
:on-error="onError"
:on-success="onSuccess"
:before-upload="beforeUpload"
:limit="1"
>
<el-button type="danger">
<template #icon>
<el-icon class="el-input__icon">
<Upload />
</el-icon>
</template>
导入
</el-button>
</el-upload>
<el-button type="danger" @click="handleExport" :loading="exportLoading" :disabled="exportLoading">
<template #icon>
<el-icon class="el-input__icon">
<Download />
</el-icon>
</template>
导出
</el-button>
</el-space> </el-space>
</template> </template>
</BasicTable> </BasicTable>
@ -44,7 +70,6 @@
<editDialog <editDialog
v-if="editVisible" v-if="editVisible"
:userId="userId" :userId="userId"
:roleList="roleList"
v-model:visible="editVisible" v-model:visible="editVisible"
@success="reloadTable" @success="reloadTable"
> >
@ -54,23 +79,33 @@
<script lang="ts" setup> <script lang="ts" setup>
import { h, nextTick, reactive, ref, unref ,defineAsyncComponent,onMounted} from 'vue'; import { h, nextTick, reactive, ref, unref ,defineAsyncComponent,onMounted} from 'vue';
import { ColProps, ElMessage } from 'element-plus'; import { ColProps, ElMessage, UploadInstance } from 'element-plus';
import { BasicTable, TableAction } from '@/components/Table'; import { BasicTable, TableAction } from '@/components/Table';
import { BasicForm, useForm } from '@/components/Form/index'; import { BasicForm, useForm } from '@/components/Form/index';
import { getUserList,userDelete,userBatchDelete } from '@/api/system/user'; import { getUserList,userDelete,userBatchDelete,userExport } from '@/api/system/user';
import {message,confirm} from "@/utils/auth"; import {message,confirm,loading, closeLoading} from "@/utils/auth";
import { getRoleAllList } from '@/api/system/role';
import { columns } from './columns'; import { columns } from './columns';
import { schemas } from './querySchemas'; import { schemas } from './querySchemas';
import { useUserStore } from '@/store/modules/user';
import {
downloadByUrl,
downloadByData,
downloadByBase64,
downloadByOnlineUrl,
} from '@/utils/file/download';
const userId = ref(0); const userId = ref(0);
const basicTableRef = ref(); const basicTableRef = ref();
const editVisible = ref(false) const editVisible = ref(false)
const roleList = ref([])
const selectionData = ref([]) const selectionData = ref([])
const tableData = ref(); const tableData = ref();
const upload = ref<UploadInstance>();
const exportLoading=ref(false)
const editDialog = defineAsyncComponent(() => const editDialog = defineAsyncComponent(() =>
import('./edit.vue') import('./edit.vue')
) )
const uploadHeaders = reactive({
authorization:useUserStore().getToken
});
const formParams = reactive({ const formParams = reactive({
username: '', username: '',
mobile: '', mobile: '',
@ -169,32 +204,47 @@
submitOnReset:true, submitOnReset:true,
schemas schemas
}); });
//
function openRemoveModal() {
lightOpenModal();
}
// //
function addUser() { function addUser() {
userId.value =0 userId.value =0
editVisible.value = true editVisible.value = true
} }
const beforeUpload = (file: UploadFile) => {
// const isLt2M = file.size / 1024 / 1024 < 200;
function removeOkModal() { if (!isLt2M) {
lightSetSubLoading(true); message("大小不能超过200MB!", "error");
lightCloseModal(); return false;
reloadTable()
} }
const getAllDict = async () => { if (!/\.(xlsx|xls|XLSX|XLS)$/.test(file.name)) {
const list = await getRoleAllList(); message("请上传.xlsx .xls", "error");
roleList.value = list ? list : []; return false;
}; }
onMounted(() => { loading("上传中");
getAllDict() return true;
}); };
const onSuccess = (file: UploadFile) => {
upload.value!.clearFiles();
closeLoading();
if (file.code == 200) {
message("导入成功");
reloadTable()
} else {
message(file.msg ? file.msg : "导入失败", "error");
}
};
const onError = () => {
upload.value!.clearFiles();
closeLoading();
message("导入失败", "error");
};
//
const handleExport = async()=>{
exportLoading.value=true
const data=await userExport()
downloadByData(data, '用户信息.xlsx');
message("导出成功");
}
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>