用户管理修改
This commit is contained in:
parent
1c23d04690
commit
e8672104a4
33
src/App.vue
33
src/App.vue
|
@ -1,25 +1,16 @@
|
|||
<template>
|
||||
<el-config-provider :locale="currentLocale">
|
||||
<router-view />
|
||||
<ReDialog />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { ElConfigProvider } from "element-plus";
|
||||
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
||||
import { ReDialog } from "@/components/ReDialog";
|
||||
export default defineComponent({
|
||||
name: "app",
|
||||
components: {
|
||||
[ElConfigProvider.name]: ElConfigProvider,
|
||||
ReDialog
|
||||
},
|
||||
computed: {
|
||||
currentLocale() {
|
||||
return zhCn;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const currentLocale = computed(() => zhCn);
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElConfigProvider :locale="currentLocale">
|
||||
<router-view />
|
||||
<ReDialog />
|
||||
</ElConfigProvider>
|
||||
</template>
|
||||
|
|
|
@ -23,6 +23,10 @@ export type LoginByPasswordDTO = {
|
|||
captchaCode: string;
|
||||
/** 验证码对应的缓存key */
|
||||
captchaCodeKey: string;
|
||||
/** 企业微信 */
|
||||
corpid: string;
|
||||
code: string;
|
||||
state: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -54,7 +58,7 @@ export interface CurrentUserInfoDTO {
|
|||
email?: string;
|
||||
loginDate?: Date;
|
||||
loginIp?: string;
|
||||
nickName?: string;
|
||||
nickname?: string;
|
||||
phoneNumber?: string;
|
||||
postId?: number;
|
||||
postName?: string;
|
||||
|
@ -106,3 +110,15 @@ type Result = {
|
|||
export const getAsyncRoutes = () => {
|
||||
return http.request<Result>("get", "/getRouters");
|
||||
};
|
||||
|
||||
type qyUserinfoQuery = {
|
||||
// 授权企业id
|
||||
corpid: string;
|
||||
// 企业微信 code
|
||||
code: string;
|
||||
}
|
||||
|
||||
/** 获取企业微信访问用户身份接口 */
|
||||
export const getQyUserinfo = (params: qyUserinfoQuery) => {
|
||||
return http.request<ResponseData<string>>("get", "/getQyUserinfo", { params });
|
||||
};
|
|
@ -0,0 +1,112 @@
|
|||
import { http } from "@/utils/http";
|
||||
|
||||
/**
|
||||
* 企业微信用户信息
|
||||
*/
|
||||
export interface QyUserDTO {
|
||||
/** 用户ID(导出列:用户ID) */
|
||||
id?: number;
|
||||
/** 全局唯一ID(导出列:全局唯一ID) */
|
||||
openUserid?: string;
|
||||
/** 企业用户ID(导出列:企业用户ID) */
|
||||
userid?: string;
|
||||
/** 用户姓名(导出列:用户姓名) */
|
||||
name?: string;
|
||||
/** 手机号码(导出列:手机号码) */
|
||||
mobile?: string;
|
||||
/** 所属部门(导出列:所属部门) */
|
||||
department?: string;
|
||||
/** 部门排序(导出列:部门排序) */
|
||||
userOrder?: string;
|
||||
/** 职务信息(导出列:职务信息) */
|
||||
position?: string;
|
||||
/** 性别(1男 2女,导出列:性别) */
|
||||
gender?: string;
|
||||
/** 邮箱(导出列:邮箱) */
|
||||
email?: string;
|
||||
/** 企业邮箱(导出列:企业邮箱) */
|
||||
bizMail?: string;
|
||||
/** 部门负责人(导出列:部门负责人) */
|
||||
isLeaderInDept?: string;
|
||||
/** 直属上级(导出列:直属上级) */
|
||||
directLeader?: string;
|
||||
/** 头像地址(导出列:头像地址) */
|
||||
avatar?: string;
|
||||
/** 座机号码(导出列:座机号码) */
|
||||
telephone?: string;
|
||||
/** 别名(导出列:别名) */
|
||||
alias?: string;
|
||||
/** 激活状态(导出列:激活状态) */
|
||||
status?: string;
|
||||
/** 个人二维码(导出列:个人二维码) */
|
||||
qrCode?: string;
|
||||
/** 操作人(导出列:操作人) */
|
||||
operator?: string;
|
||||
/** 有效状态(导出列:有效状态) */
|
||||
enableStatus?: string;
|
||||
/** 创建时间(导出列:创建时间) */
|
||||
createTimeStr?: string;
|
||||
/** 企业ID(导出列:企业ID) */
|
||||
corpid?: string;
|
||||
/** 应用ID(导出列:应用ID) */
|
||||
appid?: string;
|
||||
}
|
||||
|
||||
export interface QyUserQuery extends BasePageQuery {
|
||||
/** 姓名(导出列:姓名) */
|
||||
name?: string;
|
||||
/** 手机号(导出列:联系方式) */
|
||||
mobile?: string;
|
||||
corpid?: string;
|
||||
mainDepartment?: number;
|
||||
}
|
||||
|
||||
export interface AddQyUserCommand {
|
||||
name: string;
|
||||
mobile: string;
|
||||
department: string;
|
||||
corpid: string;
|
||||
}
|
||||
|
||||
export interface UpdateQyUserCommand extends AddQyUserCommand {
|
||||
id: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取企业微信用户列表
|
||||
* @param params 查询参数
|
||||
* @returns 分页用户列表
|
||||
*/
|
||||
export const getQyUserListApi = (params: QyUserQuery) => {
|
||||
return http.request<ResponseData<PageDTO<QyUserDTO>>>("get", "/qywx/users", {
|
||||
params: { ...params, mainDepartment: params.mainDepartment?.toString() }
|
||||
});
|
||||
};
|
||||
|
||||
export const addQyUserApi = (data: AddQyUserCommand) => {
|
||||
return http.request<ResponseData<void>>("post", "/qywx/users", { data });
|
||||
};
|
||||
|
||||
export const updateQyUserApi = (id: number, data: UpdateQyUserCommand) => {
|
||||
return http.request<ResponseData<void>>("put", `/qywx/users/${id}`, { data });
|
||||
};
|
||||
|
||||
export const deleteQyUserApi = (ids: number[]) => {
|
||||
return http.request<ResponseData<void>>("delete", `/qywx/users/${ids.join(',')}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 同步企业微信用户数据
|
||||
* @param params 同步参数
|
||||
* @param params.corpid 企业ID
|
||||
* @param params.code 授权code
|
||||
*/
|
||||
export const syncQyUserApi = (params: { corpid: string; code: string }) => {
|
||||
return http.request<ResponseData<void>>("post", "/qywx/users/sync", {
|
||||
params
|
||||
});
|
||||
};
|
||||
|
||||
export const exportQyUserExcelApi = (params: QyUserQuery, fileName: string) => {
|
||||
return http.download("/qywx/users/excel", fileName, { params });
|
||||
};
|
|
@ -48,6 +48,11 @@ export const getDeptListApi = (params?: DeptQuery) => {
|
|||
params
|
||||
});
|
||||
};
|
||||
export const getQyDeptListApi = (corpid: string) => {
|
||||
return http.request<ResponseData<Array<DeptDTO>>>("get", "/qywx/departments/depts", {
|
||||
params: { corpid }
|
||||
});
|
||||
};
|
||||
|
||||
/** 新增部门 */
|
||||
export const addDeptApi = (data: DeptRequest) => {
|
||||
|
|
|
@ -6,6 +6,7 @@ export interface UserQuery extends BasePageQuery {
|
|||
status?: number;
|
||||
userId?: number;
|
||||
username?: string;
|
||||
nickname?: string,
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,16 +7,27 @@
|
|||
<div v-if="showQr" class="qr-popover">
|
||||
<ReQrcode text="http://wxshop.ab98.cn/shop-api/api/shop/wechatAuth" :options="{ width: 200 }" />
|
||||
<div class="qr-tip">微信扫码访问</div>
|
||||
<el-button class="copy-btn" type="primary" size="small" @click="copyLink">
|
||||
复制链接
|
||||
</el-button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import ReQrcode from '@/components/ReQrcode'
|
||||
import Iphone from "@iconify-icons/ep/iphone";
|
||||
import { copyTextToClipboard } from "@pureadmin/utils";
|
||||
|
||||
const showQr = ref(false)
|
||||
|
||||
const copyLink = () => {
|
||||
const success = copyTextToClipboard('http://wxshop.ab98.cn/shop-api/api/shop/wechatAuth');
|
||||
success ? ElMessage.success('链接复制成功') : ElMessage.error('复制失败,请手动复制');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -32,6 +43,9 @@ const showQr = ref(false)
|
|||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.qr-tip {
|
||||
|
|
|
@ -17,6 +17,7 @@ const {
|
|||
onPanel,
|
||||
pureApp,
|
||||
username,
|
||||
nickname,
|
||||
userAvatar,
|
||||
avatarsStyle,
|
||||
toggleSideBar
|
||||
|
@ -43,7 +44,7 @@ const {
|
|||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link navbar-bg-hover select-none">
|
||||
<img :src="userAvatar" :style="avatarsStyle" />
|
||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||
<p v-if="nickname" class="dark:text-white">{{ nickname }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
|
|
|
@ -19,6 +19,7 @@ const {
|
|||
backTopMenu,
|
||||
onPanel,
|
||||
username,
|
||||
nickname,
|
||||
userAvatar,
|
||||
avatarsStyle
|
||||
} = useNav();
|
||||
|
@ -53,7 +54,7 @@ nextTick(() => {
|
|||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link navbar-bg-hover">
|
||||
<img :src="userAvatar" :style="avatarsStyle" />
|
||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||
<p v-if="nickname" class="dark:text-white">{{ nickname }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
|
|
|
@ -22,6 +22,7 @@ const {
|
|||
onPanel,
|
||||
resolvePath,
|
||||
username,
|
||||
nickname,
|
||||
userAvatar,
|
||||
getDivStyle,
|
||||
avatarsStyle
|
||||
|
@ -82,7 +83,7 @@ watch(
|
|||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link navbar-bg-hover select-none">
|
||||
<img :src="userAvatar" :style="avatarsStyle" />
|
||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||
<p v-if="nickname" class="dark:text-white">{{ nickname }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
|
|
|
@ -40,6 +40,10 @@ export function useNav() {
|
|||
return useUserStoreHook()?.username;
|
||||
});
|
||||
|
||||
const nickname = computed(() => {
|
||||
return useUserStoreHook()?.nickname;
|
||||
});
|
||||
|
||||
const avatarsStyle = computed(() => {
|
||||
return username.value ? { marginRight: "10px" } : "";
|
||||
});
|
||||
|
@ -140,6 +144,7 @@ export function useNav() {
|
|||
isCollapse,
|
||||
pureApp,
|
||||
username,
|
||||
nickname,
|
||||
userAvatar,
|
||||
avatarsStyle,
|
||||
tooltipEffect
|
||||
|
|
|
@ -38,6 +38,7 @@ export type setType = {
|
|||
|
||||
export type userType = {
|
||||
username?: string;
|
||||
nickname?: string;
|
||||
roles?: Array<string>;
|
||||
/** 字典ListMap 用于下拉框直接展示 */
|
||||
dictionaryList: Map<String, Array<DictionaryData>>;
|
||||
|
|
|
@ -19,6 +19,10 @@ export const useUserStore = defineStore({
|
|||
username:
|
||||
storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.userInfo
|
||||
.username ?? "",
|
||||
// 昵称
|
||||
nickname:
|
||||
storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.userInfo
|
||||
.nickname ?? "",
|
||||
// 页面级别权限
|
||||
roles: storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.roleKey
|
||||
? [storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.roleKey]
|
||||
|
@ -40,6 +44,11 @@ export const useUserStore = defineStore({
|
|||
/** TODO 这里不是应该再进一步存到sessionStorage中吗 */
|
||||
this.username = username;
|
||||
},
|
||||
/** 存储昵称 */
|
||||
SET_NICKNAME(nickname: string) {
|
||||
/** TODO 这里不是应该再进一步存到sessionStorage中吗 */
|
||||
this.nickname = nickname;
|
||||
},
|
||||
/** 存储角色 */
|
||||
SET_ROLES(roles: Array<string>) {
|
||||
this.roles = roles;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import { store } from "@/store";
|
||||
|
||||
|
||||
export const useWxStore = defineStore("wx", () => {
|
||||
// 授权企业id
|
||||
const corpid = ref<string>("")
|
||||
// 微信授权 code
|
||||
const code = ref<string>("")
|
||||
// 防止 CSRF 攻击的 state 参数
|
||||
const state = ref<string>("")
|
||||
// 用户 userid
|
||||
const userid = ref<string>("")
|
||||
|
||||
// 设置 userid
|
||||
const setUserid = (id: string) => {
|
||||
userid.value = id
|
||||
}
|
||||
|
||||
const handleWxCallback = async (params: { corpid: string; code: string; state: string }) => {
|
||||
console.log('handleWxCallback:', params)
|
||||
if (params.code && params.corpid) {
|
||||
corpid.value = params.corpid;
|
||||
code.value = params.code;
|
||||
state.value = params.state || state.value;
|
||||
|
||||
// try {
|
||||
// // 调用获取 userid 的接口
|
||||
// const res = await getQyUserinfo({
|
||||
// corpid: params.corpid,
|
||||
// code: params.code
|
||||
// })
|
||||
// console.log('获取 userid 成功:', res)
|
||||
// if (res && res.code == 0) {
|
||||
// userid.value = res.data
|
||||
// }
|
||||
// } catch (err) {
|
||||
// console.error('获取 userid 失败:', err)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
return { corpid, code, state, userid, setUserid, handleWxCallback }
|
||||
})
|
||||
|
||||
/**
|
||||
* @description 用于在 setup 外使用 store
|
||||
*/
|
||||
export function useWxStoreOutside() {
|
||||
return useWxStore(store)
|
||||
}
|
|
@ -42,6 +42,7 @@ export function setTokenFromBackend(data: TokenDTO): void {
|
|||
|
||||
useUserStoreHook().SET_USERNAME(data.currentUser.userInfo.username);
|
||||
useUserStoreHook().SET_ROLES([data.currentUser.roleKey]);
|
||||
useUserStoreHook().SET_NICKNAME(data.currentUser.userInfo.nickname);
|
||||
storageSession().setItem(sessionKey, data);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRoute } from "vue-router";
|
||||
import { onBeforeRouteUpdate, useRoute } from "vue-router";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { getCabinetCellList, deleteCabinetCell, CabinetCellDTO } from "@/api/cabinet/cabinet-cell";
|
||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||
|
@ -12,6 +12,7 @@ import Refresh from "@iconify-icons/ep/refresh";
|
|||
import CellFormModal from "./cell-form-modal.vue";
|
||||
import CellEditModal from "./cell-edit-modal.vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { on } from "events";
|
||||
|
||||
defineOptions({
|
||||
name: "CabinetCell"
|
||||
|
@ -47,6 +48,12 @@ onMounted(() => {
|
|||
getList();
|
||||
}
|
||||
});
|
||||
onBeforeRouteUpdate(() => {
|
||||
if (route.query.cabinetId) {
|
||||
searchFormParams.value.cabinetId = Number(route.query.cabinetId);
|
||||
getList();
|
||||
}
|
||||
});
|
||||
|
||||
const getList = async () => {
|
||||
try {
|
||||
|
@ -136,7 +143,6 @@ const switchCellType = (cellType: number) => {
|
|||
}
|
||||
};
|
||||
|
||||
getList();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -41,6 +41,7 @@ import Lock from "@iconify-icons/ri/lock-fill";
|
|||
import User from "@iconify-icons/ri/user-3-fill";
|
||||
import * as CommonAPI from "@/api/common/login";
|
||||
import { useUserStoreHook } from "@/store/modules/user";
|
||||
import { useWxStore } from "@/store/modules/wx";
|
||||
|
||||
defineOptions({
|
||||
name: "Login"
|
||||
|
@ -58,6 +59,8 @@ const ruleFormRef = ref<FormInstance>();
|
|||
// 判断登录页面显示哪个组件(0:登录(默认)、1:手机登录、2:二维码登录、3:注册、4:忘记密码)
|
||||
const currentPage = ref(0);
|
||||
|
||||
const wxStore = useWxStore();
|
||||
|
||||
const { initStorage } = useLayout();
|
||||
initStorage();
|
||||
const { dataTheme, dataThemeChange } = useDataThemeChange();
|
||||
|
@ -65,23 +68,66 @@ dataThemeChange();
|
|||
// const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
|
||||
const { title } = useNav();
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
console.log('urlParams', urlParams);
|
||||
const corpid = urlParams.get('corpid') || undefined;
|
||||
const code = urlParams.get('code') || undefined;
|
||||
const state = urlParams.get('state') || undefined;
|
||||
if (code && corpid) {
|
||||
wxStore.handleWxCallback({ corpid, code, state })
|
||||
}
|
||||
|
||||
const ruleForm = reactive({
|
||||
username: "admin",
|
||||
password: getPassword(),
|
||||
captchaCode: "",
|
||||
captchaCodeKey: ""
|
||||
captchaCodeKey: "",
|
||||
corpid: wxStore.corpid,
|
||||
code: wxStore.code,
|
||||
state: wxStore.state
|
||||
});
|
||||
|
||||
const onLogin = async (formEl: FormInstance | undefined) => {
|
||||
loading.value = true;
|
||||
if (!formEl) return;
|
||||
if (code && corpid) {
|
||||
CommonAPI.loginByPassword({
|
||||
username: ruleForm.username,
|
||||
password: ruleForm.password ? rsaEncrypt(ruleForm.password) : '',
|
||||
captchaCode: ruleForm.captchaCode,
|
||||
captchaCodeKey: ruleForm.captchaCodeKey,
|
||||
corpid: ruleForm.corpid,
|
||||
code: ruleForm.code,
|
||||
state: ruleForm.state
|
||||
})
|
||||
.then(({ data }) => {
|
||||
// 登录成功后 将token存储到sessionStorage中
|
||||
setTokenFromBackend(data);
|
||||
// 获取后端路由
|
||||
initRouter().then(() => {
|
||||
router.push(getTopMenu(true).path);
|
||||
message("登录成功", { type: "success" });
|
||||
});
|
||||
if (isRememberMe.value) {
|
||||
savePassword(ruleForm.password);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
//如果登陆失败则重新获取验证码
|
||||
getCaptchaCode();
|
||||
});
|
||||
} else {
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
CommonAPI.loginByPassword({
|
||||
username: ruleForm.username,
|
||||
password: rsaEncrypt(ruleForm.password),
|
||||
password: ruleForm.password ? rsaEncrypt(ruleForm.password) : '',
|
||||
captchaCode: ruleForm.captchaCode,
|
||||
captchaCodeKey: ruleForm.captchaCodeKey
|
||||
captchaCodeKey: ruleForm.captchaCodeKey,
|
||||
corpid: ruleForm.corpid,
|
||||
code: ruleForm.code,
|
||||
state: ruleForm.state
|
||||
})
|
||||
.then(({ data }) => {
|
||||
// 登录成功后 将token存储到sessionStorage中
|
||||
|
@ -105,6 +151,7 @@ const onLogin = async (formEl: FormInstance | undefined) => {
|
|||
return fields;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** 使用公共函数,避免`removeEventListener`失效 */
|
||||
|
@ -147,8 +194,9 @@ onBeforeMount(async () => {
|
|||
onMounted(() => {
|
||||
window.document.addEventListener("keypress", onkeypress);
|
||||
// 临时登录
|
||||
ruleForm.password = "admin123"
|
||||
if (wxStore.code && wxStore.corpid) {
|
||||
onLogin(ruleFormRef.value);
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<template>
|
||||
<el-form ref="formRef" label-width="120px" :model="formInline" :disabled="true">
|
||||
<el-form-item label="用户ID">
|
||||
<el-input v-model="formInline.userId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="formInline.username" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户昵称">
|
||||
<el-input v-model="formInline.nickname" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="formInline.sex">
|
||||
<el-option label="男" :value="1" />
|
||||
<el-option label="女" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码">
|
||||
<el-input v-model="formInline.phoneNumber" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门">
|
||||
<el-input v-model="formInline.deptId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-switch v-model="formInline.status" :active-value="1" :inactive-value="0" active-text="启用"
|
||||
inactive-text="停用" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
defineProps({
|
||||
formInline: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
userId: null,
|
||||
username: "",
|
||||
nickname: "",
|
||||
sex: 1,
|
||||
phoneNumber: "",
|
||||
deptId: null,
|
||||
status: 1
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
</script>
|
|
@ -0,0 +1,114 @@
|
|||
import dayjs from "dayjs";
|
||||
import { message } from "@/utils/message";
|
||||
import {
|
||||
QyUserQuery,
|
||||
getQyUserListApi
|
||||
} from "@/api/qy/qyUser";
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import { type PaginationProps } from "@pureadmin/table";
|
||||
import { reactive, ref, computed, onMounted, toRaw, h } from "vue";
|
||||
import { CommonUtils } from "@/utils/common";
|
||||
import { addDialog } from "@/components/ReDialog";
|
||||
import userDetail from "./UserDetail.vue";
|
||||
import { handleTree, setDisabledForTreeOptions } from "@/utils/tree";
|
||||
import { getQyDeptListApi } from "@/api/system/dept";
|
||||
import { getPostListApi } from "@/api/system/post";
|
||||
import { getRoleListApi } from "@/api/system/role";
|
||||
import { useWxStore } from "@/store/modules/wx";
|
||||
|
||||
export function useHook() {
|
||||
const wxStore = useWxStore();
|
||||
|
||||
const searchFormParams = reactive<QyUserQuery>({
|
||||
/** 姓名(导出列:姓名) */
|
||||
name: undefined,
|
||||
/** 手机号(导出列:联系方式) */
|
||||
mobile: undefined,
|
||||
corpid: wxStore.corpid,
|
||||
mainDepartment: undefined,
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const timeRange = ref<[string, string]>();
|
||||
|
||||
const dataList = ref([]);
|
||||
const pageLoading = ref(true);
|
||||
const switchLoadMap = ref({});
|
||||
const pagination = reactive<PaginationProps>({
|
||||
total: 0,
|
||||
pageSize: 8,
|
||||
currentPage: 1,
|
||||
background: true
|
||||
});
|
||||
|
||||
const deptTreeList = ref([]);
|
||||
const postOptions = ref([]);
|
||||
const roleOptions = ref([]);
|
||||
const buttonClass = computed(() => {
|
||||
return [
|
||||
"!h-[20px]",
|
||||
"reset-margin",
|
||||
"!text-gray-500",
|
||||
"dark:!text-white",
|
||||
"dark:hover:!text-primary"
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
async function onSearch() {
|
||||
// 点击搜索的时候 需要重置分页
|
||||
pagination.currentPage = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
CommonUtils.fillPaginationParams(searchFormParams, pagination);
|
||||
CommonUtils.fillTimeRangeParams(searchFormParams, timeRange.value);
|
||||
|
||||
pageLoading.value = true;
|
||||
const { data } = await getQyUserListApi(toRaw(searchFormParams)).finally(
|
||||
() => {
|
||||
pageLoading.value = false;
|
||||
}
|
||||
);
|
||||
|
||||
dataList.value = data.rows;
|
||||
pagination.total = data.total;
|
||||
}
|
||||
|
||||
const resetForm = formEl => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
onSearch();
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
onSearch();
|
||||
const deptResponse = await getQyDeptListApi(wxStore.corpid);
|
||||
deptTreeList.value = await setDisabledForTreeOptions(
|
||||
handleTree(deptResponse.data),
|
||||
"status"
|
||||
);
|
||||
|
||||
const postResponse = await getPostListApi({});
|
||||
postOptions.value = postResponse.data.rows;
|
||||
|
||||
const roleResponse = await getRoleListApi({});
|
||||
roleOptions.value = roleResponse.data.rows;
|
||||
});
|
||||
|
||||
const handleViewDetail = (row: any) => {
|
||||
};
|
||||
|
||||
return {
|
||||
searchFormParams,
|
||||
pageLoading,
|
||||
dataList,
|
||||
pagination,
|
||||
buttonClass,
|
||||
onSearch,
|
||||
resetForm,
|
||||
getList,
|
||||
handleViewDetail
|
||||
};
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import tree from "./tree.vue";
|
||||
import { useHook } from "./hook";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
import Password from "@iconify-icons/ri/lock-password-line";
|
||||
import More from "@iconify-icons/ep/more-filled";
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||
import Download from "@iconify-icons/ep/download";
|
||||
import Upload from "@iconify-icons/ep/upload";
|
||||
import Search from "@iconify-icons/ep/search";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||
import { useUserStoreHook } from "@/store/modules/user";
|
||||
|
||||
defineOptions({
|
||||
name: "QyUser"
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const {
|
||||
searchFormParams,
|
||||
pageLoading,
|
||||
dataList,
|
||||
pagination,
|
||||
buttonClass,
|
||||
onSearch,
|
||||
resetForm,
|
||||
getList,
|
||||
handleViewDetail
|
||||
} = useHook();
|
||||
|
||||
watch(
|
||||
() => searchFormParams.mainDepartment,
|
||||
() => {
|
||||
onSearch();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<tree class="w-[17%] float-left" v-model="searchFormParams.mainDepartment" />
|
||||
<div class="float-right w-[82%]">
|
||||
<el-form ref="formRef" :inline="true" :model="searchFormParams"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]">
|
||||
<el-form-item label="姓名:" prop="name">
|
||||
<el-input v-model="searchFormParams.name" placeholder="请输入" clearable class="!w-[160px]" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :icon="useRenderIcon(Search)" :loading="pageLoading" @click="onSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="grid-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col v-for="(item, index) in dataList" :key="index" :xs="24" :sm="12" :md="8" :lg="6">
|
||||
<el-card class="user-card">
|
||||
<div class="card-content">
|
||||
<el-avatar :size="60" :src="item.avatar" class="avatar" />
|
||||
<div class="user-info">
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="gender">性别:{{ item.gender === '1' ? '男' : item.gender === '2' ? '女' : '' }}</div>
|
||||
<div class="create-time">创建时间:{{ item.createTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-button type="primary" size="small" class="detail-btn" @click="handleViewDetail(item)">
|
||||
查看详情
|
||||
</el-button>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="pagination-wrapper">
|
||||
<el-pagination background layout="prev, pager, next" :page-size="pagination.pageSize"
|
||||
:total="pagination.total" v-model:current-page="pagination.currentPage" @current-change="getList"
|
||||
@size-change="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.el-dropdown-menu__item i) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-card {
|
||||
margin-bottom: 20px;
|
||||
min-height: 180px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.avatar {
|
||||
margin-right: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
|
||||
.name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.gender,
|
||||
.create-time {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
width: 100%;
|
||||
margin-top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
margin: 20px 0;
|
||||
padding-bottom: 60px;
|
||||
position: relative;
|
||||
|
||||
.el-row {
|
||||
margin-bottom: -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--el-bg-color);
|
||||
padding: 12px 20px;
|
||||
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.05);
|
||||
z-index: 10;
|
||||
|
||||
:deep(.el-pagination) {
|
||||
margin: 0;
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,210 @@
|
|||
<script setup lang="ts">
|
||||
import { handleTree } from "@/utils/tree";
|
||||
import { getQyDeptListApi } from "@/api/system/dept";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { ref, computed, watch, onMounted, getCurrentInstance } from "vue";
|
||||
|
||||
import Dept from "@iconify-icons/ri/git-branch-line";
|
||||
import Reset from "@iconify-icons/ri/restart-line";
|
||||
import Search from "@iconify-icons/ep/search";
|
||||
import More2Fill from "@iconify-icons/ri/more-2-fill";
|
||||
import OfficeBuilding from "@iconify-icons/ep/office-building";
|
||||
import LocationCompany from "@iconify-icons/ep/add-location";
|
||||
import ExpandIcon from "./svg/expand.svg?component";
|
||||
import UnExpandIcon from "./svg/unexpand.svg?component";
|
||||
import { useWxStore } from "@/store/modules/wx";
|
||||
|
||||
// TODO 这个类可以抽取作为SideBar TreeSelect组件
|
||||
interface Tree {
|
||||
id: number;
|
||||
deptName: string;
|
||||
highlight?: boolean;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
defineProps({
|
||||
modelValue: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const wxStore = useWxStore();
|
||||
const treeRef = ref();
|
||||
const treeData = ref([]);
|
||||
const isExpand = ref(true);
|
||||
const searchValue = ref("");
|
||||
const highlightMap = ref({});
|
||||
const { proxy } = getCurrentInstance();
|
||||
const defaultProps = {
|
||||
children: "children",
|
||||
label: "deptName"
|
||||
};
|
||||
const buttonClass = computed(() => {
|
||||
return [
|
||||
"!h-[20px]",
|
||||
"reset-margin",
|
||||
"!text-gray-500",
|
||||
"dark:!text-white",
|
||||
"dark:hover:!text-primary"
|
||||
];
|
||||
});
|
||||
|
||||
const filterNode = (value: string, data: Tree) => {
|
||||
if (!value) return true;
|
||||
return data.deptName.includes(value);
|
||||
};
|
||||
|
||||
function nodeClick(value) {
|
||||
console.log(value);
|
||||
const nodeId = value.$treeNodeId;
|
||||
console.log(nodeId);
|
||||
highlightMap.value[nodeId] = highlightMap.value[nodeId]?.highlight
|
||||
? Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
|
||||
highlight: false
|
||||
})
|
||||
: Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
|
||||
highlight: true
|
||||
});
|
||||
Object.values(highlightMap.value).forEach((v: Tree) => {
|
||||
if (v.id !== nodeId) {
|
||||
v.highlight = false;
|
||||
}
|
||||
});
|
||||
|
||||
proxy.$emit("update:modelValue", value.id);
|
||||
}
|
||||
|
||||
function toggleRowExpansionAll(status) {
|
||||
isExpand.value = status;
|
||||
const nodes = (proxy.$refs["treeRef"] as any).store._getAllNodes();
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
nodes[i].expanded = status;
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置状态(选中状态、搜索框值、树初始化) */
|
||||
function onReset() {
|
||||
highlightMap.value = {};
|
||||
searchValue.value = "";
|
||||
toggleRowExpansionAll(true);
|
||||
}
|
||||
|
||||
watch(searchValue, val => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const { data } = await getQyDeptListApi(wxStore.corpid);
|
||||
treeData.value = handleTree(data);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full bg-bg_color overflow-y-auto" :style="{ height: `calc(100vh - 133px)` }">
|
||||
<!--<div class="flex items-center h-[56px]">
|
||||
<p class="flex-1 ml-2 font-bold text-base truncate" title="部门列表">
|
||||
部门列表
|
||||
</p>
|
||||
<el-input
|
||||
style="flex: 2"
|
||||
size="default"
|
||||
v-model="searchValue"
|
||||
placeholder="请输入部门名称"
|
||||
clearable
|
||||
>
|
||||
<template #suffix>
|
||||
<el-icon class="el-input__icon">
|
||||
<IconifyIconOffline
|
||||
v-show="searchValue.length === 0"
|
||||
:icon="Search"
|
||||
/>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-dropdown :hide-on-click="false">
|
||||
<IconifyIconOffline class="w-[38px] cursor-pointer" width="20px" :icon="More2Fill" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<el-button
|
||||
:class="buttonClass"
|
||||
link
|
||||
type="primary"
|
||||
:icon="useRenderIcon(isExpand ? ExpandIcon : UnExpandIcon)"
|
||||
@click="toggleRowExpansionAll(isExpand ? false : true)"
|
||||
>
|
||||
{{ isExpand ? "折叠全部" : "展开全部" }}
|
||||
</el-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<el-button
|
||||
:class="buttonClass"
|
||||
link
|
||||
type="primary"
|
||||
:icon="useRenderIcon(Reset)"
|
||||
@click="onReset"
|
||||
>
|
||||
重置状态
|
||||
</el-button>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<el-divider />-->
|
||||
<el-tree ref="treeRef" :data="treeData" node-key="id" size="default" :props="defaultProps" default-expand-all
|
||||
:expand-on-click-node="false" :filter-node-method="filterNode" @node-click="nodeClick">
|
||||
<template #default="{ node, data }">
|
||||
<span :class="[
|
||||
'text-base',
|
||||
'flex',
|
||||
'items-center',
|
||||
'tracking-wider',
|
||||
'gap-2',
|
||||
'select-none',
|
||||
searchValue.trim().length > 0 &&
|
||||
node.label.includes(searchValue) &&
|
||||
'text-red-500',
|
||||
highlightMap[node.id]?.highlight ? 'dark:text-primary' : ''
|
||||
]" :style="{
|
||||
background: highlightMap[node.id]?.highlight
|
||||
? 'var(--el-color-primary-light-7)'
|
||||
: 'transparent'
|
||||
}">
|
||||
<!-- <IconifyIconOffline :icon="data.parentId === 0
|
||||
? OfficeBuilding
|
||||
: data.type === 2
|
||||
? LocationCompany
|
||||
: Dept
|
||||
" /> -->
|
||||
{{ node.label }}
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-divider) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tree) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(144, 147, 153, 0.3);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(144, 147, 153, 0.5);
|
||||
}
|
||||
</style>
|
|
@ -21,9 +21,10 @@ import { reactive, ref, computed, onMounted, toRaw, h } from "vue";
|
|||
import { CommonUtils } from "@/utils/common";
|
||||
import { addDialog } from "@/components/ReDialog";
|
||||
import { handleTree, setDisabledForTreeOptions } from "@/utils/tree";
|
||||
import { getDeptListApi } from "@/api/system/dept";
|
||||
import { getQyDeptListApi } from "@/api/system/dept";
|
||||
import { getPostListApi } from "@/api/system/post";
|
||||
import { getRoleListApi } from "@/api/system/role";
|
||||
import { useWxStore } from "@/store/modules/wx";
|
||||
|
||||
export function useHook() {
|
||||
const searchFormParams = reactive<UserQuery>({
|
||||
|
@ -31,6 +32,7 @@ export function useHook() {
|
|||
phoneNumber: undefined,
|
||||
status: undefined,
|
||||
username: undefined,
|
||||
nickname: undefined,
|
||||
timeRangeColumn: "createTime"
|
||||
});
|
||||
|
||||
|
@ -51,6 +53,8 @@ export function useHook() {
|
|||
const postOptions = ref([]);
|
||||
const roleOptions = ref([]);
|
||||
|
||||
const wxStore = useWxStore();
|
||||
|
||||
const columns: TableColumnList = [
|
||||
{
|
||||
label: "用户编号",
|
||||
|
@ -153,10 +157,8 @@ export function useHook() {
|
|||
|
||||
function onChange({ row, index }) {
|
||||
ElMessageBox.confirm(
|
||||
`确认要<strong>${
|
||||
row.status === 0 ? "停用" : "启用"
|
||||
}</strong><strong style='color:var(--el-color-primary)'>${
|
||||
row.username
|
||||
`确认要<strong>${row.status === 0 ? "停用" : "启用"
|
||||
}</strong><strong style='color:var(--el-color-primary)'>${row.username
|
||||
}</strong>用户吗?`,
|
||||
"系统提示",
|
||||
{
|
||||
|
@ -356,7 +358,7 @@ export function useHook() {
|
|||
|
||||
onMounted(async () => {
|
||||
onSearch();
|
||||
const deptResponse = await getDeptListApi();
|
||||
const deptResponse = await getQyDeptListApi(wxStore.corpid);
|
||||
deptTreeList.value = await setDisabledForTreeOptions(
|
||||
handleTree(deptResponse.data),
|
||||
"status"
|
||||
|
|
|
@ -50,13 +50,12 @@ watch(
|
|||
<div class="main">
|
||||
<tree class="w-[17%] float-left" v-model="searchFormParams.deptId" />
|
||||
<div class="float-right w-[82%]">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:inline="true"
|
||||
:model="searchFormParams"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
|
||||
>
|
||||
<el-form-item label="用户编号:" prop="userId">
|
||||
<el-form ref="formRef" :inline="true" :model="searchFormParams"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]">
|
||||
<el-form-item label="姓名:" prop="nickname">
|
||||
<el-input v-model="searchFormParams.nickname" placeholder="请输入" clearable class="!w-[160px]" />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="用户编号:" prop="userId">
|
||||
<el-input
|
||||
v-model="searchFormParams.userId"
|
||||
placeholder="请输入用户编号"
|
||||
|
@ -94,14 +93,9 @@ watch(
|
|||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon(Search)"
|
||||
:loading="pageLoading"
|
||||
@click="onSearch"
|
||||
>
|
||||
<el-button type="primary" :icon="useRenderIcon(Search)" :loading="pageLoading" @click="onSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
|
@ -112,90 +106,42 @@ watch(
|
|||
|
||||
<PureTableBar title="用户管理" :columns="columns" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog('新增')"
|
||||
>
|
||||
<el-button type="primary" :icon="useRenderIcon(AddFill)" @click="openDialog('新增')">
|
||||
新增用户
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
:icon="useRenderIcon(Upload)"
|
||||
@click="openUploadDialog"
|
||||
>
|
||||
<el-button type="info" :icon="useRenderIcon(Upload)" @click="openUploadDialog">
|
||||
导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
:icon="useRenderIcon(Download)"
|
||||
@click="exportAllExcel"
|
||||
>
|
||||
<el-button type="warning" :icon="useRenderIcon(Download)" @click="exportAllExcel">
|
||||
导出
|
||||
</el-button>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
border
|
||||
adaptive
|
||||
align-whole="center"
|
||||
table-layout="auto"
|
||||
:loading="pageLoading"
|
||||
:size="size"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small' ? true : false"
|
||||
:header-cell-style="{
|
||||
<pure-table border adaptive align-whole="center" table-layout="auto" :loading="pageLoading" :size="size"
|
||||
:data="dataList" :columns="dynamicColumns" :pagination="pagination"
|
||||
:paginationSmall="size === 'small' ? true : false" :header-cell-style="{
|
||||
background: 'var(--el-table-row-hover-bg-color)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
@page-size-change="getList"
|
||||
@page-current-change="getList"
|
||||
>
|
||||
}" @page-size-change="getList" @page-current-change="getList">
|
||||
<template #operation="{ row }">
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
@click="openDialog('编辑', row)"
|
||||
:icon="useRenderIcon(EditPen)"
|
||||
>
|
||||
<el-button class="reset-margin" link type="primary" :size="size" @click="openDialog('编辑', row)"
|
||||
:icon="useRenderIcon(EditPen)">
|
||||
修改
|
||||
</el-button>
|
||||
<el-popconfirm title="是否确认删除?" @confirm="handleDelete(row)">
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Delete)"
|
||||
>
|
||||
<el-button class="reset-margin" link type="primary" :size="size" :icon="useRenderIcon(Delete)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<el-dropdown>
|
||||
<el-button
|
||||
class="ml-3 mt-[2px]"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(More)"
|
||||
/>
|
||||
<el-button class="ml-3 mt-[2px]" link type="primary" :size="size" :icon="useRenderIcon(More)" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<el-button
|
||||
:class="buttonClass"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Password)"
|
||||
@click="openResetPasswordDialog(row)"
|
||||
>
|
||||
<el-button :class="buttonClass" link type="primary" :size="size" :icon="useRenderIcon(Password)"
|
||||
@click="openResetPasswordDialog(row)">
|
||||
重置密码
|
||||
</el-button>
|
||||
</el-dropdown-item>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { handleTree } from "@/utils/tree";
|
||||
import { getDeptListApi } from "@/api/system/dept";
|
||||
import { getQyDeptListApi } from "@/api/system/dept";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { ref, computed, watch, onMounted, getCurrentInstance } from "vue";
|
||||
|
||||
|
@ -12,6 +12,7 @@ import OfficeBuilding from "@iconify-icons/ep/office-building";
|
|||
import LocationCompany from "@iconify-icons/ep/add-location";
|
||||
import ExpandIcon from "./svg/expand.svg?component";
|
||||
import UnExpandIcon from "./svg/unexpand.svg?component";
|
||||
import { useWxStore } from "@/store/modules/wx";
|
||||
|
||||
// TODO 这个类可以抽取作为SideBar TreeSelect组件
|
||||
interface Tree {
|
||||
|
@ -28,6 +29,7 @@ defineProps({
|
|||
}
|
||||
});
|
||||
|
||||
const wxStore = useWxStore();
|
||||
const treeRef = ref();
|
||||
const treeData = ref([]);
|
||||
const isExpand = ref(true);
|
||||
|
@ -93,17 +95,14 @@ watch(searchValue, val => {
|
|||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const { data } = await getDeptListApi();
|
||||
const { data } = await getQyDeptListApi(wxStore.corpid);
|
||||
treeData.value = handleTree(data);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="h-full bg-bg_color overflow-auto"
|
||||
:style="{ minHeight: `calc(100vh - 133px)` }"
|
||||
>
|
||||
<div class="flex items-center h-[56px]">
|
||||
<div class="h-full bg-bg_color overflow-y-auto" :style="{ height: `calc(100vh - 133px)` }">
|
||||
<!--<div class="flex items-center h-[56px]">
|
||||
<p class="flex-1 ml-2 font-bold text-base truncate" title="部门列表">
|
||||
部门列表
|
||||
</p>
|
||||
|
@ -124,11 +123,7 @@ onMounted(async () => {
|
|||
</template>
|
||||
</el-input>
|
||||
<el-dropdown :hide-on-click="false">
|
||||
<IconifyIconOffline
|
||||
class="w-[38px] cursor-pointer"
|
||||
width="20px"
|
||||
:icon="More2Fill"
|
||||
/>
|
||||
<IconifyIconOffline class="w-[38px] cursor-pointer" width="20px" :icon="More2Fill" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
|
@ -157,21 +152,11 @@ onMounted(async () => {
|
|||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<el-divider />
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="treeData"
|
||||
node-key="id"
|
||||
size="default"
|
||||
:props="defaultProps"
|
||||
default-expand-all
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="nodeClick"
|
||||
>
|
||||
<el-divider />-->
|
||||
<el-tree ref="treeRef" :data="treeData" node-key="id" size="default" :props="defaultProps" default-expand-all
|
||||
:expand-on-click-node="false" :filter-node-method="filterNode" @node-click="nodeClick">
|
||||
<template #default="{ node, data }">
|
||||
<span
|
||||
:class="[
|
||||
<span :class="[
|
||||
'text-base',
|
||||
'flex',
|
||||
'items-center',
|
||||
|
@ -182,22 +167,17 @@ onMounted(async () => {
|
|||
node.label.includes(searchValue) &&
|
||||
'text-red-500',
|
||||
highlightMap[node.id]?.highlight ? 'dark:text-primary' : ''
|
||||
]"
|
||||
:style="{
|
||||
]" :style="{
|
||||
background: highlightMap[node.id]?.highlight
|
||||
? 'var(--el-color-primary-light-7)'
|
||||
: 'transparent'
|
||||
}"
|
||||
>
|
||||
<IconifyIconOffline
|
||||
:icon="
|
||||
data.parentId === 0
|
||||
}">
|
||||
<!-- <IconifyIconOffline :icon="data.parentId === 0
|
||||
? OfficeBuilding
|
||||
: data.type === 2
|
||||
? LocationCompany
|
||||
: Dept
|
||||
"
|
||||
/>
|
||||
" /> -->
|
||||
{{ node.label }}
|
||||
</span>
|
||||
</template>
|
||||
|
@ -209,4 +189,22 @@ onMounted(async () => {
|
|||
:deep(.el-divider) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tree) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(144, 147, 153, 0.3);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(144, 147, 153, 0.5);
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue