用户管理修改
This commit is contained in:
parent
1c23d04690
commit
e8672104a4
33
src/App.vue
33
src/App.vue
|
@ -1,25 +1,16 @@
|
||||||
<template>
|
<script setup lang="ts">
|
||||||
<el-config-provider :locale="currentLocale">
|
import { computed } from "vue";
|
||||||
<router-view />
|
|
||||||
<ReDialog />
|
|
||||||
</el-config-provider>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from "vue";
|
|
||||||
import { ElConfigProvider } from "element-plus";
|
import { ElConfigProvider } from "element-plus";
|
||||||
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
||||||
import { ReDialog } from "@/components/ReDialog";
|
import { ReDialog } from "@/components/ReDialog";
|
||||||
export default defineComponent({
|
|
||||||
name: "app",
|
const currentLocale = computed(() => zhCn);
|
||||||
components: {
|
|
||||||
[ElConfigProvider.name]: ElConfigProvider,
|
|
||||||
ReDialog
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
currentLocale() {
|
|
||||||
return zhCn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ElConfigProvider :locale="currentLocale">
|
||||||
|
<router-view />
|
||||||
|
<ReDialog />
|
||||||
|
</ElConfigProvider>
|
||||||
|
</template>
|
||||||
|
|
|
@ -23,6 +23,10 @@ export type LoginByPasswordDTO = {
|
||||||
captchaCode: string;
|
captchaCode: string;
|
||||||
/** 验证码对应的缓存key */
|
/** 验证码对应的缓存key */
|
||||||
captchaCodeKey: string;
|
captchaCodeKey: string;
|
||||||
|
/** 企业微信 */
|
||||||
|
corpid: string;
|
||||||
|
code: string;
|
||||||
|
state: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +58,7 @@ export interface CurrentUserInfoDTO {
|
||||||
email?: string;
|
email?: string;
|
||||||
loginDate?: Date;
|
loginDate?: Date;
|
||||||
loginIp?: string;
|
loginIp?: string;
|
||||||
nickName?: string;
|
nickname?: string;
|
||||||
phoneNumber?: string;
|
phoneNumber?: string;
|
||||||
postId?: number;
|
postId?: number;
|
||||||
postName?: string;
|
postName?: string;
|
||||||
|
@ -106,3 +110,15 @@ type Result = {
|
||||||
export const getAsyncRoutes = () => {
|
export const getAsyncRoutes = () => {
|
||||||
return http.request<Result>("get", "/getRouters");
|
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
|
params
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
export const getQyDeptListApi = (corpid: string) => {
|
||||||
|
return http.request<ResponseData<Array<DeptDTO>>>("get", "/qywx/departments/depts", {
|
||||||
|
params: { corpid }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/** 新增部门 */
|
/** 新增部门 */
|
||||||
export const addDeptApi = (data: DeptRequest) => {
|
export const addDeptApi = (data: DeptRequest) => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ export interface UserQuery extends BasePageQuery {
|
||||||
status?: number;
|
status?: number;
|
||||||
userId?: number;
|
userId?: number;
|
||||||
username?: string;
|
username?: string;
|
||||||
|
nickname?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,16 +7,27 @@
|
||||||
<div v-if="showQr" class="qr-popover">
|
<div v-if="showQr" class="qr-popover">
|
||||||
<ReQrcode text="http://wxshop.ab98.cn/shop-api/api/shop/wechatAuth" :options="{ width: 200 }" />
|
<ReQrcode text="http://wxshop.ab98.cn/shop-api/api/shop/wechatAuth" :options="{ width: 200 }" />
|
||||||
<div class="qr-tip">微信扫码访问</div>
|
<div class="qr-tip">微信扫码访问</div>
|
||||||
|
<el-button class="copy-btn" type="primary" size="small" @click="copyLink">
|
||||||
|
复制链接
|
||||||
|
</el-button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
import ReQrcode from '@/components/ReQrcode'
|
import ReQrcode from '@/components/ReQrcode'
|
||||||
import Iphone from "@iconify-icons/ep/iphone";
|
import Iphone from "@iconify-icons/ep/iphone";
|
||||||
|
import { copyTextToClipboard } from "@pureadmin/utils";
|
||||||
|
|
||||||
const showQr = ref(false)
|
const showQr = ref(false)
|
||||||
|
|
||||||
|
const copyLink = () => {
|
||||||
|
const success = copyTextToClipboard('http://wxshop.ab98.cn/shop-api/api/shop/wechatAuth');
|
||||||
|
success ? ElMessage.success('链接复制成功') : ElMessage.error('复制失败,请手动复制');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -32,6 +43,9 @@ const showQr = ref(false)
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-tip {
|
.qr-tip {
|
||||||
|
|
|
@ -17,6 +17,7 @@ const {
|
||||||
onPanel,
|
onPanel,
|
||||||
pureApp,
|
pureApp,
|
||||||
username,
|
username,
|
||||||
|
nickname,
|
||||||
userAvatar,
|
userAvatar,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
toggleSideBar
|
toggleSideBar
|
||||||
|
@ -43,7 +44,7 @@ const {
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link navbar-bg-hover select-none">
|
<span class="el-dropdown-link navbar-bg-hover select-none">
|
||||||
<img :src="userAvatar" :style="avatarsStyle" />
|
<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>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="logout">
|
<el-dropdown-menu class="logout">
|
||||||
|
|
|
@ -19,6 +19,7 @@ const {
|
||||||
backTopMenu,
|
backTopMenu,
|
||||||
onPanel,
|
onPanel,
|
||||||
username,
|
username,
|
||||||
|
nickname,
|
||||||
userAvatar,
|
userAvatar,
|
||||||
avatarsStyle
|
avatarsStyle
|
||||||
} = useNav();
|
} = useNav();
|
||||||
|
@ -53,7 +54,7 @@ nextTick(() => {
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link navbar-bg-hover">
|
<span class="el-dropdown-link navbar-bg-hover">
|
||||||
<img :src="userAvatar" :style="avatarsStyle" />
|
<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>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="logout">
|
<el-dropdown-menu class="logout">
|
||||||
|
|
|
@ -22,6 +22,7 @@ const {
|
||||||
onPanel,
|
onPanel,
|
||||||
resolvePath,
|
resolvePath,
|
||||||
username,
|
username,
|
||||||
|
nickname,
|
||||||
userAvatar,
|
userAvatar,
|
||||||
getDivStyle,
|
getDivStyle,
|
||||||
avatarsStyle
|
avatarsStyle
|
||||||
|
@ -82,7 +83,7 @@ watch(
|
||||||
<el-dropdown trigger="click">
|
<el-dropdown trigger="click">
|
||||||
<span class="el-dropdown-link navbar-bg-hover select-none">
|
<span class="el-dropdown-link navbar-bg-hover select-none">
|
||||||
<img :src="userAvatar" :style="avatarsStyle" />
|
<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>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="logout">
|
<el-dropdown-menu class="logout">
|
||||||
|
|
|
@ -40,6 +40,10 @@ export function useNav() {
|
||||||
return useUserStoreHook()?.username;
|
return useUserStoreHook()?.username;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const nickname = computed(() => {
|
||||||
|
return useUserStoreHook()?.nickname;
|
||||||
|
});
|
||||||
|
|
||||||
const avatarsStyle = computed(() => {
|
const avatarsStyle = computed(() => {
|
||||||
return username.value ? { marginRight: "10px" } : "";
|
return username.value ? { marginRight: "10px" } : "";
|
||||||
});
|
});
|
||||||
|
@ -140,6 +144,7 @@ export function useNav() {
|
||||||
isCollapse,
|
isCollapse,
|
||||||
pureApp,
|
pureApp,
|
||||||
username,
|
username,
|
||||||
|
nickname,
|
||||||
userAvatar,
|
userAvatar,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
tooltipEffect
|
tooltipEffect
|
||||||
|
|
|
@ -38,6 +38,7 @@ export type setType = {
|
||||||
|
|
||||||
export type userType = {
|
export type userType = {
|
||||||
username?: string;
|
username?: string;
|
||||||
|
nickname?: string;
|
||||||
roles?: Array<string>;
|
roles?: Array<string>;
|
||||||
/** 字典ListMap 用于下拉框直接展示 */
|
/** 字典ListMap 用于下拉框直接展示 */
|
||||||
dictionaryList: Map<String, Array<DictionaryData>>;
|
dictionaryList: Map<String, Array<DictionaryData>>;
|
||||||
|
|
|
@ -19,6 +19,10 @@ export const useUserStore = defineStore({
|
||||||
username:
|
username:
|
||||||
storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.userInfo
|
storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.userInfo
|
||||||
.username ?? "",
|
.username ?? "",
|
||||||
|
// 昵称
|
||||||
|
nickname:
|
||||||
|
storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.userInfo
|
||||||
|
.nickname ?? "",
|
||||||
// 页面级别权限
|
// 页面级别权限
|
||||||
roles: storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.roleKey
|
roles: storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.roleKey
|
||||||
? [storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.roleKey]
|
? [storageSession().getItem<TokenDTO>(sessionKey)?.currentUser.roleKey]
|
||||||
|
@ -40,6 +44,11 @@ export const useUserStore = defineStore({
|
||||||
/** TODO 这里不是应该再进一步存到sessionStorage中吗 */
|
/** TODO 这里不是应该再进一步存到sessionStorage中吗 */
|
||||||
this.username = username;
|
this.username = username;
|
||||||
},
|
},
|
||||||
|
/** 存储昵称 */
|
||||||
|
SET_NICKNAME(nickname: string) {
|
||||||
|
/** TODO 这里不是应该再进一步存到sessionStorage中吗 */
|
||||||
|
this.nickname = nickname;
|
||||||
|
},
|
||||||
/** 存储角色 */
|
/** 存储角色 */
|
||||||
SET_ROLES(roles: Array<string>) {
|
SET_ROLES(roles: Array<string>) {
|
||||||
this.roles = roles;
|
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_USERNAME(data.currentUser.userInfo.username);
|
||||||
useUserStoreHook().SET_ROLES([data.currentUser.roleKey]);
|
useUserStoreHook().SET_ROLES([data.currentUser.roleKey]);
|
||||||
|
useUserStoreHook().SET_NICKNAME(data.currentUser.userInfo.nickname);
|
||||||
storageSession().setItem(sessionKey, data);
|
storageSession().setItem(sessionKey, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import { PureTableBar } from "@/components/RePureTableBar";
|
import { PureTableBar } from "@/components/RePureTableBar";
|
||||||
import { useRoute } from "vue-router";
|
import { onBeforeRouteUpdate, useRoute } from "vue-router";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
import { getCabinetCellList, deleteCabinetCell, CabinetCellDTO } from "@/api/cabinet/cabinet-cell";
|
import { getCabinetCellList, deleteCabinetCell, CabinetCellDTO } from "@/api/cabinet/cabinet-cell";
|
||||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
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 CellFormModal from "./cell-form-modal.vue";
|
||||||
import CellEditModal from "./cell-edit-modal.vue";
|
import CellEditModal from "./cell-edit-modal.vue";
|
||||||
import { ElMessage, ElMessageBox } from "element-plus";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
|
import { on } from "events";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "CabinetCell"
|
name: "CabinetCell"
|
||||||
|
@ -47,6 +48,12 @@ onMounted(() => {
|
||||||
getList();
|
getList();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
onBeforeRouteUpdate(() => {
|
||||||
|
if (route.query.cabinetId) {
|
||||||
|
searchFormParams.value.cabinetId = Number(route.query.cabinetId);
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -136,7 +143,6 @@ const switchCellType = (cellType: number) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getList();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -41,6 +41,7 @@ import Lock from "@iconify-icons/ri/lock-fill";
|
||||||
import User from "@iconify-icons/ri/user-3-fill";
|
import User from "@iconify-icons/ri/user-3-fill";
|
||||||
import * as CommonAPI from "@/api/common/login";
|
import * as CommonAPI from "@/api/common/login";
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStoreHook } from "@/store/modules/user";
|
||||||
|
import { useWxStore } from "@/store/modules/wx";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "Login"
|
name: "Login"
|
||||||
|
@ -58,6 +59,8 @@ const ruleFormRef = ref<FormInstance>();
|
||||||
// 判断登录页面显示哪个组件(0:登录(默认)、1:手机登录、2:二维码登录、3:注册、4:忘记密码)
|
// 判断登录页面显示哪个组件(0:登录(默认)、1:手机登录、2:二维码登录、3:注册、4:忘记密码)
|
||||||
const currentPage = ref(0);
|
const currentPage = ref(0);
|
||||||
|
|
||||||
|
const wxStore = useWxStore();
|
||||||
|
|
||||||
const { initStorage } = useLayout();
|
const { initStorage } = useLayout();
|
||||||
initStorage();
|
initStorage();
|
||||||
const { dataTheme, dataThemeChange } = useDataThemeChange();
|
const { dataTheme, dataThemeChange } = useDataThemeChange();
|
||||||
|
@ -65,23 +68,66 @@ dataThemeChange();
|
||||||
// const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
|
// const { title, getDropdownItemStyle, getDropdownItemClass } = useNav();
|
||||||
const { title } = 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({
|
const ruleForm = reactive({
|
||||||
username: "admin",
|
username: "admin",
|
||||||
password: getPassword(),
|
password: getPassword(),
|
||||||
captchaCode: "",
|
captchaCode: "",
|
||||||
captchaCodeKey: ""
|
captchaCodeKey: "",
|
||||||
|
corpid: wxStore.corpid,
|
||||||
|
code: wxStore.code,
|
||||||
|
state: wxStore.state
|
||||||
});
|
});
|
||||||
|
|
||||||
const onLogin = async (formEl: FormInstance | undefined) => {
|
const onLogin = async (formEl: FormInstance | undefined) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
if (!formEl) return;
|
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) => {
|
await formEl.validate((valid, fields) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
CommonAPI.loginByPassword({
|
CommonAPI.loginByPassword({
|
||||||
username: ruleForm.username,
|
username: ruleForm.username,
|
||||||
password: rsaEncrypt(ruleForm.password),
|
password: ruleForm.password ? rsaEncrypt(ruleForm.password) : '',
|
||||||
captchaCode: ruleForm.captchaCode,
|
captchaCode: ruleForm.captchaCode,
|
||||||
captchaCodeKey: ruleForm.captchaCodeKey
|
captchaCodeKey: ruleForm.captchaCodeKey,
|
||||||
|
corpid: ruleForm.corpid,
|
||||||
|
code: ruleForm.code,
|
||||||
|
state: ruleForm.state
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
// 登录成功后 将token存储到sessionStorage中
|
// 登录成功后 将token存储到sessionStorage中
|
||||||
|
@ -105,6 +151,7 @@ const onLogin = async (formEl: FormInstance | undefined) => {
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 使用公共函数,避免`removeEventListener`失效 */
|
/** 使用公共函数,避免`removeEventListener`失效 */
|
||||||
|
@ -147,8 +194,9 @@ onBeforeMount(async () => {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.document.addEventListener("keypress", onkeypress);
|
window.document.addEventListener("keypress", onkeypress);
|
||||||
// 临时登录
|
// 临时登录
|
||||||
ruleForm.password = "admin123"
|
if (wxStore.code && wxStore.corpid) {
|
||||||
onLogin(ruleFormRef.value);
|
onLogin(ruleFormRef.value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
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 { CommonUtils } from "@/utils/common";
|
||||||
import { addDialog } from "@/components/ReDialog";
|
import { addDialog } from "@/components/ReDialog";
|
||||||
import { handleTree, setDisabledForTreeOptions } from "@/utils/tree";
|
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 { getPostListApi } from "@/api/system/post";
|
||||||
import { getRoleListApi } from "@/api/system/role";
|
import { getRoleListApi } from "@/api/system/role";
|
||||||
|
import { useWxStore } from "@/store/modules/wx";
|
||||||
|
|
||||||
export function useHook() {
|
export function useHook() {
|
||||||
const searchFormParams = reactive<UserQuery>({
|
const searchFormParams = reactive<UserQuery>({
|
||||||
|
@ -31,6 +32,7 @@ export function useHook() {
|
||||||
phoneNumber: undefined,
|
phoneNumber: undefined,
|
||||||
status: undefined,
|
status: undefined,
|
||||||
username: undefined,
|
username: undefined,
|
||||||
|
nickname: undefined,
|
||||||
timeRangeColumn: "createTime"
|
timeRangeColumn: "createTime"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,6 +53,8 @@ export function useHook() {
|
||||||
const postOptions = ref([]);
|
const postOptions = ref([]);
|
||||||
const roleOptions = ref([]);
|
const roleOptions = ref([]);
|
||||||
|
|
||||||
|
const wxStore = useWxStore();
|
||||||
|
|
||||||
const columns: TableColumnList = [
|
const columns: TableColumnList = [
|
||||||
{
|
{
|
||||||
label: "用户编号",
|
label: "用户编号",
|
||||||
|
@ -153,10 +157,8 @@ export function useHook() {
|
||||||
|
|
||||||
function onChange({ row, index }) {
|
function onChange({ row, index }) {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
`确认要<strong>${
|
`确认要<strong>${row.status === 0 ? "停用" : "启用"
|
||||||
row.status === 0 ? "停用" : "启用"
|
}</strong><strong style='color:var(--el-color-primary)'>${row.username
|
||||||
}</strong><strong style='color:var(--el-color-primary)'>${
|
|
||||||
row.username
|
|
||||||
}</strong>用户吗?`,
|
}</strong>用户吗?`,
|
||||||
"系统提示",
|
"系统提示",
|
||||||
{
|
{
|
||||||
|
@ -356,7 +358,7 @@ export function useHook() {
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
onSearch();
|
onSearch();
|
||||||
const deptResponse = await getDeptListApi();
|
const deptResponse = await getQyDeptListApi(wxStore.corpid);
|
||||||
deptTreeList.value = await setDisabledForTreeOptions(
|
deptTreeList.value = await setDisabledForTreeOptions(
|
||||||
handleTree(deptResponse.data),
|
handleTree(deptResponse.data),
|
||||||
"status"
|
"status"
|
||||||
|
|
|
@ -50,13 +50,12 @@ watch(
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<tree class="w-[17%] float-left" v-model="searchFormParams.deptId" />
|
<tree class="w-[17%] float-left" v-model="searchFormParams.deptId" />
|
||||||
<div class="float-right w-[82%]">
|
<div class="float-right w-[82%]">
|
||||||
<el-form
|
<el-form ref="formRef" :inline="true" :model="searchFormParams"
|
||||||
ref="formRef"
|
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]">
|
||||||
:inline="true"
|
<el-form-item label="姓名:" prop="nickname">
|
||||||
:model="searchFormParams"
|
<el-input v-model="searchFormParams.nickname" placeholder="请输入" clearable class="!w-[160px]" />
|
||||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
|
</el-form-item>
|
||||||
>
|
<!-- <el-form-item label="用户编号:" prop="userId">
|
||||||
<el-form-item label="用户编号:" prop="userId">
|
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchFormParams.userId"
|
v-model="searchFormParams.userId"
|
||||||
placeholder="请输入用户编号"
|
placeholder="请输入用户编号"
|
||||||
|
@ -94,14 +93,9 @@ watch(
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item> -->
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button
|
<el-button type="primary" :icon="useRenderIcon(Search)" :loading="pageLoading" @click="onSearch">
|
||||||
type="primary"
|
|
||||||
:icon="useRenderIcon(Search)"
|
|
||||||
:loading="pageLoading"
|
|
||||||
@click="onSearch"
|
|
||||||
>
|
|
||||||
搜索
|
搜索
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||||
|
@ -112,90 +106,42 @@ watch(
|
||||||
|
|
||||||
<PureTableBar title="用户管理" :columns="columns" @refresh="onSearch">
|
<PureTableBar title="用户管理" :columns="columns" @refresh="onSearch">
|
||||||
<template #buttons>
|
<template #buttons>
|
||||||
<el-button
|
<el-button type="primary" :icon="useRenderIcon(AddFill)" @click="openDialog('新增')">
|
||||||
type="primary"
|
|
||||||
:icon="useRenderIcon(AddFill)"
|
|
||||||
@click="openDialog('新增')"
|
|
||||||
>
|
|
||||||
新增用户
|
新增用户
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button type="info" :icon="useRenderIcon(Upload)" @click="openUploadDialog">
|
||||||
type="info"
|
|
||||||
:icon="useRenderIcon(Upload)"
|
|
||||||
@click="openUploadDialog"
|
|
||||||
>
|
|
||||||
导入
|
导入
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button type="warning" :icon="useRenderIcon(Download)" @click="exportAllExcel">
|
||||||
type="warning"
|
|
||||||
:icon="useRenderIcon(Download)"
|
|
||||||
@click="exportAllExcel"
|
|
||||||
>
|
|
||||||
导出
|
导出
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
<pure-table
|
<pure-table border adaptive align-whole="center" table-layout="auto" :loading="pageLoading" :size="size"
|
||||||
border
|
:data="dataList" :columns="dynamicColumns" :pagination="pagination"
|
||||||
adaptive
|
:paginationSmall="size === 'small' ? true : false" :header-cell-style="{
|
||||||
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)',
|
background: 'var(--el-table-row-hover-bg-color)',
|
||||||
color: 'var(--el-text-color-primary)'
|
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 }">
|
<template #operation="{ row }">
|
||||||
<el-button
|
<el-button class="reset-margin" link type="primary" :size="size" @click="openDialog('编辑', row)"
|
||||||
class="reset-margin"
|
:icon="useRenderIcon(EditPen)">
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
@click="openDialog('编辑', row)"
|
|
||||||
:icon="useRenderIcon(EditPen)"
|
|
||||||
>
|
|
||||||
修改
|
修改
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-popconfirm title="是否确认删除?" @confirm="handleDelete(row)">
|
<el-popconfirm title="是否确认删除?" @confirm="handleDelete(row)">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-button
|
<el-button class="reset-margin" link type="primary" :size="size" :icon="useRenderIcon(Delete)">
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(Delete)"
|
|
||||||
>
|
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-popconfirm>
|
</el-popconfirm>
|
||||||
<el-dropdown>
|
<el-dropdown>
|
||||||
<el-button
|
<el-button class="ml-3 mt-[2px]" link type="primary" :size="size" :icon="useRenderIcon(More)" />
|
||||||
class="ml-3 mt-[2px]"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(More)"
|
|
||||||
/>
|
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-button
|
<el-button :class="buttonClass" link type="primary" :size="size" :icon="useRenderIcon(Password)"
|
||||||
:class="buttonClass"
|
@click="openResetPasswordDialog(row)">
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(Password)"
|
|
||||||
@click="openResetPasswordDialog(row)"
|
|
||||||
>
|
|
||||||
重置密码
|
重置密码
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { handleTree } from "@/utils/tree";
|
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 { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
import { ref, computed, watch, onMounted, getCurrentInstance } from "vue";
|
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 LocationCompany from "@iconify-icons/ep/add-location";
|
||||||
import ExpandIcon from "./svg/expand.svg?component";
|
import ExpandIcon from "./svg/expand.svg?component";
|
||||||
import UnExpandIcon from "./svg/unexpand.svg?component";
|
import UnExpandIcon from "./svg/unexpand.svg?component";
|
||||||
|
import { useWxStore } from "@/store/modules/wx";
|
||||||
|
|
||||||
// TODO 这个类可以抽取作为SideBar TreeSelect组件
|
// TODO 这个类可以抽取作为SideBar TreeSelect组件
|
||||||
interface Tree {
|
interface Tree {
|
||||||
|
@ -28,6 +29,7 @@ defineProps({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const wxStore = useWxStore();
|
||||||
const treeRef = ref();
|
const treeRef = ref();
|
||||||
const treeData = ref([]);
|
const treeData = ref([]);
|
||||||
const isExpand = ref(true);
|
const isExpand = ref(true);
|
||||||
|
@ -93,17 +95,14 @@ watch(searchValue, val => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const { data } = await getDeptListApi();
|
const { data } = await getQyDeptListApi(wxStore.corpid);
|
||||||
treeData.value = handleTree(data);
|
treeData.value = handleTree(data);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="h-full bg-bg_color overflow-y-auto" :style="{ height: `calc(100vh - 133px)` }">
|
||||||
class="h-full bg-bg_color overflow-auto"
|
<!--<div class="flex items-center h-[56px]">
|
||||||
:style="{ minHeight: `calc(100vh - 133px)` }"
|
|
||||||
>
|
|
||||||
<div class="flex items-center h-[56px]">
|
|
||||||
<p class="flex-1 ml-2 font-bold text-base truncate" title="部门列表">
|
<p class="flex-1 ml-2 font-bold text-base truncate" title="部门列表">
|
||||||
部门列表
|
部门列表
|
||||||
</p>
|
</p>
|
||||||
|
@ -124,11 +123,7 @@ onMounted(async () => {
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-dropdown :hide-on-click="false">
|
<el-dropdown :hide-on-click="false">
|
||||||
<IconifyIconOffline
|
<IconifyIconOffline class="w-[38px] cursor-pointer" width="20px" :icon="More2Fill" />
|
||||||
class="w-[38px] cursor-pointer"
|
|
||||||
width="20px"
|
|
||||||
:icon="More2Fill"
|
|
||||||
/>
|
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
|
@ -157,21 +152,11 @@ onMounted(async () => {
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<el-divider />
|
<el-divider />-->
|
||||||
<el-tree
|
<el-tree ref="treeRef" :data="treeData" node-key="id" size="default" :props="defaultProps" default-expand-all
|
||||||
ref="treeRef"
|
:expand-on-click-node="false" :filter-node-method="filterNode" @node-click="nodeClick">
|
||||||
: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 }">
|
<template #default="{ node, data }">
|
||||||
<span
|
<span :class="[
|
||||||
:class="[
|
|
||||||
'text-base',
|
'text-base',
|
||||||
'flex',
|
'flex',
|
||||||
'items-center',
|
'items-center',
|
||||||
|
@ -182,22 +167,17 @@ onMounted(async () => {
|
||||||
node.label.includes(searchValue) &&
|
node.label.includes(searchValue) &&
|
||||||
'text-red-500',
|
'text-red-500',
|
||||||
highlightMap[node.id]?.highlight ? 'dark:text-primary' : ''
|
highlightMap[node.id]?.highlight ? 'dark:text-primary' : ''
|
||||||
]"
|
]" :style="{
|
||||||
:style="{
|
|
||||||
background: highlightMap[node.id]?.highlight
|
background: highlightMap[node.id]?.highlight
|
||||||
? 'var(--el-color-primary-light-7)'
|
? 'var(--el-color-primary-light-7)'
|
||||||
: 'transparent'
|
: 'transparent'
|
||||||
}"
|
}">
|
||||||
>
|
<!-- <IconifyIconOffline :icon="data.parentId === 0
|
||||||
<IconifyIconOffline
|
|
||||||
:icon="
|
|
||||||
data.parentId === 0
|
|
||||||
? OfficeBuilding
|
? OfficeBuilding
|
||||||
: data.type === 2
|
: data.type === 2
|
||||||
? LocationCompany
|
? LocationCompany
|
||||||
: Dept
|
: Dept
|
||||||
"
|
" /> -->
|
||||||
/>
|
|
||||||
{{ node.label }}
|
{{ node.label }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -209,4 +189,22 @@ onMounted(async () => {
|
||||||
:deep(.el-divider) {
|
:deep(.el-divider) {
|
||||||
margin: 0;
|
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>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue