Files
django-vue3-admin/web/src/views/system/dept/components/DeptUserCom/index.vue
2023-11-24 15:12:46 +08:00

321 lines
7.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div v-show="!showCount" class="dept-user-com-box dept-info">
<div class="di-left">
<h3>{{ deptInfo.dept_name || '' }}</h3>
<div class="di-cell">
<p>部门负责人</p>
<p class="content">{{ deptInfo.owner || '无' }}</p>
</div>
<div class="di-cell">
<p>部门人数</p>
<p class="content">{{ deptInfo.dept_user || 0 }}</p>
</div>
<div class="di-cell">
<p>部门简介</p>
<p class="content">{{ deptInfo.description || '无' }}</p>
</div>
<div class="di-cell">
<p>显示子级</p>
<el-switch
v-model="isShowChildFlag"
inline-prompt
active-text=""
inactive-text=""
:disabled="!currentDeptId"
@change="handleSwitchChange"
style="--el-switch-on-color: var(--el-color-primary)"
/>
</div>
</div>
<div style="height: 180px; width: 380px" ref="deptCountBar"></div>
<div style="height: 180px; width: 200px" ref="deptSexPie"></div>
</div>
<fs-crud
ref="crudRef"
v-bind="crudBinding"
:customClass="!showCount ? 'dept-user-com-box dept-user-com-table' : 'dept-user-com-box dept-user-com-table-cover'"
>
<template #toolbar-left>
<el-button :icon="!showCount ? 'Hide' : 'View'" circle @click="showCount = !showCount"></el-button>
</template>
<template #actionbar-right>
<importExcel api="api/system/user/" v-auth="'user:Import'">导入 </importExcel>
</template>
</fs-crud>
<el-dialog v-model="resetPwdVisible" title="重设密码" width="400px" draggable :before-close="handleResetPwdClose">
<div>
<el-input v-model="resetPwdFormState.newPassword" type="password" placeholder="请输入密码" show-password style="margin-bottom: 20px" />
<el-input v-model="resetPwdFormState.newPassword2" type="password" placeholder="请再次输入密码" show-password />
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleResetPwdClose">取消</el-button>
<el-button type="primary" @click="handleResetPwdSubmit"> 保存 </el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup name="user">
import { ref, reactive, onMounted } from 'vue';
import { useExpose, useCrud } from '@fast-crud/fast-crud';
import { Md5 } from 'ts-md5';
import { createCrudOptions } from './crud';
import importExcel from '/@/components/importExcel/index.vue';
import * as echarts from 'echarts';
import { ECharts, EChartsOption, init } from 'echarts';
import { getDeptInfoById, resetPwd } from './api';
import { warningNotification, successNotification } from '/@/utils/message';
import { HeadDeptInfoType } from '../../types';
let deptCountChart: ECharts;
let deptSexChart: ECharts;
// crud组件的ref
const crudRef = ref();
// crud 配置的ref
const crudBinding = ref();
// 暴露的方法
const { crudExpose } = useExpose({ crudRef, crudBinding });
let currentDeptId = ref('');
let deptCountBar = ref();
let deptSexPie = ref();
let isShowChildFlag = ref(false);
let deptInfo = ref<Partial<HeadDeptInfoType>>({});
let showCount = ref(false);
let resetPwdVisible = ref(false);
let resetPwdFormState = reactive({
id: 0,
newPassword: '',
newPassword2: '',
});
/**
* 初始化顶部部门折线图
*/
const initDeptCountBarChart = () => {
const xAxisData = deptInfo.value.sub_dept_map?.map((item) => item.name) || [];
const yAxisData = deptInfo.value.sub_dept_map?.map((item) => item.count) || [];
const option: EChartsOption = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
xAxis: {
type: 'category',
data: xAxisData,
axisTick: {
alignWithLabel: true,
},
},
yAxis: {
type: 'value',
},
dataZoom: [
{
type: 'inside',
},
],
grid: {
top: '6%',
right: '5%',
bottom: '10%',
left: '10%',
},
series: [
{
data: yAxisData,
type: 'bar',
barWidth: '60%',
showBackground: true,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' },
]),
},
},
],
};
deptCountChart.setOption(option);
};
/**
* 初始化顶部性别统计
*/
const initDeptSexPieChart = () => {
const option: EChartsOption = {
tooltip: {
trigger: 'item',
},
legend: {
orient: 'vertical',
right: '0%',
left: '65%',
top: 'center',
itemWidth: 12,
itemHeight: 12,
},
series: [
{
type: 'pie',
radius: '65%',
center: ['32%', '50%'],
label: {
show: false,
position: 'center',
},
color: ['#188df0', '#f56c6c', '#dcdfe6'],
data: [
{ value: deptInfo.value.gender?.male || 0, name: '男' },
{ value: deptInfo.value.gender?.female || 0, name: '女' },
{ value: deptInfo.value.gender?.unknown || 0, name: '未知' },
],
},
],
};
deptSexChart.setOption(option);
};
/**
* 获取顶部部门信息
*/
const getDeptInfo = async () => {
const res = await getDeptInfoById(currentDeptId.value, isShowChildFlag.value ? '1' : '0');
if (res?.code === 2000) {
deptInfo.value = res.data;
initDeptCountBarChart();
initDeptSexPieChart();
}
};
/**
* 部门切换刷新用户列表
*/
const handleDoRefreshUser = (id: string) => {
currentDeptId.value = id;
crudExpose.doSearch({ form: { dept: id } });
getDeptInfo();
};
const handleSwitchChange = () => {
handleDoRefreshUser(currentDeptId.value);
};
const handleResetPwdOpen = ({ id }: { id: number }) => {
resetPwdFormState.id = id;
resetPwdVisible.value = true;
};
const handleResetPwdClose = () => {
resetPwdVisible.value = false;
resetPwdFormState.id = 0;
resetPwdFormState.newPassword = '';
resetPwdFormState.newPassword2 = '';
};
const handleResetPwdSubmit = async () => {
if (!resetPwdFormState.id) {
warningNotification('请选择用户!');
return;
}
if (!resetPwdFormState.newPassword || !resetPwdFormState.newPassword2) {
warningNotification('请输入密码!');
return;
}
if (resetPwdFormState.newPassword !== resetPwdFormState.newPassword2) {
warningNotification('两次输入密码不一致');
return;
}
const pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}');
if (!pwdRegex.test(resetPwdFormState.newPassword) || !pwdRegex.test(resetPwdFormState.newPassword2)) {
warningNotification('您的密码复杂度太低(密码中必须包含字母、数字)');
return;
}
const res = await resetPwd(resetPwdFormState.id, {
newPassword: Md5.hashStr(resetPwdFormState.newPassword),
newPassword2: Md5.hashStr(resetPwdFormState.newPassword2),
});
if (res?.code === 2000) {
successNotification(res.msg || '修改成功!');
handleResetPwdClose();
}
};
onMounted(() => {
deptCountChart = init(deptCountBar.value as HTMLElement);
deptSexChart = init(deptSexPie.value as HTMLElement);
getDeptInfo();
crudExpose.doRefresh();
});
defineExpose({
handleDoRefreshUser,
});
// 你的crud配置
const { crudOptions } = createCrudOptions({ crudExpose, context: { getDeptInfo, isShowChildFlag, handleResetPwdOpen } });
// 初始化crud配置
const { resetCrudOptions } = useCrud({
crudExpose,
crudOptions,
context: {},
});
</script>
<style lang="scss" scoped>
.dept-user-com-box {
padding: 0 10px;
border-radius: 8px 0 0 8px;
box-sizing: border-box;
background-color: #fff;
}
.dept-user-com-table {
height: calc(100% - 200px);
}
.dept-user-com-table-cover {
height: 100%;
}
.dept-info {
width: 100%;
height: 200px;
display: flex;
align-items: center;
justify-content: space-around;
margin-bottom: 10px;
.di-left {
h3 {
font-size: 18px;
font-weight: 900;
}
.di-cell {
margin-top: 6px;
display: flex;
align-items: center;
p:nth-child(1) {
display: block;
width: 85px;
text-align: left;
}
.content {
max-width: 120px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
</style>