初始化项目

This commit is contained in:
2025-11-20 09:10:35 +08:00
parent b60a4fe9f4
commit a96be99a54
254 changed files with 40718 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
<template>
<div class="system-edit-dic-container">
<el-dialog :title="(ruleForm.configId!==0?'修改':'添加')+'参数'" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" ref="formRef" :rules="rules" size="default" label-width="90px">
<el-form-item label="参数名称" prop="configName">
<el-input v-model="ruleForm.configName" placeholder="请输入参数名称" />
</el-form-item>
<el-form-item label="参数键名" prop="configKey">
<el-input v-model="ruleForm.configKey" placeholder="请输入参数键名" />
</el-form-item>
<el-form-item label="参数键值" prop="configValue">
<el-input v-model="ruleForm.configValue" placeholder="请输入参数键值" />
</el-form-item>
<el-form-item label="系统内置" prop="configType">
<el-radio-group v-model="ruleForm.configType">
<el-radio
v-for="dict in sysYesNoOptions"
:key="dict.value"
:label="dict.value"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="ruleForm.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ruleForm.configId!==0?'修 改':'添 加'}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent,ref, unref } from 'vue';
import {ElMessage} from "element-plus";
import {addConfig, editConfig, getConfig} from "/@/api/system/config";
interface RuleFormState {
configId: number;
configName: string;
configKey: string;
configValue: string;
configType: string;
remark: string
}
interface DicState {
isShowDialog: boolean;
ruleForm: RuleFormState;
rules:{}
}
export default defineComponent({
name: 'systemEditDicData',
props:{
sysYesNoOptions:{
type:Array,
default:()=>[]
}
},
setup(prop,{emit}) {
const formRef = ref<HTMLElement | null>(null);
const state = reactive<DicState>({
isShowDialog: false,
ruleForm: {
configId: 0,
configName: '',
configKey: '',
configValue: '',
configType: '0',
remark: '',
},
rules: {
configName: [
{ required: true, message: "参数名称不能为空", trigger: "blur" }
],
configKey: [
{ required: true, message: "参数键名不能为空", trigger: "blur" }
],
configValue: [
{ required: true, message: "参数键值不能为空", trigger: "blur" }
]
}
});
// 打开弹窗
const openDialog = (row: RuleFormState|null) => {
resetForm();
if (row){
getConfig(row.configId).then((res:any)=>{
const data:RuleFormState = res.data.data
data.configType = String(data.configType)
state.ruleForm = data
})
state.ruleForm = row;
}
state.isShowDialog = true;
};
const resetForm = ()=>{
state.ruleForm = {
configId: 0,
configName: '',
configKey: '',
configValue: '',
configType: '0',
remark: '',
}
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
if(state.ruleForm.configId!==0){
//修改
editConfig(state.ruleForm).then(()=>{
ElMessage.success('参数修改成功');
closeDialog(); // 关闭弹窗
emit('dataList')
})
}else{
//添加
addConfig(state.ruleForm).then(()=>{
ElMessage.success('参数添加成功');
closeDialog(); // 关闭弹窗
emit('dataList')
})
}
}
});
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
formRef,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,243 @@
<template>
<div class="system-dic-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="参数名称" prop="configName">
<el-input
v-model="tableData.param.configName"
placeholder="请输入参数名称"
clearable
size="default"
@keyup.enter.native="dataList"
/>
</el-form-item>
<el-form-item label="参数键名" prop="configKey">
<el-input
v-model="tableData.param.configKey"
placeholder="请输入参数键名"
clearable
size="default"
@keyup.enter.native="dataList"
/>
</el-form-item>
<el-form-item label="系统内置" prop="configType" style="width: 200px;">
<el-select
v-model="tableData.param.configType"
placeholder="系统内置"
clearable
size="default"
style="width: 240px"
>
<el-option v-for="dict in sys_yes_no"
:key="dict.value"
:label="dict.label"
:value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="dateRange">
<el-date-picker
v-model="tableData.param.dateRange"
size="default"
style="width: 240px"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="dataList">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" @click="resetQuery(queryRef)">
<el-icon>
<ele-Refresh />
</el-icon>
重置
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddDic">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增参数
</el-button>
<el-button size="default" type="danger" class="ml10" @click="onRowDel(null)">
<el-icon>
<ele-Delete />
</el-icon>
删除参数
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="参数主键" align="center" prop="configId" />
<el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />
<el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" />
<el-table-column label="参数键值" align="center" prop="configValue" />
<el-table-column label="系统内置" align="center" prop="configType" :formatter="typeFormat" />
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createdAt" width="180" />
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditDic(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="dataList"
/>
</el-card>
<EditConfig ref="editDicRef" @dataList="dataList" :sysYesNoOptions="sys_yes_no"/>
</div>
</template>
<script lang="ts">
import {toRefs,reactive,onMounted,ref,defineComponent,unref,getCurrentInstance} from 'vue';
import { ElMessageBox, ElMessage,FormInstance} from 'element-plus';
import EditConfig from '/@/views/system/config/component/editConfig.vue';
import {deleteConfig, getConfigList} from "/@/api/system/config";
// 定义接口来定义对象的类型
interface TableDataRow {
configId: number;
configName: string;
configKey: string;
configValue: string,
configType: number,
remark: string,
createdAt: string,
}
interface TableDataState {
ids:number[];
tableData: {
data: Array<TableDataRow>;
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
configName:string;
configKey:string;
configType:string;
dateRange:string[];
};
};
}
export default defineComponent({
name: 'apiV1SystemDictDataList',
components: { EditConfig },
setup() {
const {proxy} = getCurrentInstance() as any;
const addDicRef = ref();
const editDicRef = ref();
const queryRef = ref();
const {sys_yes_no} = proxy.useDict('sys_yes_no')
const state = reactive<TableDataState>({
ids:[],
tableData: {
data: [],
total: 0,
loading: false,
param: {
dateRange:[],
pageNum: 1,
pageSize: 10,
configName:'',
configKey:'',
configType:''
},
},
});
// 初始化表格数据
const initTableData = () => {
dataList()
};
const dataList=()=>{
getConfigList(state.tableData.param).then((res:any)=>{
state.tableData.data = res.data.list;
state.tableData.total = res.data.total;
});
};
// 打开新增字典弹窗
const onOpenAddDic = () => {
editDicRef.value.openDialog();
};
// 打开修改字典弹窗
const onOpenEditDic = (row: TableDataRow) => {
editDicRef.value.openDialog(row);
};
// 删除字典
const onRowDel = (row: TableDataRow) => {
let msg = '你确定要删除所选数据?';
let ids:number[] = [] ;
if(row){
msg = `此操作将永久删除用户:“${row.configName}”,是否继续?`
ids = [row.configId]
}else{
ids = state.ids
}
if(ids.length===0){
ElMessage.error('请选择要删除的数据。');
return
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteConfig(ids).then(()=>{
ElMessage.success('删除成功');
dataList();
})
})
.catch(() => {});
};
// 页面加载时
onMounted(() => {
initTableData();
});
/** 重置按钮操作 */
const resetQuery = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
dataList()
};
// 多选框选中数据
const handleSelectionChange = (selection:TableDataRow[])=> {
state.ids = selection.map(item => item.configId)
};
// 参数系统内置字典翻译
const typeFormat=(row:TableDataRow) => {
return proxy.selectDictLabel(unref(sys_yes_no), row.configType);
};
return {
addDicRef,
editDicRef,
queryRef,
sys_yes_no,
onOpenAddDic,
onOpenEditDic,
onRowDel,
dataList,
resetQuery,
handleSelectionChange,
typeFormat,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,185 @@
<template>
<div class="system-edit-dept-container">
<el-dialog :title="(ruleForm.deptId!==0?'修改':'添加')+'部门'" v-model="isShowDialog" width="769px">
<el-form ref="formRef" :model="ruleForm" :rules="rules" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="上级部门">
<el-cascader
:options="deptData"
:props="{ checkStrictly: true,emitPath: false, value: 'deptId', label: 'deptName' }"
placeholder="请选择部门"
clearable
class="w100"
v-model="ruleForm.parentId"
>
<template #default="{ node, data }">
<span>{{ data.deptName }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="部门名称" prop="deptName">
<el-input v-model="ruleForm.deptName" placeholder="请输入部门名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="负责人">
<el-input v-model="ruleForm.leader" placeholder="请输入负责人" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="手机号">
<el-input v-model="ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="邮箱">
<el-input v-model="ruleForm.email" placeholder="请输入" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="排序">
<el-input-number v-model="ruleForm.orderNum" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="部门状态">
<el-switch v-model="ruleForm.status" :active-value="1" :inactive-value="0" inline-prompt active-text="启" inactive-text="禁"></el-switch>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ruleForm.deptId!==0?'修 改':'添 加'}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import {reactive, toRefs, defineComponent, getCurrentInstance,ref,unref} from 'vue';
import {addDept,editDept, getDeptList} from "/@/api/system/dept";
import {ElMessage} from "element-plus";
// 定义接口来定义对象的类型
interface TableDataRow {
deptName: string;
id: number;
parentId:number;
children?: TableDataRow[];
}
interface RuleFormState {
deptId:number;
parentId: number;
deptName: string;
orderNum: number;
leader: string;
phone: string | number;
email: string;
status: number;
}
interface DeptSate {
isShowDialog: boolean;
ruleForm: RuleFormState;
deptData: Array<TableDataRow>;
rules: object;
}
export default defineComponent({
name: 'systemEditDept',
setup(prop,{emit}) {
const {proxy} = getCurrentInstance() as any;
const formRef = ref<HTMLElement | null>(null);
const state = reactive<DeptSate>({
isShowDialog: false,
ruleForm: {
deptId:0,
parentId: 0, // 上级部门
deptName: '', // 部门名称
orderNum:0,
leader: '',
phone: '',
email: '',
status: 1,
},
deptData: [], // 部门数据
rules: {
deptName:[
{required: true, message: "部门名称不能为空", trigger: "blur"},
]
}
});
// 打开弹窗
const openDialog = (row?: RuleFormState|number) => {
resetForm()
getDeptList().then((res:any)=>{
state.deptData = proxy.handleTree(res.data.deptList??[], "deptId","parentId");
});
if(row && typeof row === "object"){
state.ruleForm = row;
}else if(row && typeof row === 'number'){
state.ruleForm.parentId = row
}
state.isShowDialog = true;
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
if(state.ruleForm.deptId===0){
//添加
addDept(state.ruleForm).then(()=>{
ElMessage.success('角色添加成功');
closeDialog(); // 关闭弹窗
emit('deptList')
});
}else{
//修改
editDept(state.ruleForm).then(()=>{
ElMessage.success('角色修改成功');
closeDialog(); // 关闭弹窗
emit('deptList')
});
}
}
});
};
const resetForm = ()=>{
state.ruleForm = {
deptId:0,
parentId: 0, // 上级部门
deptName: '', // 部门名称
orderNum:0,
leader: '',
phone: '',
email: '',
status: 1,
}
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
formRef,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,153 @@
<template>
<div class="system-dept-container">
<el-card shadow="hover">
<div class="system-dept-search mb15">
<el-form :inline="true">
<el-form-item label="部门名称">
<el-input size="default" v-model="tableData.param.deptName" placeholder="请输入部门名称" class="w-50 m-2" clearable/>
</el-form-item>
<el-form-item label="状态">
<el-select size="default" placeholder="请选择状态" class="w-50 m-2" v-model="tableData.param.status" clearable>
<el-option label="启用" value="1" />
<el-option label="禁用" value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="deptList">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddDept">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增部门
</el-button>
</el-form-item>
</el-form>
</div>
<el-table
:data="tableData.data"
style="width: 100%"
row-key="deptId"
default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column prop="deptName" label="部门名称" show-overflow-tooltip> </el-table-column>
<el-table-column prop="status" label="部门状态" show-overflow-tooltip>
<template #default="scope">
<el-tag type="success" v-if="scope.row.status===1">启用</el-tag>
<el-tag type="info" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="orderNum" label="排序" show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenAddDept(scope.row)">新增</el-button>
<el-button size="small" text type="primary" @click="onOpenEditDept(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<EditDept ref="editDeptRef" @deptList="deptList"/>
</div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, onMounted, defineComponent,getCurrentInstance } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import EditDept from '/@/views/system/dept/component/editDept.vue';
import {deleteDept, getDeptList} from "/@/api/system/dept";
// 定义接口来定义对象的类型
interface TableDataRow {
deptId:number;
parentId:number;
deptName:string;
status:number;
orderNum:number;
createdAt:string;
children?:TableDataRow[];
}
interface TableDataState {
tableData: {
data: Array<TableDataRow>;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
deptName:string;
status:string;
};
};
}
export default defineComponent({
name: 'systemDept',
components: { EditDept },
setup() {
const {proxy} = getCurrentInstance() as any;
const editDeptRef = ref();
const state = reactive<TableDataState>({
tableData: {
data: [],
loading: false,
param: {
pageNum: 1,
pageSize: 10,
deptName:'',
status:''
},
},
});
// 初始化表格数据
const initTableData = () => {
deptList();
};
const deptList = ()=>{
getDeptList(state.tableData.param).then((res:any)=>{
state.tableData.data = proxy.handleTree(res.data.deptList??[], "deptId","parentId");
});
};
// 打开新增菜单弹窗
const onOpenAddDept = (row?: TableDataRow) => {
editDeptRef.value.openDialog(row?.deptId);
};
// 打开编辑菜单弹窗
const onOpenEditDept = (row: TableDataRow) => {
editDeptRef.value.openDialog(row);
};
// 删除当前行
const onTabelRowDel = (row: TableDataRow) => {
ElMessageBox.confirm(`此操作将永久删除部门:${row.deptName}, 是否继续?`, '提示', {
confirmButtonText: '删除',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteDept(row.deptId).then(()=>{
ElMessage.success('删除成功');
deptList();
})
})
.catch(() => {});
};
// 页面加载时
onMounted(() => {
initTableData();
});
return {
editDeptRef,
deptList,
onOpenAddDept,
onOpenEditDept,
onTabelRowDel,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,134 @@
<template>
<div class="system-edit-dic-container">
<el-dialog :title="(ruleForm.dictId!==0?'修改':'添加')+'字典'" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" ref="formRef" :rules="rules" size="default" label-width="90px">
<el-form-item label="字典名称" prop="dictName">
<el-input v-model="ruleForm.dictName" placeholder="请输入字典名称" />
</el-form-item>
<el-form-item label="字典类型" prop="dictType">
<el-input v-model="ruleForm.dictType" placeholder="请输入字典类型" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="ruleForm.status">
<el-radio :label="1" >启用</el-radio>
<el-radio :label="0" >禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="ruleForm.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ruleForm.dictId!==0?'修 改':'添 加'}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent,ref, unref } from 'vue';
import { getType,addType,editType } from '/@/api/system/dict/type';
import {ElMessage} from "element-plus";
interface RuleFormState {
dictId:number;
dictName:string;
dictType:string;
status:number;
remark:string;
}
interface DicState {
isShowDialog: boolean;
ruleForm: RuleFormState;
rules:{}
}
export default defineComponent({
name: 'systemEditDic',
setup(prop,{emit}) {
const formRef = ref<HTMLElement | null>(null);
const state = reactive<DicState>({
isShowDialog: false,
ruleForm: {
dictId:0,
dictName:'',
dictType:'',
status:1,
remark:''
},
rules: {
dictName: [
{ required: true, message: "字典名称不能为空", trigger: "blur" }
],
dictType: [
{ required: true, message: "字典类型不能为空", trigger: "blur" }
]
}
});
// 打开弹窗
const openDialog = (row: RuleFormState|null) => {
resetForm();
if (row){
getType(row.dictId).then((res:any)=>{
state.ruleForm = res.data.dictType
})
state.ruleForm = row;
}
state.isShowDialog = true;
};
const resetForm = ()=>{
state.ruleForm = {
dictId:0,
dictName:'',
dictType:'',
status:1,
remark:''
}
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
if(state.ruleForm.dictId!==0){
//修改
editType(state.ruleForm).then(()=>{
ElMessage.success('字典类型修改成功');
closeDialog(); // 关闭弹窗
emit('typeList')
})
}else{
//添加
addType(state.ruleForm).then(()=>{
ElMessage.success('字典类型添加成功');
closeDialog(); // 关闭弹窗
emit('typeList')
})
}
}
});
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
formRef,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,166 @@
<template>
<div class="system-edit-dic-container">
<el-dialog :title="(ruleForm.dictCode!==0?'修改':'添加')+'字典'" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" ref="formRef" :rules="rules" size="default" label-width="90px">
<el-form-item label="字典类型">
<el-input v-model="ruleForm.dictType" :disabled="true" />
</el-form-item>
<el-form-item label="数据标签" prop="dictLabel">
<el-input v-model="ruleForm.dictLabel" placeholder="请输入数据标签" />
</el-form-item>
<el-form-item label="数据键值" prop="dictValue">
<el-input v-model="ruleForm.dictValue" placeholder="请输入数据键值" />
</el-form-item>
<el-form-item label="显示排序" prop="dictSort">
<el-input-number v-model="ruleForm.dictSort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="系统默认">
<el-switch v-model="ruleForm.isDefault"
active-text=""
inactive-text=""
:active-value="1"
:inactive-value="0"
></el-switch>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="ruleForm.status">
<el-radio :label="1" >启用</el-radio>
<el-radio :label="0" >禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="ruleForm.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ruleForm.dictCode!==0?'修 改':'添 加'}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent,ref, unref } from 'vue';
import { getData,addData,editData } from '/@/api/system/dict/data';
import {ElMessage} from "element-plus";
interface RuleFormState {
dictCode: number;
dictLabel: string;
dictValue: string;
dictSort:number;
isDefault:number;
status:number;
remark:string;
dictType:string;
}
interface DicState {
isShowDialog: boolean;
ruleForm: RuleFormState;
rules:{}
}
export default defineComponent({
name: 'systemEditDicData',
props:{
dictType:{
type:String,
default:()=>''
}
},
setup(prop,{emit}) {
const formRef = ref<HTMLElement | null>(null);
const state = reactive<DicState>({
isShowDialog: false,
ruleForm: {
dictCode: 0,
dictLabel: '',
dictValue: '',
dictSort: 0,
isDefault:0,
status: 1,
remark: '',
dictType:prop.dictType
},
rules: {
dictLabel: [
{ required: true, message: "数据标签不能为空", trigger: "blur" }
],
dictValue: [
{ required: true, message: "数据键值不能为空", trigger: "blur" }
],
dictSort: [
{ required: true, message: "数据顺序不能为空", trigger: "blur" }
]
}
});
// 打开弹窗
const openDialog = (row: RuleFormState|null) => {
resetForm();
if (row){
getData(row.dictCode).then((res:any)=>{
state.ruleForm = res.data.dict
})
state.ruleForm = row;
}
state.isShowDialog = true;
};
const resetForm = ()=>{
state.ruleForm = {
dictCode: 0,
dictLabel: '',
dictValue: '',
dictSort: 0,
isDefault:0,
status: 1,
remark: '',
dictType:prop.dictType
}
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
if(state.ruleForm.dictCode!==0){
//修改
editData(state.ruleForm).then(()=>{
ElMessage.success('字典数据修改成功');
closeDialog(); // 关闭弹窗
emit('dataList')
})
}else{
//添加
addData(state.ruleForm).then(()=>{
ElMessage.success('字典数据添加成功');
closeDialog(); // 关闭弹窗
emit('dataList')
})
}
}
});
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
formRef,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,231 @@
<template>
<div class="system-dic-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="字典类型" prop="dictType">
<el-input
v-model="tableData.param.dictType"
placeholder="请输入字典类型"
clearable
size="default"
@keyup.enter.native="dataList"
/>
</el-form-item>
<el-form-item label="字典标签" prop="dictLabel">
<el-input
v-model="tableData.param.dictLabel"
placeholder="请输入字典标签"
clearable
size="default"
@keyup.enter.native="dataList"
/>
</el-form-item>
<el-form-item label="状态" prop="status" style="width: 200px;">
<el-select
v-model="tableData.param.status"
placeholder="字典状态"
clearable
size="default"
style="width: 240px"
>
<el-option label="启用" :value="1"/>
<el-option label="禁用" :value="0"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="dataList">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" @click="resetQuery(queryRef)">
<el-icon>
<ele-Refresh />
</el-icon>
重置
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddDic">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增字典
</el-button>
<el-button size="default" type="danger" class="ml10" @click="onRowDel(null)">
<el-icon>
<ele-Delete />
</el-icon>
删除字典
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="字典编码" align="center" prop="dictCode" />
<el-table-column label="字典标签" align="center" prop="dictLabel" />
<el-table-column label="字典键值" align="center" prop="dictValue" />
<el-table-column label="字典排序" align="center" prop="dictSort" />
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createdAt" width="180"/>
<el-table-column prop="status" label="字典状态" show-overflow-tooltip>
<template #default="scope">
<el-tag type="success" v-if="scope.row.status">启用</el-tag>
<el-tag type="info" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditDic(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="dataList"
/>
</el-card>
<EditDic ref="editDicRef" @dataList="dataList" :dict-type="tableData.param.dictType"/>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
import { ElMessageBox, ElMessage,FormInstance} from 'element-plus';
import EditDic from '/@/views/system/dict/component/editDicData.vue';
import {getDataList,deleteData} from "/@/api/system/dict/data";
import { useRoute } from 'vue-router';
// 定义接口来定义对象的类型
interface TableDataRow {
dictCode: number;
dictSort: number;
dictLabel: string;
dictValue: string;
dictType: string;
status: number,
remark: string;
createdAt: string
}
interface TableDataState {
ids:number[];
tableData: {
data: Array<TableDataRow>;
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
dictType: string;
dictLabel:string;
status: string;
};
};
}
export default defineComponent({
name: 'apiV1SystemDictDataList',
components: { EditDic },
setup() {
const route = useRoute();
const addDicRef = ref();
const editDicRef = ref();
const queryRef = ref();
const state = reactive<TableDataState>({
ids:[],
tableData: {
data: [],
total: 0,
loading: false,
param: {
pageNum: 1,
pageSize: 10,
dictLabel:'',
dictType:'',
status:''
},
},
});
// 初始化表格数据
const initTableData = () => {
dataList()
};
const dataList=()=>{
getDataList(state.tableData.param).then((res:any)=>{
state.tableData.data = res.data.list;
state.tableData.total = res.data.total;
});
};
// 打开新增字典弹窗
const onOpenAddDic = () => {
editDicRef.value.openDialog();
};
// 打开修改字典弹窗
const onOpenEditDic = (row: TableDataRow) => {
editDicRef.value.openDialog(row);
};
// 删除字典
const onRowDel = (row: TableDataRow) => {
let msg = '你确定要删除所选数据?';
let ids:number[] = [] ;
if(row){
msg = `此操作将永久删除用户:“${row.dictLabel}”,是否继续?`
ids = [row.dictCode]
}else{
ids = state.ids
}
if(ids.length===0){
ElMessage.error('请选择要删除的数据。');
return
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteData(ids).then(()=>{
ElMessage.success('删除成功');
dataList();
})
})
.catch(() => {});
};
// 页面加载时
onMounted(() => {
const dictType = route.params && route.params.dictType;
state.tableData.param.dictType = <string>dictType
initTableData();
});
/** 重置按钮操作 */
const resetQuery = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
dataList()
};
// 多选框选中数据
const handleSelectionChange = (selection:TableDataRow[])=> {
state.ids = selection.map(item => item.dictCode)
};
return {
addDicRef,
editDicRef,
queryRef,
onOpenAddDic,
onOpenEditDic,
onRowDel,
dataList,
resetQuery,
handleSelectionChange,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,245 @@
<template>
<div class="system-dic-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="字典名称" prop="dictName">
<el-input
v-model="tableData.param.dictName"
placeholder="请输入字典名称"
clearable
size="default"
style="width: 240px"
@keyup.enter.native="typeList"
/>
</el-form-item>
<el-form-item label="字典类型" prop="dictType">
<el-input
v-model="tableData.param.dictType"
placeholder="请输入字典类型"
clearable
size="default"
style="width: 240px"
@keyup.enter.native="typeList"
/>
</el-form-item>
<el-form-item label="状态" prop="status" style="width: 200px;">
<el-select
v-model="tableData.param.status"
placeholder="字典状态"
clearable
size="default"
style="width: 240px"
>
<el-option label="启用" :value="1"/>
<el-option label="禁用" :value="0"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="dateRange">
<el-date-picker
v-model="tableData.param.dateRange"
size="default"
style="width: 240px"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="typeList">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" @click="resetQuery(queryRef)">
<el-icon>
<ele-Refresh />
</el-icon>
重置
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddDic">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增字典
</el-button>
<el-button size="default" type="danger" class="ml10" @click="onRowDel(null)">
<el-icon>
<ele-Delete />
</el-icon>
删除字典
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="字典ID" align="center" prop="dictId" width="120"/>
<el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />
<el-table-column label="字典类型" align="center" :show-overflow-tooltip="true">
<template #default="scope">
<router-link :to="'/system/dict/data/list/' + scope.row.dictType" class="link-type">
<span>{{ scope.row.dictType }}</span>
</router-link>
</template>
</el-table-column>
<el-table-column prop="status" label="字典状态" show-overflow-tooltip>
<template #default="scope">
<el-tag type="success" v-if="scope.row.status">启用</el-tag>
<el-tag type="info" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="remark" label="字典描述" show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip width="180"></el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditDic(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="typeList"
/>
</el-card>
<EditDic ref="editDicRef" @typeList="typeList"/>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
import { ElMessageBox, ElMessage,FormInstance} from 'element-plus';
import EditDic from '/@/views/system/dict/component/editDic.vue';
import {deleteType, getTypeList} from "/@/api/system/dict/type";
// 定义接口来定义对象的类型
interface TableDataRow {
dictId:number;
dictName: string;
dictType: string;
status: number;
remark:string;
createdAt:string;
}
interface TableDataState {
ids:number[];
tableData: {
data: Array<TableDataRow>;
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
dictName: string;
dictType: string;
status: string;
dateRange:string[];
};
};
}
export default defineComponent({
name: 'systemDic',
components: { EditDic },
setup() {
const addDicRef = ref();
const editDicRef = ref();
const queryRef = ref();
const state = reactive<TableDataState>({
ids:[],
tableData: {
data: [],
total: 0,
loading: false,
param: {
pageNum: 1,
pageSize: 10,
dictName:'',
dictType:'',
status:'',
dateRange:[],
},
},
});
// 初始化表格数据
const initTableData = () => {
typeList()
};
const typeList=()=>{
getTypeList(state.tableData.param).then((res:any)=>{
state.tableData.data = res.data.dictTypeList;
state.tableData.total = res.data.total;
});
};
// 打开新增字典弹窗
const onOpenAddDic = () => {
editDicRef.value.openDialog();
};
// 打开修改字典弹窗
const onOpenEditDic = (row: TableDataRow) => {
editDicRef.value.openDialog(row);
};
// 删除字典
const onRowDel = (row: TableDataRow) => {
let msg = '你确定要删除所选数据?';
let ids:number[] = [] ;
if(row){
msg = `此操作将永久删除用户:“${row.dictName}”,是否继续?`
ids = [row.dictId]
}else{
ids = state.ids
}
if(ids.length===0){
ElMessage.error('请选择要删除的数据。');
return
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteType(ids).then(()=>{
ElMessage.success('删除成功');
typeList();
})
})
.catch(() => {});
};
// 页面加载时
onMounted(() => {
initTableData();
});
/** 重置按钮操作 */
const resetQuery = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
typeList()
};
// 多选框选中数据
const handleSelectionChange = (selection:TableDataRow[])=> {
state.ids = selection.map(item => item.dictId)
};
return {
addDicRef,
editDicRef,
queryRef,
onOpenAddDic,
onOpenEditDic,
onRowDel,
typeList,
resetQuery,
handleSelectionChange,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,334 @@
<template>
<div class="system-edit-menu-container">
<el-dialog :title="(acType==='add'?'新增':'修改')+'菜单'" v-model="isShowDialog"
width="769px" :close-on-click-modal="false">
<el-form :model="ruleForm" :rules="rules"
ref="ruleFormRef" size="default" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="上级菜单">
<el-cascader
:options="menuData"
:props="{ label: 'title',value: 'id',checkStrictly: true,emitPath: false }"
placeholder="请选择上级菜单"
clearable
class="w100"
v-model="ruleForm.pid"
>
<template #default="{ node, data }">
<span>{{ data.title }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="菜单类型" prop="menuType">
<el-radio-group v-model="ruleForm.menuType">
<el-radio label="0">目录</el-radio>
<el-radio label="1">菜单</el-radio>
<el-radio label="2">按钮</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="菜单名称" prop="menuName">
<el-input v-model="ruleForm.menuName" placeholder="请填写菜单名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="接口规则" prop="name">
<el-input v-model="ruleForm.name" placeholder="后端 aip 地址" clearable></el-input>
</el-form-item>
</el-col>
<template v-if="ruleForm.menuType === '0'||ruleForm.menuType === '1'">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="路由路径" prop="path">
<el-input v-model="ruleForm.path" placeholder="路由中的 path 值" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="重定向">
<el-input v-model="ruleForm.redirect" placeholder="请输入路由重定向" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="菜单图标">
<IconSelector placeholder="请输入菜单图标" v-model="ruleForm.icon" type="all" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="组件路径" prop="component">
<el-input v-model="ruleForm.component" placeholder="组件路径" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="链接地址">
<el-input v-model="ruleForm.linkUrl" placeholder="外链/内嵌时链接地址http:xxx.com" clearable :disabled="ruleForm.isLink===0">
</el-input>
</el-form-item>
</el-col>
</template>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="权限标识">
<el-select v-model="ruleForm.roles" multiple placeholder="选择角色" clearable class="w100">
<el-option v-for="role in roles" :key="'role_'+role.id" :label="role.name" :value="role.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="菜单排序">
<el-input-number v-model="ruleForm.menuSort" controls-position="right" placeholder="请输入排序" class="w100" />
</el-form-item>
</el-col>
<template v-if="ruleForm.menuType === '0'||ruleForm.menuType === '1'">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="是否隐藏">
<el-radio-group v-model="ruleForm.isHide">
<el-radio
v-for="dict in visibleOptions"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="页面缓存">
<el-radio-group v-model="ruleForm.isKeepAlive">
<el-radio :label="1">缓存</el-radio>
<el-radio :label="0">不缓存</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="是否固定">
<el-radio-group v-model="ruleForm.isAffix">
<el-radio :label="1">固定</el-radio>
<el-radio :label="0">不固定</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="是否外链">
<el-radio-group v-model="ruleForm.isLink" :disabled="ruleForm.isIframe===1">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="是否内嵌">
<el-radio-group v-model="ruleForm.isIframe" @change="onSelectIframeChange">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">{{acType==='add'?'新 增':'修 改'}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent,ref,unref,getCurrentInstance,nextTick } from 'vue';
import IconSelector from '/@/components/iconSelector/index.vue';
import { refreshBackEndControlRoutes } from "/@/router/backEnd";
import {getMenuParams, addMenu, getMenuInfo, updateMenu} from "/@/api/system/menu";
import {ElMessage} from "element-plus"
export default defineComponent({
name: 'systemEditMenu',
components: { IconSelector },
props:{
visibleOptions:{
type:Array,
default:()=>[],
},
acType:{
type:String,
default:()=>'add'
}
},
setup(props,{emit}) {
const ruleFormRef = ref<HTMLElement | null>(null);
const {proxy} = getCurrentInstance() as any;
const state = reactive({
loading: false,
isShowDialog: false,
roles:[],
// 参数请参考 `/src/router/route.ts` 中的 `dynamicRoutes` 路由菜单格式
ruleForm: {
id:undefined,
pid: 0, // 上级菜单
menuType: '0', // 菜单类型
menuName:'', // 菜单名称
name: '', // 接口规则
component: '', // 组件路径
isLink: 0, // 是否外链
menuSort: 0, // 菜单排序
path: '', // 路由路径
redirect: '', // 路由重定向,有子集 children 时
icon: '', // 菜单图标
roles: [], // 权限标识,取角色管理
isHide: '0', // 是否隐藏
isKeepAlive: 1, // 是否缓存
isAffix: 0, // 是否固定
linkUrl: '', // 外链/内嵌时链接地址http:xxx.com开启外链条件`1、isLink:true 2、链接地址不为空`
isIframe: 0, // 是否内嵌,开启条件,`1、isIframe:true 2、链接地址不为空`
},
// 表单校验
rules: {
parentId: [
{required: true, message: "父菜单不能为空", trigger: "blur"},
],
name:[
{required: true, message: "接口规则不能为空", trigger: "blur"},
],
path:[
{required: true, message: "路由地址不能为空", trigger: "blur"},
],
menuName: [
{required: true, message: "菜单名称不能为空", trigger: "blur"},
],
menuType: [
{required: true, message: "菜单类型不能为空", trigger: "blur"},
],
},
menuData: [], // 上级菜单数据
});
// 打开弹窗
const openDialog = (row: any) => {
initForm();
nextTick(()=>{
//获取角色信息
getMenuParams().then((res:any)=>{
state.roles = res.data.roles;
const menu = { id: 0, title: '主类目', children: [] };
menu.children = proxy.handleTree(res.data.menus, "id","pid");
state.menuData=new Array(menu) as any;
});
if(row) {
if (props.acType === 'add') {
state.ruleForm.pid = row.id
} else if (props.acType === 'edit') {
getMenuInfo(row.id).then(res => {
const data = res.data.rule;
state.ruleForm = {
id: data.id,
pid: data.pid, // 上级菜单
menuType: '' + data.menuType, // 菜单类型
menuName: data.title, // 菜单名称
name: data.name, // 接口规则
component: data.component, // 组件路径
isLink: data.isLink, // 是否外链
menuSort: data.weigh, // 菜单排序
path: data.path, // 路由路径
redirect: data.redirect, // 路由重定向,有子集 children 时
icon: data.icon, // 菜单图标
roles: res.data.roleIds, // 权限标识,取角色管理
isHide: '' + data.isHide, // 是否隐藏
isKeepAlive: data.isCached, // 是否缓存
isAffix: data.isAffix, // 是否固定
linkUrl: data.linkUrl, // 外链/内嵌时链接地址http:xxx.com开启外链条件`1、isLink:true 2、链接地址不为空`
isIframe: data.isIframe, // 是否内嵌,开启条件,`1、isIframe:true 2、链接地址不为空`
}
})
}
}
state.isShowDialog = true;
state.loading = false;
})
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 是否内嵌下拉改变
const onSelectIframeChange = () => {
if (state.ruleForm.isIframe===1) state.ruleForm.isLink = 1;
else state.ruleForm.isLink = 0;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(ruleFormRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
state.loading = true;
if(props.acType==='add'){
//添加
addMenu(state.ruleForm).then(()=>{
ElMessage.success('菜单添加成功');
closeDialog(); // 关闭弹窗
resetMenuSession()
emit('menuList')
}).finally(()=>{
state.loading = false;
})
}else{
//修改
updateMenu(state.ruleForm).then(()=>{
ElMessage.success('菜单修改成功');
closeDialog(); // 关闭弹窗
resetMenuSession()
emit('menuList')
}).finally(()=>{
state.loading = false;
})
}
}
})
};
// 重置菜单session
const resetMenuSession = () => {
refreshBackEndControlRoutes();
};
const initForm=()=>{
state.ruleForm = {
id:undefined,
pid: 0, // 上级菜单
menuType: '0', // 菜单类型
menuName:'', // 菜单名称
name: '', // 接口规则
component: '', // 组件路径
isLink: 0, // 是否外链
menuSort: 0, // 菜单排序
path: '', // 路由路径
redirect: '', // 路由重定向,有子集 children 时
icon: '', // 菜单图标
roles: [], // 权限标识,取角色管理
isHide: '0', // 是否隐藏
isKeepAlive: 1, // 是否缓存
isAffix: 0, // 是否固定
linkUrl: '', // 外链/内嵌时链接地址http:xxx.com开启外链条件`1、isLink:true 2、链接地址不为空`
isIframe: 0, // 是否内嵌,开启条件,`1、isIframe:true 2、链接地址不为空`
}
};
return {
ruleFormRef,
openDialog,
closeDialog,
onSelectIframeChange,
onCancel,
onSubmit,
resetMenuSession,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,157 @@
<template>
<div class="system-menu-container">
<el-card shadow="hover">
<div class="system-menu-search mb15">
<el-form :inline="true">
<el-form-item label="菜单名称">
<el-input
v-model="queryParams.title"
placeholder="请输入菜单名称"
clearable
class="w-50 m-2"
size="default"
/>
</el-form-item>
<el-form-item label="组件路径">
<el-input
v-model="queryParams.component"
placeholder="请输入组件路径"
clearable
size="default"
class="w-50 m-2"
/>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="handleQuery">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddMenu(null)" v-auth="'api/v1/system/menu/add'">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增菜单
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="menuTableData" style="width: 100%" row-key="path" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
<el-table-column label="菜单名称" show-overflow-tooltip>
<template #default="scope">
<SvgIcon :name="scope.row.icon" />
<span class="ml10">{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column prop="path" label="路由路径" show-overflow-tooltip></el-table-column>
<el-table-column label="组件路径" show-overflow-tooltip>
<template #default="scope">
<span>{{ scope.row.component }}</span>
</template>
</el-table-column>
<el-table-column label="api接口" show-overflow-tooltip>
<template #default="scope">
<span>{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column label="排序" show-overflow-tooltip width="80">
<template #default="scope">
{{ scope.row.weigh }}
</template>
</el-table-column>
<el-table-column label="类型" show-overflow-tooltip width="80">
<template #default="scope">
<el-tag :type="scope.row.menuType===0?'danger':(scope.row.menuType===1?'success':'warning')"
size="small">{{scope.row.menuType===0?'目录':(scope.row.menuType===1?'菜单':'按钮') }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="isHide" label="显示状态" :formatter="formatIsHide" width="120"></el-table-column>
<el-table-column label="操作" width="240">
<template #default="scope">
<el-button v-if="scope.row.menuType!==2" size="small" text type="primary" @click="onOpenAddMenu(scope.row)" v-auth="'api/v1/system/menu/add'">新增</el-button>
<el-button size="small" text type="primary" @click="onOpenEditMenu(scope.row)" v-auth="'api/v1/system/menu/update'">修改</el-button>
<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)" v-auth="'api/v1/system/menu/delete'">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<EditMenu ref="editMenuRef" @menuList="menuList" :visibleOptions="sys_show_hide" :acType="acType"/>
</div>
</template>
<script lang="ts">
import {ref, toRefs, reactive, onBeforeMount, defineComponent, getCurrentInstance, unref} from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import EditMenu from '/@/views/system/menu/component/editMenu.vue';
import {delMenu, getMenuList} from "/@/api/system/menu";
export default defineComponent({
name: 'apiV1SystemAuthMenuList',
components: { EditMenu },
setup() {
const editMenuRef = ref();
const state = reactive({
queryParams:{
title:"",
component:""
},
menuTableData:[],
});
const {proxy} = getCurrentInstance() as any;
const {sys_show_hide} = proxy.useDict('sys_show_hide')
const acType = ref('add')
// 打开新增菜单弹窗
const onOpenAddMenu = (row:any) => {
acType.value = 'add'
editMenuRef.value.openDialog(row);
};
// 打开编辑菜单弹窗
const onOpenEditMenu = (row: any) => {
acType.value='edit'
editMenuRef.value.openDialog(row);
};
// 删除当前行
const onTabelRowDel = (row: any) => {
ElMessageBox.confirm(`此操作将永久删除菜单:“${row.title}”, 是否继续?`, '提示', {
confirmButtonText: '删除',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
delMenu(row.id).then(()=>{
ElMessage.success('删除成功');
proxy.$refs['editMenuRef'].resetMenuSession()
menuList();
})
})
.catch(() => {});
};
const formatIsHide = (row:any)=>{
return proxy.selectDictLabel(unref(sys_show_hide), ''+row.isHide);
};
onBeforeMount(()=>{
menuList()
});
const handleQuery=() => {
menuList();
};
const menuList = ()=>{
getMenuList(state.queryParams).then(res=>{
state.menuTableData = proxy.handleTree(res.data.rules??[], "id","pid");
})
};
return {
editMenuRef,
onOpenAddMenu,
onOpenEditMenu,
onTabelRowDel,
formatIsHide,
menuList,
handleQuery,
...toRefs(state),
sys_show_hide,
acType
};
},
});
</script>

View File

@@ -0,0 +1,262 @@
<template>
<div class="system-dic-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="登录IP" prop="ipaddr">
<el-input
v-model="tableData.param.ipaddr"
placeholder="请输入登录地址"
clearable
style="width: 180px;"
size="default"
@keyup.enter.native="dataList"
/>
</el-form-item>
<el-form-item label="登录地点" prop="loginLocation">
<el-input
v-model="tableData.param.loginLocation"
placeholder="请输入登录地点"
clearable
style="width: 180px;"
size="default"
@keyup.enter.native="dataList"
/>
</el-form-item>
<el-form-item label="用户名称" prop="userName">
<el-input
v-model="tableData.param.userName"
placeholder="请输入用户名称"
clearable
style="width: 180px;"
size="default"
@keyup.enter.native="dataList"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="tableData.param.status"
placeholder="登录状态"
clearable
size="default"
style="width: 180px"
>
<el-option
v-for="dict in admin_login_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="登录时间" prop="dateRange">
<el-date-picker
v-model="tableData.param.dateRange"
size="default"
style="width: 240px"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="dataList">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" @click="resetQuery(queryRef)">
<el-icon>
<ele-Refresh />
</el-icon>
重置
</el-button>
<el-button size="default" type="danger" class="ml10" @click="onRowDel(null)">
<el-icon>
<ele-Delete />
</el-icon>
删除日志
</el-button>
<el-button size="default" type="danger" class="ml10" @click="onRowClear()">
<el-icon>
<ele-Delete />
</el-icon>
清空日志
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="编号" align="center" prop="infoId" />
<el-table-column label="登录名称" align="center" prop="loginName" />
<el-table-column label="登录地址" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" />
<el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
<el-table-column label="浏览器" align="center" prop="browser" />
<el-table-column label="操作系统" align="center" prop="os" />
<el-table-column label="登录状态" align="center" prop="status" :formatter="statusFormat" />
<el-table-column label="操作信息" align="center" prop="msg" />
<el-table-column label="登录日期" align="center" prop="loginTime" width="180" />
<el-table-column label="登录模块" alian="center" prop="module"></el-table-column>
</el-table>
<pagination
v-show="tableData.total>0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="dataList"
/>
</el-card>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref, defineComponent,getCurrentInstance,unref } from 'vue';
import { ElMessageBox, ElMessage,FormInstance} from 'element-plus';
import { logList,deleteLog,clearLog } from '/@/api/system/monitor/loginLog';
// 定义接口来定义对象的类型
interface TableDataRow {
infoId:number;
loginName:string;
ipaddr:string;
loginLocation:string;
browser:string;
os:string;
status:number;
msg:string;
loginTime:string;
module:string;
}
interface TableDataState {
ids:number[];
tableData: {
data: Array<TableDataRow>;
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
dateRange: string[];
status: string;
ipaddr:string;
loginLocation:string;
userName:string;
};
};
}
export default defineComponent({
name: 'apiV1SystemLoginLogList',
setup() {
const {proxy} = getCurrentInstance() as any;
const queryRef = ref();
const {admin_login_status} = proxy.useDict('admin_login_status')
const state = reactive<TableDataState>({
ids:[],
tableData: {
data: [],
total: 0,
loading: false,
param: {
pageNum: 1,
pageSize: 10,
dateRange: [],
status: '',
ipaddr:'',
loginLocation:'',
userName:''
},
},
});
// 初始化表格数据
const initTableData = () => {
dataList()
};
const dataList=()=>{
logList(state.tableData.param).then((res:any)=>{
state.tableData.data = res.data.list;
state.tableData.total = res.data.total;
});
};
// 删除日志
const onRowDel = (row: TableDataRow) => {
let msg = '你确定要删除所选数据?';
let ids:number[] = [] ;
if(row){
msg = `此操作将永久删除:“${row.loginName}”,是否继续?`
ids = [row.infoId]
}else{
ids = state.ids
}
if(ids.length===0){
ElMessage.error('请选择要删除的数据。');
return
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteLog(ids).then(()=>{
ElMessage.success('删除成功');
dataList();
})
})
.catch(() => {});
};
// 清空日志
const onRowClear = () => {
ElMessageBox.confirm('你确定要删除所选数据?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
clearLog().then(()=>{
ElMessage.success('清除成功');
dataList();
})
})
.catch(() => {});
};
// 页面加载时
onMounted(() => {
initTableData();
});
/** 重置按钮操作 */
const resetQuery = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
dataList()
};
// 多选框选中数据
const handleSelectionChange = (selection:TableDataRow[])=> {
state.ids = selection.map(item => item.infoId)
};
// 登录状态字典翻译
const statusFormat = (row:TableDataRow) => {
return proxy.selectDictLabel(unref(admin_login_status), row.status);
};
return {
queryRef,
onRowDel,
dataList,
resetQuery,
handleSelectionChange,
statusFormat,
onRowClear,
admin_login_status,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,194 @@
<template>
<!-- 操作日志详情抽屉 -->
<div class="system-sysOperLog-detail">
<el-drawer v-model="isShowDialog" size="80%" direction="ltr">
<template #header>
<h4>操作日志详情</h4>
</template>
<el-form ref="formRef" :model="formData" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="日志编号">{{ formData.operId }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="系统模块">{{ formData.title }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="操作方法">{{ formData.method }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="请求方式">{{ proxy.getOptionValue(formData.requestMethod, requestMethodOptions,'value','label') }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="操作人员">{{ formData.operName }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门名称">{{formData.deptName}}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="请求URL">{{ formData.operUrl }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="主机地址">{{ formData.operIp }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="操作地点">{{ formData.operLocation }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="请求参数">{{ formData.operParam }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="错误消息">{{ formData.errorMsg }}</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="操作时间">{{ proxy.parseTime(formData.operTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-form-item>
</el-col>
</el-row>
</el-form>
</el-drawer>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent,ref,getCurrentInstance,computed } from 'vue';
import {
getSysOperLog
} from "/@/api/system/monitor/operLog";
import {
SysOperLogInfoData,
SysOperLogEditState,
} from "/@/views/system/monitor/operLog/component/model"
export default defineComponent({
name:"apiV1SystemSysOperLogDetail",
components:{
},
props:{
requestMethodOptions:{
type:Array,
default:()=>[]
},
},
setup(props,{emit}) {
const {proxy} = <any>getCurrentInstance()
const formRef = ref<HTMLElement | null>(null);
const menuRef = ref();
const state = reactive<SysOperLogEditState>({
loading:false,
isShowDialog: false,
formData: {
operId: undefined,
title: undefined,
businessType: undefined,
method: undefined,
requestMethod: undefined,
operatorType: undefined,
operName: undefined,
deptName: undefined,
operUrl: undefined,
operIp: undefined,
operLocation: undefined,
operParam: undefined,
jsonResult: undefined,
status: false ,
errorMsg: undefined,
operTime: undefined,
linkedSysOperLogSysDept: {
deptId:undefined, // 部门id
deptName:undefined, // 部门名称
},
},
// 表单校验
rules: {
operId : [
{ required: true, message: "日志编号不能为空", trigger: "blur" }
],
operName : [
{ required: true, message: "操作人员不能为空", trigger: "blur" }
],
deptName : [
{ required: true, message: "部门名称不能为空", trigger: "blur" }
],
status : [
{ required: true, message: "操作状态0正常 1异常不能为空", trigger: "blur" }
],
}
});
// 打开弹窗
const openDialog = (row?: SysOperLogInfoData) => {
resetForm();
if(row) {
getSysOperLog(row.operId!).then((res:any)=>{
const data = res.data;
state.formData = data;
})
}
state.isShowDialog = true;
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
const resetForm = ()=>{
state.formData = {
operId: undefined,
title: undefined,
businessType: undefined,
method: undefined,
requestMethod: undefined,
operatorType: undefined,
operName: undefined,
deptName: undefined,
operUrl: undefined,
operIp: undefined,
operLocation: undefined,
operParam: undefined,
jsonResult: undefined,
status: false ,
errorMsg: undefined,
operTime: undefined,
linkedSysOperLogSysDept: {
deptId:undefined, // 部门id
deptName:undefined, // 部门名称
},
}
};
//关联sys_dept表选项
const getSysDeptItemsDeptName = () => {
emit("getSysDeptItemsDeptName")
}
return {
proxy,
openDialog,
closeDialog,
onCancel,
menuRef,
formRef,
getSysDeptItemsDeptName,
...toRefs(state),
};
}
})
</script>
<style scoped>
.system-sysOperLog-detail :deep(.el-form-item--large .el-form-item__label){
font-weight: bolder;
}
.pic-block{
margin-right: 8px;
}
.file-block{
width: 100%;
border: 1px solid var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
margin-bottom: 5px;
padding: 3px 6px;
}
.ml-2{margin-right: 5px;}
</style>

View File

@@ -0,0 +1,68 @@
export interface SysOperLogTableColumns {
operId:number; // 日志编号
title:string; // 系统模块
requestMethod:string; // 请求方式
operName:string; // 操作人员
deptName:string; // 部门名称
operUrl:string; // 请求URL
operIp:string; // 主机地址
operLocation:string; // 操作地点
operParam:string; // 请求参数
status:number; // 操作状态0正常 1异常
operTime:string; // 操作时间
linkedSysOperLogSysDept:LinkedSysOperLogSysDept;
}
export interface SysOperLogInfoData {
operId:number|undefined; // 日志编号
title:string|undefined; // 系统模块
businessType:number|undefined; // 操作类型
method:string|undefined; // 操作方法
requestMethod:string|undefined; // 请求方式
operatorType:number|undefined; // 操作类别
operName:string|undefined; // 操作人员
deptName:string|undefined; // 部门名称
operUrl:string|undefined; // 请求URL
operIp:string|undefined; // 主机地址
operLocation:string|undefined; // 操作地点
operParam:string|undefined; // 请求参数
jsonResult:string|undefined; // 返回参数
status:boolean; // 操作状态0正常 1异常
errorMsg:string|undefined; // 错误消息
operTime:string|undefined; // 操作时间
linkedSysOperLogSysDept:LinkedSysOperLogSysDept;
}
export interface LinkedSysOperLogSysDept {
deptId:number|undefined; // 部门id
deptName:string|undefined; // 部门名称
}
export interface SysOperLogTableDataState {
operIds:any[];
tableData: {
data: Array<SysOperLogTableColumns>;
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
title: string|undefined;
requestMethod: string|undefined;
operName: string|undefined;
status: number|undefined;
dateRange: string[];
};
};
}
export interface SysOperLogEditState{
loading:boolean;
isShowDialog: boolean;
formData:SysOperLogInfoData;
rules: object;
}

View File

@@ -0,0 +1,361 @@
<template>
<div class="system-sysOperLog-container">
<el-card shadow="hover">
<div class="system-sysOperLog-search mb15">
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="100px">
<el-row>
<el-col :span="8" class="colBlock">
<el-form-item label="系统模块" prop="title">
<el-input
v-model="tableData.param.title"
placeholder="请输入系统模块"
clearable
size="small"
@keyup.enter.native="sysOperLogList"
/>
</el-form-item>
</el-col>
<el-col :span="8" class="colBlock">
<el-form-item label="请求方式" prop="requestMethod">
<el-select v-model="tableData.param.requestMethod" placeholder="请选择请求方式" clearable size="small">
<el-option
v-for="dict in sys_oper_log_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" :class="!showAll ? 'colBlock' : 'colNone'">
<el-form-item>
<el-button type="primary" size="small" @click="sysOperLogList"><el-icon><ele-Search /></el-icon>搜索</el-button>
<el-button size="small" @click="resetQuery(queryRef)"><el-icon><ele-Refresh /></el-icon>重置</el-button>
<el-button type="primary" link size="small" @click="toggleSearch">
{{ word }}
<el-icon v-show="showAll"><ele-ArrowUp/></el-icon>
<el-icon v-show="!showAll"><ele-ArrowDown /></el-icon>
</el-button>
</el-form-item>
</el-col>
<el-col :span="8" :class="showAll ? 'colBlock' : 'colNone'">
<el-form-item label="操作人员" prop="operName">
<el-input
v-model="tableData.param.operName"
placeholder="请输入操作人员"
clearable
size="small"
@keyup.enter.native="sysOperLogList"
/>
</el-form-item>
</el-col>
<el-col :span="8" :class="showAll ? 'colBlock' : 'colNone'">
<el-form-item label="操作时间" prop="dateRange">
<el-date-picker
clearable size="small" style="width: 200px"
v-model="tableData.param.dateRange"
type="daterange"
range-separator="-"
value-format="YYYY-MM-DD"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8" :class="showAll ? 'colBlock' : 'colNone'">
<el-form-item>
<el-button type="primary" size="small" @click="sysOperLogList"><el-icon><ele-Search /></el-icon>搜索</el-button>
<el-button size="small" @click="resetQuery(queryRef)"><el-icon><ele-Refresh /></el-icon>重置</el-button>
<el-button type="primary" link size="small" @click="toggleSearch">
{{ word }}
<el-icon v-show="showAll"><ele-ArrowUp/></el-icon>
<el-icon v-show="!showAll"><ele-ArrowDown /></el-icon>
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="danger"
size="small"
:disabled="multiple"
@click="handleDelete(null)"
v-auth="'api/v1/system/sysOperLog/delete'"
><el-icon><ele-Delete /></el-icon>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button size="small" type="danger" class="ml10" @click="onRowClear()">
<el-icon>
<ele-Delete />
</el-icon>
清空日志
</el-button>
</el-col>
</el-row>
</div>
<el-table v-loading="loading" :data="tableData.data" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="日志编号" align="center" prop="operId"
min-width="100px"
/>
<el-table-column label="系统模块" align="center" prop="title"
min-width="100px"
/>
<el-table-column label="请求方式" align="center" prop="requestMethod" :formatter="requestMethodFormat"
min-width="100px"
/>
<el-table-column label="操作人员" align="center" prop="operName"
min-width="100px"
/>
<el-table-column label="部门名称" align="center" prop="deptName"
min-width="100px"
/>
<el-table-column label="请求URL" align="center" prop="operUrl"
min-width="100px"
:show-overflow-tooltip="true"
/>
<el-table-column label="主机地址" align="center" prop="operIp"
min-width="100px"
/>
<el-table-column label="操作地点" align="center" prop="operLocation"
min-width="100px"
/>
<el-table-column label="操作时间" align="center" prop="operTime"
min-width="120px"
>
<template #default="scope">
<span>{{ proxy.parseTime(scope.row.operTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding" min-width="120px" fixed="right">
<template #default="scope">
<el-button
size="small"
type="primary"
link
icon="el-icon-view"
@click="handleView(scope.row)"
v-auth="'api/v1/system/sysOperLog/view'"
>详情</el-button>
<el-button
size="small"
type="primary"
link
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-auth="'api/v1/system/sysOperLog/delete'"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="sysOperLogList"
/>
</el-card>
<apiV1SystemSysOperLogDetail
ref="detailRef"
:requestMethodOptions="sys_oper_log_type"
:deptNameOptions="deptNameOptions"
@sysOperLogList="sysOperLogList"
></apiV1SystemSysOperLogDetail>
</div>
</template>
<script lang="ts">
import {ItemOptions} from "/@/api/items";
import {toRefs, reactive, onMounted, ref, defineComponent, computed,getCurrentInstance,toRaw} from 'vue';
import {ElMessageBox, ElMessage, FormInstance} from 'element-plus';
import {
listSysOperLog,
delSysOperLog,
clearOperLog,
} from "/@/api/system/monitor/operLog";
import {
SysOperLogTableColumns,
SysOperLogInfoData,
SysOperLogTableDataState,
} from "/@/views/system/monitor/operLog/component/model"
import apiV1SystemSysOperLogDetail from "/@/views/system/monitor/operLog/component/detail.vue"
export default defineComponent({
name: "apiV1SystemSysOperLogList",
components:{
apiV1SystemSysOperLogDetail
},
setup() {
const {proxy} = <any>getCurrentInstance()
const loading = ref(false)
const queryRef = ref()
const editRef = ref();
const detailRef = ref();
// 是否显示所有搜索选项
const showAll = ref(false)
// 非单个禁用
const single = ref(true)
// 非多个禁用
const multiple =ref(true)
const word = computed(()=>{
if(showAll.value === false) {
//对文字进行处理
return "展开搜索";
} else {
return "收起搜索";
}
})
// 字典选项数据
const {
sys_oper_log_type,
} = proxy.useDict(
'sys_oper_log_type',
)
// deptNameOptions关联表数据
const deptNameOptions = ref<Array<ItemOptions>>([])
const state = reactive<SysOperLogTableDataState>({
operIds:[],
tableData: {
data: [],
total: 0,
loading: false,
param: {
pageNum: 1,
pageSize: 10,
title: undefined,
requestMethod: undefined,
operName: undefined,
status: undefined,
dateRange: []
},
},
});
// 页面加载时
onMounted(() => {
initTableData();
});
// 初始化表格数据
const initTableData = () => {
sysOperLogList()
};
/** 重置按钮操作 */
const resetQuery = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
sysOperLogList()
};
// 获取列表数据
const sysOperLogList = ()=>{
loading.value = true
listSysOperLog(state.tableData.param).then((res:any)=>{
let list = res.data.list??[];
state.tableData.data = list;
state.tableData.total = res.data.total;
loading.value = false
})
};
const toggleSearch = () => {
showAll.value = !showAll.value;
}
// 请求方式字典翻译
const requestMethodFormat = (row:SysOperLogTableColumns) => {
return proxy.selectDictLabel(sys_oper_log_type.value, row.requestMethod);
}
// 多选框选中数据
const handleSelectionChange = (selection:Array<SysOperLogInfoData>) => {
state.operIds = selection.map(item => item.operId)
single.value = selection.length!=1
multiple.value = !selection.length
}
const handleAdd = ()=>{
editRef.value.openDialog()
}
const handleUpdate = (row: SysOperLogTableColumns) => {
if(!row){
row = state.tableData.data.find((item:SysOperLogTableColumns)=>{
return item.operId ===state.operIds[0]
}) as SysOperLogTableColumns
}
editRef.value.openDialog(toRaw(row));
};
const handleDelete = (row: SysOperLogTableColumns) => {
let msg = '你确定要删除所选数据?';
let ids:number[] = [] ;
if(row){
msg = `此操作将永久删除数据,是否继续?`
ids = [row.operId]
}else{
ids = state.operIds
}
if(ids.length===0){
ElMessage.error('请选择要删除的数据。');
return
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
delSysOperLog(ids).then(()=>{
ElMessage.success('删除成功');
sysOperLogList();
})
})
.catch(() => {});
}
// 清空日志
const onRowClear = () => {
ElMessageBox.confirm('你确定要删除所选数据?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
clearOperLog().then(()=>{
ElMessage.success('清除成功');
sysOperLogList();
})
})
.catch(() => {});
};
const handleView = (row:SysOperLogTableColumns)=>{
detailRef.value.openDialog(toRaw(row));
}
return {
proxy,
editRef,
detailRef,
showAll,
loading,
single,
multiple,
word,
queryRef,
resetQuery,
sysOperLogList,
toggleSearch,
requestMethodFormat,
sys_oper_log_type,
//关联表数据选项
deptNameOptions,
handleSelectionChange,
handleAdd,
handleUpdate,
handleDelete,
handleView,
onRowClear,
...toRefs(state),
}
}
})
</script>
<style lang="scss" scoped>
.colBlock {
display: block;
}
.colNone {
display: none;
}
</style>

View File

@@ -0,0 +1,508 @@
<template>
<div class="system-user-container">
<el-row :gutter="30">
<el-col :xs="24" :sm="12" :md="12" class="marg-b-15">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>运行资源</span>
</div>
</template>
<div class=" el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<tbody>
<tr>
<td>
<div class="cell">操作系统:</div>
</td>
<td>
<div class="cell">{{sysInfo.sysOsName}}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">系统架构:</div>
</td>
<td>
<div class="cell">{{sysInfo.sysOsArch}}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">服务器名称:</div>
</td>
<td>
<div class="cell">{{sysInfo.sysComputerName}}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">服务器IP:</div>
</td>
<td>
<div class="cell">{{sysInfo.sysComputerIp}}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">Go语言版本</div>
</td>
<td>
<div class="cell">{{sysInfo.goVersion}}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">启动时间</div>
</td>
<td>
<div class="cell">{{sysInfo.goStartTime}}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">运行时长:</div>
</td>
<td>
<div class="cell">{{timeFormat(sysInfo.goRunTime)}}</div>
</td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="12" :md="12" class="marg-b-15">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>硬盘资源</span>
</div>
</template>
<div class=" el-table--enable-row-hover el-table--medium">
<table cellspacing="0" style="width: 100%;">
<tbody>
<tr>
<td>
<div class="cell">盘符路径</div>
</td>
<td>
<div class="cell">文件系统</div>
</td>
<td>
<div class="cell">总大小</div>
</td>
<td>
<div class="cell">可用大小</div>
</td>
<td>
<div class="cell">已用大小</div>
</td>
<td>
<div class="cell">已用百分比</div>
</td>
</tr>
<tr v-for="(sysFile, index) in sysInfo.diskList" :key="index">
<td>
<div class="cell">{{ sysFile.path }}</div>
</td>
<td>
<div class="cell">{{ sysFile.fstype }}</div>
</td>
<td>
<div class="cell">{{ memorySizeFormat(sysFile.total) }}</div>
</td>
<td>
<div class="cell">{{ memorySizeFormat(sysFile.free) }}</div>
</td>
<td>
<div class="cell">{{ memorySizeFormat(sysFile.used) }}</div>
</td>
<td>
<div class="cell">{{ sysFile.usedPercent }}%</div>
</td>
</tr>
</tbody>
</table>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="12" :md="12" class="marg-b-15">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>CPU</span>
</div>
</template>
<div class=" el-table--enable-row-hover el-table--medium">
<el-row :gutter="30">
<el-col :xs="24" :sm="24" :md="12">
<table cellspacing="0" style="width: 100%;">
<tbody>
<tr>
<td>
<div class="cell">核心数:</div>
</td>
<td>
<div class="cell">{{sysInfo.cpuNum}}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">使用率:</div>
</td>
<td>
<div class="cell">{{sysInfo.cpuUsed}}%</div>
</td>
</tr>
<tr>
<td>
<div class="cell">Load Avg 5:</div>
</td>
<td>
<div class="cell">{{sysInfo.cpuAvg5}}%</div>
</td>
</tr>
<tr>
<td>
<div class="cell">Load Avg 15:</div>
</td>
<td>
<div class="cell">{{sysInfo.cpuAvg15}}%</div>
</td>
</tr>
</tbody>
</table>
</el-col>
<el-col :xs="24" :sm="24" :md="12">
<div style="min-height: 280px;" ref="chartsWarningRef1"></div>
</el-col>
</el-row>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="12" :md="12" class="marg-b-15">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>内存</span>
</div>
</template>
<div class=" el-table--enable-row-hover el-table--medium">
<el-row :gutter="30">
<el-col :xs="24" :sm="24" :md="12">
<table cellspacing="0" style="width: 100%;">
<tbody>
<tr>
<td>
<div class="cell">总数:</div>
</td>
<td>
<div class="cell">{{ memorySizeFormat(sysInfo.memTotal) }}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">已使用:</div>
</td>
<td>
<div class="cell">{{ memorySizeFormat(sysInfo.memUsed)}}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">剩余:</div>
</td>
<td>
<div class="cell">{{ memorySizeFormat(sysInfo.memFree) }}</div>
</td>
</tr>
<tr>
<td>
<div class="cell">GFast系统使用:</div>
</td>
<td>
<div class="cell">{{ memorySizeFormat(sysInfo.goUsed)}}</div>
</td>
</tr>
</tbody>
</table>
</el-col>
<el-col :xs="24" :sm="24" :md="12">
<div style="min-height: 280px;" ref="chartsWarningRef2"></div>
</el-col>
</el-row>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, getCurrentInstance, defineComponent } from 'vue';
import * as echarts from 'echarts';
import 'echarts-wordcloud';
import { getSysInfo } from "/@/api/system/monitor/server";
let interval: any = null
export default defineComponent({
name: 'monitor',
components: {},
setup() {
const { proxy } = getCurrentInstance() as any;
const state: any = reactive({
myCharts: [],
sysInfo:{}
});
let myChart1: any;
let myChart2: any;
function setOptChart1 (value:number) {
myChart1.setOption({
series: [
{
data: [{
value: value,
name: 'CPU使用率',
}],
},
],
})
}
function setOptChart2 (value:number) {
myChart2.setOption({
series: [
{
data: [{
value: value,
name: '内存使用率',
}],
},
],
})
}
//CPU
const initChartCPU = () => {
myChart1 = echarts.init(proxy.$refs.chartsWarningRef1);
const option = {
tooltip: {
formatter: '{a} <br/>{b} : {c}%',
},
series: [
{
type: 'gauge',
name: 'CPU',
radius: '80%', //修改表盘大小
title: {
'show': true, //控制表盘title(今日预计用电量)字体是否显示
'fontSize': 14, //控制表盘title(今日预计用电量)字体大小
// 'color': 'red', //控制表盘title(今日预计用电量)字体颜色
'offsetCenter': [0, '40%'], //设置表盘title(今日预计用电量)位置
},
axisLine: {
show: true,
lineStyle: { // 属性lineStyle控制线条样式
color: [
[0.3, '#4dabf7'],
[0.6, '#69db7c'],
[0.8, '#ffa94d'],
[1, '#ff6b6b'],
],
},
},
detail: {
valueAnimation: true,
formatter: '{value}%',
textStyle: {
fontSize: 36,
color: 'red',
},
offsetCenter: ['0', '80%'], //表盘数据(30%)位置
},
// data: [
// {
// value: 15,
// name: 'CPU使用率',
// },
// ],
},
],
};
myChart1.setOption(option);
state.myCharts.push(myChart1);
};
//内存
const initChartRAM = () => {
myChart2 = echarts.init(proxy.$refs.chartsWarningRef2);
const option = {
tooltip: {
formatter: '{a} <br/>{b} : {c}%',
},
series: [
{
type: 'gauge',
name: '内存',
radius: '80%', //修改表盘大小
title: {
'show': true, //控制表盘title(今日预计用电量)字体是否显示
'fontSize': 14, //控制表盘title(今日预计用电量)字体大小
// 'color': 'red', //控制表盘title(今日预计用电量)字体颜色
'offsetCenter': [0, '40%'], //设置表盘title(今日预计用电量)位置
},
axisLine: {
show: true,
lineStyle: { // 属性lineStyle控制线条样式
color: [
[0.3, '#4dabf7'],
[0.6, '#69db7c'],
[0.8, '#ffa94d'],
[1, '#ff6b6b'],
],
},
},
detail: {
valueAnimation: true,
formatter: '{value}%',
textStyle: {
fontSize: 36,
color: 'red',
},
offsetCenter: ['0', '80%'], //表盘数据(30%)位置
},
// data: [
// {
// value: 30,
// name: '内存使用率',
// },
// ],
},
],
};
myChart2.setOption(option);
state.myCharts.push(myChart2);
};
// 页面加载时
onMounted(() => {
initChartCPU();
initChartRAM();
});
function getSystemInfo() {
getSysInfo().then((res:any)=>{
const {code , data} = res
if(code === 0) {
state.sysInfo = data
setOptChart1(data.cpuUsed)
setOptChart2(data.memUsage)
}
})
}
return {
...toRefs(state),
getSystemInfo,
setOptChart1,
setOptChart2,
};
},
created() {
this.getSystemInfo()
if (interval === null) {
interval = setInterval(()=> {
this.getSystemInfo()
}, 5000)
}
},
unmounted() {
if (interval !== null) {
clearInterval(interval)
interval = null
}
},
data() {
return {}
},
methods:{
memorySizeFormat(size:any){
size = parseFloat(size);
let rank =0;
let rankchar ='Bytes';
while(size>1024&&rankchar!='TB'){
size = size/1024;
rank++;
if(rank==1){
rankchar="KB";
}
else if(rank==2){
rankchar="MB";
}
else if(rank==3){
rankchar="GB";
}else if(rank==4){
rankchar="TB";
}
}
return size.toFixed(2)+ " "+ rankchar;
},
timeFormat(second:any){
second = parseFloat(second)
let rank = 0
let rankchar = '秒'
while((second>60&&rankchar!='小时'&&rankchar!='天')||(second>24&&rankchar=='小时')){
if(rankchar=='小时'){
second = second/24;
}else{
second = second/60;
}
rank++
if(rank==1){
rankchar = '分'
}else if(rank==2){
rankchar='小时'
}else if(rank==3){
rankchar='天'
}
}
return second.toFixed(2)+" "+rankchar
}
}
});
</script>
<style scoped lang="scss">
.el-card{
height:300px;
overflow-y: auto;
}
.system-user-container {
}
.marg-b-15 {
margin-bottom: 15px;
}
.cell {
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
word-break: break-all;
line-height: 36px;
padding-left: 10px;
padding-right: 10px;
}
.box-card {
min-height: 372px;
}
</style>

View File

@@ -0,0 +1,179 @@
<template>
<div class="system-post-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-form :model="tableData.param" ref="queryRef" :inline="true">
<el-form-item label="登录IP" prop="ipaddr">
<el-input size="default" v-model="tableData.param.ipaddr" placeholder="请输入登录IP" class="w-50 m-2" clearable/>
</el-form-item>
<el-form-item label="用户名称" prop="userName">
<el-input size="default" v-model="tableData.param.userName" placeholder="请输入登录名称" class="w-50 m-2" clearable/>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="getList">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" @click="resetQuery(queryRef)">
<el-icon>
<ele-Refresh />
</el-icon>
重置
</el-button>
<el-button size="default" type="danger" class="ml10" @click="onRowDel(null)">
<el-icon>
<ele-Delete />
</el-icon>
强制退出
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="uuid" label="会话编号" show-overflow-tooltip></el-table-column>
<el-table-column prop="userName" label="登录名称"></el-table-column>
<el-table-column prop="ip" label="主机" ></el-table-column>
<el-table-column prop="explorer" label="浏览器" show-overflow-tooltip></el-table-column>
<el-table-column label="操作系统" align="center" prop="os" />
<el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">强退</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="getList"
/>
</el-card>
</div>
</template>
<script lang="ts">
import {toRefs, reactive, onMounted, defineComponent, ref} from 'vue';
import {ElMessageBox, ElMessage, FormInstance} from 'element-plus';
import { forceLogout, listSysUserOnline} from "/@/api/system/monitor/userOnline";
// 定义接口来定义对象的类型
interface TableData {
id: number;
uuid: string;
token: string;
createTime: string;
userName: string;
ip: string;
explorer: string;
os: string;
}
interface TableDataState {
ids:number[];
tableData: {
data: Array<TableData>;
total: number;
loading: boolean;
param: {
ipaddr:string;
userName:string;
pageNum: number;
pageSize: number;
};
};
}
export default defineComponent({
name: 'apiV1SystemOnlineList',
setup() {
const queryRef = ref();
const state = reactive<TableDataState>({
ids:[],
tableData: {
data: [],
total: 0,
loading: false,
param: {
ipaddr:'',
userName:'',
pageNum: 1,
pageSize: 10,
},
},
});
// 初始化表格数据
const initTableData = () => {
getList()
};
const getList = ()=>{
listSysUserOnline(state.tableData.param).then(res=>{
state.tableData.data = res.data.list??[];
state.tableData.total = res.data.total;
})
};
/** 重置按钮操作 */
const resetQuery = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
getList()
};
// 删除岗位
const onRowDel = (row: TableData) => {
let msg = '你确定要强制退出用户登录?';
let ids:number[] = [] ;
if(row){
msg = `将强制用户下线,是否继续?`
ids = [row.id]
}else{
ids = state.ids
}
if(ids.length===0){
ElMessage.error('请选择要强制退出登录的用户。');
return
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
forceLogout(ids).then(()=>{
ElMessage.success('退出成功');
getList();
})
})
.catch(() => {});
};
// 分页改变
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;
};
// 分页改变
const onHandleCurrentChange = (val: number) => {
state.tableData.param.pageNum = val;
};
// 页面加载时
onMounted(() => {
initTableData();
});
// 多选框选中数据
const handleSelectionChange = (selection:Array<TableData>)=> {
state.ids = selection.map(item => item.id)
};
return {
queryRef,
onRowDel,
onHandleSizeChange,
onHandleCurrentChange,
getList,
handleSelectionChange,
resetQuery,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,516 @@
<template>
<div class="personal">
<el-row>
<!-- 个人信息 -->
<el-col :xs="24" :sm="16">
<el-card shadow="hover" header="个人信息">
<div class="personal-user">
<div class="personal-user-left">
<el-upload
class=" h100 personal-user-left-upload avatar-uploader"
:action="baseURL+'/api/v1/system/upload/singleImg'"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:data="dataParam"
>
<img v-if="imageUrl" :src="proxy.getUpFileUrl(imageUrl)" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><ele-Plus /></el-icon>
</el-upload>
</div>
<div class="personal-user-right">
<el-row>
<el-col :span="24" class="personal-title mb18">{{ currentTime }}{{ personalForm.nickname }}{{ personalForm.describe }} </el-col>
<el-col :span="24">
<el-row>
<el-col :xs="24" :sm="8" class="personal-item mb6">
<div class="personal-item-label">昵称</div>
<div class="personal-item-value"> {{ personalForm.nickname }}</div>
</el-col>
<el-col :xs="24" :sm="16" class="personal-item mb6">
<div class="personal-item-label">联系电话</div>
<div class="personal-item-value">{{ personalForm.mobile }}</div>
</el-col>
</el-row>
</el-col>
<el-col :span="24">
<el-row>
<el-col :xs="24" :sm="8" class="personal-item mb6">
<div class="personal-item-label">登录IP</div>
<div class="personal-item-value">{{personalForm.lastLoginIp}}</div>
</el-col>
<el-col :xs="24" :sm="16" class="personal-item mb6">
<div class="personal-item-label">登录时间</div>
<div class="personal-item-value">{{personalForm.lastLoginTime}}</div>
</el-col>
</el-row>
</el-col>
<el-col :span="24">
<el-row>
<el-col :xs="24" :sm="8" class="personal-item mb6">
<div class="personal-item-label">所属部门</div>
<div class="personal-item-value">{{ deptName }}</div>
</el-col>
<el-col :xs="24" :sm="16" class="personal-item mb6">
<div class="personal-item-label">所属角色</div>
<div class="personal-item-value">{{ roles.join(',') }}</div>
</el-col>
</el-row>
</el-col>
</el-row>
</div>
</div>
</el-card>
</el-col>
<!-- 消息通知 -->
<el-col :xs="24" :sm="8" class="pl15 personal-info">
<el-card shadow="hover">
<template #header>
<span>消息通知</span>
<span class="personal-info-more">更多</span>
</template>
<div class="personal-info-box">
<ul class="personal-info-ul">
<li v-for="(v, k) in newsInfoList" :key="k" class="personal-info-li">
<a :href="v.link" target="_block" class="personal-info-li-title">{{ v.title }}</a>
</li>
</ul>
</div>
</el-card>
</el-col>
<!-- 营销推荐 -->
<el-col :span="24">
<el-card shadow="hover" class="mt15" header="营销推荐">
<el-row :gutter="15" class="personal-recommend-row">
<el-col :sm="6" v-for="(v, k) in recommendList" :key="k" class="personal-recommend-col">
<div class="personal-recommend" :style="{ 'background-color': v.bg }">
<SvgIcon :name="v.icon" :size="70" :style="{ color: v.iconColor }" />
<div class="personal-recommend-auto">
<div>{{ v.title }}</div>
<div class="personal-recommend-msg">{{ v.msg }}</div>
</div>
</div>
</el-col>
</el-row>
</el-card>
</el-col>
<!-- 更新信息 -->
<el-col :span="24">
<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
<div class="personal-edit-title">基本信息</div>
<el-form :model="personalForm" size="default" label-width="40px" class="mt35 mb35">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="昵称">
<el-input v-model="personalForm.nickname" placeholder="请输入昵称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="邮箱">
<el-input v-model="personalForm.userEmail" placeholder="请输入邮箱" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="签名">
<el-input v-model="personalForm.describe" placeholder="请输入签名" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="职业">
<el-select v-model="personalForm.remark" placeholder="请选择职业" clearable class="w100">
<el-option label="计算机 / 互联网 / 通信" value="1"></el-option>
<el-option label="生产 / 工艺 / 制造" value="2"></el-option>
<el-option label="医疗 / 护理 / 制药" value="3"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="手机">
<el-input v-model="personalForm.mobile" placeholder="请输入手机" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="性别">
<el-select v-model="personalForm.sex" placeholder="请选择性别" clearable class="w100">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item>
<el-button type="primary" @click="handleUpload">
<el-icon>
<ele-Position />
</el-icon>
更新个人信息
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="personal-edit-title mb15">账号安全</div>
<div class="personal-edit-safe-box">
<div class="personal-edit-safe-item">
<div class="personal-edit-safe-item-left">
<div class="personal-edit-safe-item-left-label">账户密码</div>
<div class="personal-edit-safe-item-left-value">当前密码强度</div>
</div>
<div class="personal-edit-safe-item-right">
<el-button text type="primary" @click="handleEditPass">立即修改</el-button>
</div>
</div>
</div>
<div class="personal-edit-safe-box">
<div class="personal-edit-safe-item">
<div class="personal-edit-safe-item-left">
<div class="personal-edit-safe-item-left-label">密保手机</div>
<div class="personal-edit-safe-item-left-value">已绑定手机132****4108</div>
</div>
<div class="personal-edit-safe-item-right">
<el-button text type="primary">立即修改</el-button>
</div>
</div>
</div>
<div class="personal-edit-safe-box">
<div class="personal-edit-safe-item">
<div class="personal-edit-safe-item-left">
<div class="personal-edit-safe-item-left-label">密保问题</div>
<div class="personal-edit-safe-item-left-value">已设置密保问题账号安全大幅度提升</div>
</div>
<div class="personal-edit-safe-item-right">
<el-button text type="primary">立即设置</el-button>
</div>
</div>
</div>
<div class="personal-edit-safe-box">
<div class="personal-edit-safe-item">
<div class="personal-edit-safe-item-left">
<div class="personal-edit-safe-item-left-label">绑定QQ</div>
<div class="personal-edit-safe-item-left-value">已绑定QQ110****566</div>
</div>
<div class="personal-edit-safe-item-right">
<el-button text type="primary">立即设置</el-button>
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, computed, defineComponent,getCurrentInstance,onMounted } from 'vue';
import { formatAxis } from '/@/utils/formatTime';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo';
import {getPersonalInfo, editPersonal, resetPwdPersonal} from "/@/api/system/personal";
import type { UploadProps } from 'element-plus'
import {ElMessage} from "element-plus";
import {ElMessageBox} from 'element-plus'
import {getToken} from "/@/utils/gfast"
import { newsInfoList, recommendList } from './mock';
import {Session} from "/@/utils/storage";
// 定义接口来定义对象的类型
interface PersonalState {
imageUrl:'',
deptName: '';
roles: [];
personalForm: any;
newsInfoList: any;
recommendList: any;
}
export default defineComponent({
name: 'personals',
setup() {
const baseURL:string|undefined|boolean = import.meta.env.VITE_API_URL
const {proxy} = <any>getCurrentInstance();
const stores = useUserInfo();
const { userInfos } = storeToRefs(stores);
const dataParam = reactive({
token:getToken(),
})
const state = reactive<PersonalState>({
newsInfoList,
recommendList,
imageUrl:'',
deptName:'',
roles:[],
personalForm: {
nickname: '',
userEmail: '',
describe: '',
mobile: '',
sex: '',
remark:'',
avatar:'',
lastLoginIp:'',
lastLoginTime:''
},
});
// const handleUpload =
const handleUpload = () => {
// console.log(state.personalForm)
editPersonal(state.personalForm).then((res:any)=>{
const userInfo = res.data.userInfo
userInfo.avatar = proxy.getUpFileUrl(userInfo.avatar)
// 存储 token 到浏览器缓存
Session.set('token', res.data.token);
// 存储用户信息到浏览器缓存
Session.set('userInfo', userInfo);
useUserInfo().setUserInfos();
ElMessage.success('已更新');
});
};
// 当前时间提示语
const currentTime = computed(() => {
return formatAxis(new Date());
});
const handleAvatarSuccess: UploadProps['onSuccess'] = (
response,
uploadFile
) => {
if(response.code == 0){
state.imageUrl = response.data.path;
state.personalForm.avatar = response.data.path;
handleUpload();
}
};
/** 重置密码按钮操作 */
const handleEditPass = ()=> {
ElMessageBox.prompt('请输入"' + state.personalForm.nickname + '"的新密码', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消"
}).then(({ value }) => {
if(!value || value==''){
ElMessage.success('密码不能为空');
return
}
resetPwdPersonal({password:value}).then(() => {
ElMessage.success("修改成功,新密码是:" + value);
});
}).catch(() => {});
};
// 初始化用户数据
const initUserInfo = () => {
getPersonalInfo().then((res:any)=>{
const user = res.data.user;
state.imageUrl = user.avatar;
state.personalForm = {
nickname:user.userNickname,
userEmail:user.userEmail,
describe: user.describe,
mobile: user.mobile,
sex: String(user.sex),
remark:user.remark,
avatar:user.avatar,
lastLoginIp:user.lastLoginIp,
lastLoginTime:user.lastLoginTime
}
state.deptName = res.data.deptName;
state.roles = res.data.roles;
})
};
// 页面加载时
onMounted(() => {
initUserInfo();
});
return {
proxy,
baseURL,
userInfos,
currentTime,
handleUpload,
handleEditPass,
handleAvatarSuccess,
dataParam,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
@use '../../../theme/mixins/index.scss' as *;
.personal {
.personal-user {
height: 130px;
display: flex;
align-items: center;
.personal-user-left {
width: 130px;
height: 130px;
border-radius: 3px;
:deep(.el-upload) {
height: 100%;
}
.avatar-uploader{
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
text-align: center;
font-size: 20px;
}
.personal-user-left-upload {
img {
width: 100%;
height: 100%;
border-radius: 3px;
}
&:hover {
img {
animation: logoAnimation 0.3s ease-in-out;
}
}
}
}
.personal-user-right {
flex: 1;
padding: 0 15px;
.personal-title {
font-size: 18px;
@include text-ellipsis(1);
}
.personal-item {
display: flex;
align-items: center;
font-size: 13px;
line-height: 26px;
.personal-item-label {
color: var(--el-text-color-secondary);
@include text-ellipsis(1);
}
.personal-item-value {
@include text-ellipsis(1);
}
}
}
}
.personal-info {
.personal-info-more {
float: right;
color: var(--el-text-color-secondary);
font-size: 13px;
&:hover {
color: var(--el-color-primary);
cursor: pointer;
}
}
.personal-info-box {
height: 130px;
overflow: hidden;
.personal-info-ul {
list-style: none;
.personal-info-li {
font-size: 13px;
padding-bottom: 10px;
.personal-info-li-title {
display: inline-block;
@include text-ellipsis(1);
color: var(--el-text-color-secondary);
text-decoration: none;
}
& a:hover {
color: var(--el-color-primary);
cursor: pointer;
}
}
}
}
}
.personal-recommend-row {
.personal-recommend-col {
.personal-recommend {
position: relative;
height: 100px;
border-radius: 3px;
overflow: hidden;
cursor: pointer;
&:hover {
i {
right: 0px !important;
bottom: 0px !important;
transition: all ease 0.3s;
}
}
i {
position: absolute;
right: -10px;
bottom: -10px;
font-size: 70px;
transform: rotate(-30deg);
transition: all ease 0.3s;
}
.personal-recommend-auto {
padding: 15px;
position: absolute;
left: 0;
top: 5%;
color: var(--next-color-white);
.personal-recommend-msg {
font-size: 12px;
margin-top: 10px;
}
}
}
}
}
.personal-edit {
.personal-edit-title {
position: relative;
padding-left: 10px;
color: var(--el-text-color-regular);
&::after {
content: '';
width: 2px;
height: 10px;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
background: var(--el-color-primary);
}
}
.personal-edit-safe-box {
border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
padding: 15px 0;
.personal-edit-safe-item {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.personal-edit-safe-item-left {
flex: 1;
overflow: hidden;
.personal-edit-safe-item-left-label {
color: var(--el-text-color-regular);
margin-bottom: 5px;
}
.personal-edit-safe-item-left-value {
color: var(--el-text-color-secondary);
@include text-ellipsis(1);
margin-right: 15px;
}
}
}
&:last-of-type {
padding-bottom: 0;
border-bottom: none;
}
}
}
}
</style>

View File

@@ -0,0 +1,66 @@
/**
* 消息通知
* @returns 返回模拟数据
*/
export const newsInfoList: Array<object> = [
{
title: '[发布] 2021年02月28日发布基于 vue3.x + vite v1.0.0 版本',
date: '02/28',
link: 'https://gitee.com/lyt-top/vue-next-admin',
},
{
title: '[发布] 2021年04月15日发布 vue2.x + webpack 重构版本',
date: '04/15',
link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
},
{
title: '[重构] 2021年04月10日 重构 vue2.x + webpack v1.0.0 版本',
date: '04/10',
link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/',
},
{
title: '[预览] 2020年12月08日基于 vue3.x 版本后台模板的预览',
date: '12/08',
link: 'http://lyt-top.gitee.io/vue-next-admin-preview/#/login',
},
{
title: '[预览] 2020年11月15日基于 vue2.x 版本后台模板的预览',
date: '11/15',
link: 'https://lyt-top.gitee.io/vue-prev-admin-preview/#/login',
},
];
/**
* 营销推荐
* @returns 返回模拟数据
*/
export const recommendList: Array<object> = [
{
title: '优惠券',
msg: '现金券、折扣券、营销必备',
icon: 'ele-Food',
bg: '#48D18D',
iconColor: '#64d89d',
},
{
title: '多人拼团',
msg: '社交电商、开辟流量',
icon: 'ele-ShoppingCart',
bg: '#F95959',
iconColor: '#F86C6B',
},
{
title: '分销中心',
msg: '轻松招募分销员,成功推广奖励',
icon: 'ele-School',
bg: '#8595F4',
iconColor: '#92A1F4',
},
{
title: '秒杀',
msg: '超低价抢购引导更多销量',
icon: 'ele-AlarmClock',
bg: '#FEBB50',
iconColor: '#FDC566',
},
];

View File

@@ -0,0 +1,183 @@
<template>
<div class="system-edit-post-container">
<el-dialog v-model="isShowDialog" width="769px">
<template #header>
<div v-drag="['.system-edit-post-container .el-dialog', '.system-edit-post-container .el-dialog__header']">{{(formData.postId===0?'添加':'修改')+'岗位'}}</div>
</template>
<el-form ref="formRef" :model="formData" :rules="rules" size="default" label-width="90px">
<el-form-item label="岗位名称" prop="postName">
<el-input v-model="formData.postName" placeholder="请输入岗位名称" />
</el-form-item>
<el-form-item label="岗位编码" prop="postCode">
<el-input v-model="formData.postCode" placeholder="请输入编码名称" />
</el-form-item>
<el-form-item label="岗位顺序" prop="postSort">
<el-input-number v-model="formData.postSort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="岗位状态" prop="status">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" inline-prompt active-text="启" inactive-text="禁"></el-switch>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">{{formData.postId===0?'新 增':'修 改'}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent,ref,unref } from 'vue';
import {addPost, editPost} from "/@/api/system/post";
import {ElMessage} from "element-plus";
interface DialogRow {
postId:number;
postCode:string;
postName:string;
postSort:number;
status:number;
remark:string;
}
interface PostState {
loading:boolean;
isShowDialog: boolean;
formData: DialogRow;
menuExpand:boolean;
menuNodeAll:boolean;
menuCheckStrictly:boolean;
menuProps: {
children: string;
label: string;
};
rules: object;
}
export default defineComponent({
name: 'systemEditPost',
setup(props,{emit}) {
const formRef = ref<HTMLElement | null>(null);
const menuRef = ref();
const state = reactive<PostState>({
loading:false,
isShowDialog: false,
formData: {
postId:0,
postCode:'',
postName:'',
postSort:0,
status:1,
remark:'',
},
// 表单校验
rules: {
postName: [
{ required: true, message: "岗位名称不能为空", trigger: "blur" }
],
postCode: [
{ required: true, message: "岗位编码不能为空", trigger: "blur" }
],
postSort: [
{ required: true, message: "岗位顺序不能为空", trigger: "blur" }
]
},
menuExpand:false,
menuNodeAll:false,
menuCheckStrictly:false,
menuProps: {
children: 'children',
label: 'title',
},
});
// 打开弹窗
const openDialog = (row?: DialogRow) => {
resetForm();
if(row) {
state.formData = row;
}
state.isShowDialog = true;
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
state.loading = true;
if(state.formData.postId===0){
//添加
addPost(state.formData).then(()=>{
ElMessage.success('岗位添加成功');
closeDialog(); // 关闭弹窗
emit('getPostList')
}).finally(()=>{
state.loading = false;
})
}else{
//修改
editPost(state.formData).then(()=>{
ElMessage.success('岗位修改成功');
closeDialog(); // 关闭弹窗
emit('getPostList')
}).finally(()=>{
state.loading = false;
})
}
}
});
};
const resetForm = ()=>{
state.menuCheckStrictly=false;
state.menuExpand = false;
state.menuNodeAll = false;
state.formData = {
postId:0,
postCode:'',
postName:'',
postSort:0,
status:1,
remark:'',
}
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
menuRef,
formRef,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7!important;
background: #fff none!important;
border-radius: 4px;
}
.system-edit-post-container {
.menu-data-tree {
border: var(--el-input-border, var(--el-border-base));
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
padding: 5px;
}
}
</style>

View File

@@ -0,0 +1,200 @@
<template>
<div class="system-post-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-form :inline="true">
<el-form-item label="岗位名称">
<el-input size="default" v-model="tableData.param.postName" placeholder="请输入岗位名称" class="w-50 m-2" clearable/>
</el-form-item>
<el-form-item label="岗位编码">
<el-input size="default" v-model="tableData.param.postCode" placeholder="请输入岗位编码" class="w-50 m-2" clearable/>
</el-form-item>
<el-form-item label="状态">
<el-select size="default" placeholder="请选择状态" class="w-50 m-2" v-model="tableData.param.status" clearable>
<el-option label="启用" value="1" />
<el-option label="禁用" value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="postList">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddPost">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增岗位
</el-button>
<el-button size="default" type="danger" class="ml10" @click="onRowDel(null)">
<el-icon>
<ele-Delete />
</el-icon>
删除岗位
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="postCode" label="岗位编码" show-overflow-tooltip></el-table-column>
<el-table-column prop="postName" label="岗位名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="postSort" label="排序" show-overflow-tooltip></el-table-column>
<el-table-column prop="status" label="岗位状态" show-overflow-tooltip>
<template #default="scope">
<el-tag type="success" v-if="scope.row.status===1">启用</el-tag>
<el-tag type="info" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="remark" label="岗位描述" show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditPost(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="postList"
/>
</el-card>
<EditPost ref="editPostRef" @getPostList="postList"/>
</div>
</template>
<script lang="ts">
import {toRefs, reactive, onMounted, ref, defineComponent, toRaw} from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import EditPost from '/@/views/system/post/component/editPost.vue';
import {deletePost, getPostList} from "/@/api/system/post";
// 定义接口来定义对象的类型
interface TableData {
postId:number;
postCode:string;
postName:string;
postSort:number;
status:number;
remark:string;
createdAt:string;
}
interface TableDataState {
ids:number[];
tableData: {
data: Array<TableData>;
total: number;
loading: boolean;
param: {
postName:string;
status:string;
postCode:string;
pageNum: number;
pageSize: number;
};
};
}
export default defineComponent({
name: 'apiV1SystemPostList',
components: {EditPost},
setup() {
const addPostRef = ref();
const editPostRef = ref();
const state = reactive<TableDataState>({
ids:[],
tableData: {
data: [],
total: 0,
loading: false,
param: {
postName:'',
status:'',
postCode:'',
pageNum: 1,
pageSize: 10,
},
},
});
// 初始化表格数据
const initTableData = () => {
postList()
};
const postList = ()=>{
getPostList(state.tableData.param).then(res=>{
state.tableData.data = res.data.postList??[];
state.tableData.total = res.data.total;
})
};
// 打开新增岗位弹窗
const onOpenAddPost = () => {
editPostRef.value.openDialog();
};
// 打开修改岗位弹窗
const onOpenEditPost = (row: Object) => {
editPostRef.value.openDialog(toRaw(row));
};
// 删除岗位
const onRowDel = (row: any) => {
let msg = '你确定要删除所选岗位?';
let ids:number[] = [] ;
if(row){
msg = `此操作将永久删除岗位:“${row.postName}”,是否继续?`
ids = [row.postId]
}else{
ids = state.ids
}
if(ids.length===0){
ElMessage.error('请选择要删除的数据。');
return
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deletePost(ids).then(()=>{
ElMessage.success('删除成功');
postList();
})
})
.catch(() => {});
};
// 分页改变
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;
};
// 分页改变
const onHandleCurrentChange = (val: number) => {
state.tableData.param.pageNum = val;
};
// 页面加载时
onMounted(() => {
initTableData();
});
// 多选框选中数据
const handleSelectionChange = (selection:Array<TableData>)=> {
state.ids = selection.map(item => item.postId)
};
return {
addPostRef,
editPostRef,
onOpenAddPost,
onOpenEditPost,
onRowDel,
onHandleSizeChange,
onHandleCurrentChange,
postList,
handleSelectionChange,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,263 @@
<template>
<div class="system-edit-role-container">
<el-dialog :title="(formData.id===0?'添加':'修改')+'角色'" v-model="isShowDialog" width="769px">
<el-form ref="formRef" :model="formData" :rules="rules" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入角色名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="排序">
<el-input-number v-model="formData.listOrder" :min="0" controls-position="right" placeholder="请输入排序" class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色状态">
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" inline-prompt active-text="启" inactive-text="禁"></el-switch>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="角色描述">
<el-input v-model="formData.remark" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="菜单权限">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event)">展开/折叠</el-checkbox>
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event)">全选/全不选</el-checkbox>
<el-checkbox v-model="menuCheckStrictly" @change="handleCheckedTreeConnect($event)">父子联动</el-checkbox>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-tree
:data="menuData"
ref="menuRef"
:props="menuProps"
:default-checked-keys="formData.menuIds"
node-key="id"
show-checkbox class="menu-data-tree tree-border"
:check-strictly="!menuCheckStrictly"/>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default" :loading="loading">{{formData.id===0?'新 增':'修 改'}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent,ref,getCurrentInstance,unref } from 'vue';
import {addRole, editRole, getRole, getRoleParams} from "/@/api/system/role";
import {ElMessage} from "element-plus";
import {refreshBackEndControlRoutes} from "/@/router/backEnd";
// 定义接口来定义对象的类型
interface MenuDataTree {
id: number;
pid:number;
title: string;
children?: MenuDataTree[];
}
interface DialogRow {
id:number;
name: string;
status: number;
listOrder: number;
remark: string;
menuIds:Array<number>
}
interface RoleState {
loading:boolean;
isShowDialog: boolean;
formData: DialogRow;
menuData: Array<MenuDataTree>;
menuExpand:boolean;
menuNodeAll:boolean;
menuCheckStrictly:boolean;
menuProps: {
children: string;
label: string;
};
rules: object;
}
export default defineComponent({
name: 'systemEditRole',
setup(props,{emit}) {
const {proxy} = getCurrentInstance() as any;
const formRef = ref<HTMLElement | null>(null);
const menuRef = ref();
const state = reactive<RoleState>({
loading:false,
isShowDialog: false,
formData: {
id:0,
name: '',
status: 1,
listOrder: 0,
remark: '',
menuIds:[]
},
// 表单校验
rules: {
name:[
{required: true, message: "角色名称不能为空", trigger: "blur"},
]
},
menuData: [],
menuExpand:false,
menuNodeAll:false,
menuCheckStrictly:false,
menuProps: {
children: 'children',
label: 'title',
},
});
// 打开弹窗
const openDialog = (row?: DialogRow) => {
resetForm();
getMenuData();
if(row) {
getRole(row.id).then((res:any)=>{
if(res.data.role){
state.formData = res.data.role;
state.formData.menuIds = res.data.menuIds??[]
}
})
}
state.isShowDialog = true;
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
state.loading = true;
state.formData.menuIds = getMenuAllCheckedKeys();
if(state.formData.id===0){
//添加
addRole(state.formData).then(()=>{
ElMessage.success('角色添加成功');
closeDialog(); // 关闭弹窗
resetMenuSession()
emit('getRoleList')
}).finally(()=>{
state.loading = false;
})
}else{
//修改
editRole(state.formData).then(()=>{
ElMessage.success('角色修改成功');
closeDialog(); // 关闭弹窗
resetMenuSession()
emit('getRoleList')
}).finally(()=>{
state.loading = false;
})
}
}
});
};
// 获取菜单结构数据
const getMenuData = () => {
getRoleParams().then((res:any)=>{
state.menuData = proxy.handleTree(res.data.menu, "id","pid");
})
};
const resetForm = ()=>{
state.menuCheckStrictly=false;
state.menuExpand = false;
state.menuNodeAll = false;
state.formData = {
id:0,
name: '',
status: 1,
listOrder: 0,
remark: '',
menuIds:[]
}
};
/** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value:any) => {
let treeList = state.menuData;
for (let i = 0; i < treeList.length; i++) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
}
/** 树权限(全选/全不选) */
const handleCheckedTreeNodeAll = (value:any) => {
menuRef.value.setCheckedNodes(value ? state.menuData : []);
}
/** 树权限(父子联动) */
const handleCheckedTreeConnect = (value:any) => {
state.menuCheckStrictly = value ? true : false;
}
/** 所有菜单节点数据 */
function getMenuAllCheckedKeys() {
// 目前被选中的菜单节点
let checkedKeys = menuRef.value.getCheckedKeys();
// 半选中的菜单节点
let halfCheckedKeys = menuRef.value.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
return checkedKeys;
}
// 重置菜单session
const resetMenuSession = () => {
refreshBackEndControlRoutes();
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
menuRef,
formRef,
handleCheckedTreeExpand,
handleCheckedTreeNodeAll,
handleCheckedTreeConnect,
resetMenuSession,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7!important;
border-radius: 4px;
}
.system-edit-role-container {
.menu-data-tree {
border: var(--el-input-border, var(--el-border-base));
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
padding: 5px;
}
}
</style>

View File

@@ -0,0 +1,186 @@
<template>
<div class="system-role-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-form :inline="true">
<el-form-item label="角色名称">
<el-input size="default" v-model="tableData.param.roleName" placeholder="请输入角色名称" class="w-50 m-2" clearable/>
</el-form-item>
<el-form-item label="状态">
<el-select size="default" placeholder="请选择状态" class="w-50 m-2" v-model="tableData.param.roleStatus" clearable>
<el-option label="启用" value="1" />
<el-option label="禁用" value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="roleList">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddRole">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增角色
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%">
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="name" label="角色名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="listOrder" label="排序" show-overflow-tooltip></el-table-column>
<el-table-column prop="status" label="角色状态" show-overflow-tooltip>
<template #default="scope">
<el-tag type="success" v-if="scope.row.status===1">启用</el-tag>
<el-tag type="info" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="remark" label="角色描述" show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="220">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditRole(scope.row)"><el-icon><ele-EditPen /></el-icon>修改</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)"><el-icon><ele-DeleteFilled /></el-icon>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="roleList"
/>
</el-card>
<EditRole ref="editRoleRef" @getRoleList="roleList"/>
</div>
</template>
<script lang="ts">
import {toRefs, reactive, onMounted, ref, defineComponent, toRaw,getCurrentInstance} from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import EditRole from '/@/views/system/role/component/editRole.vue';
import {deleteRole, getRoleList} from "/@/api/system/role";
// 定义接口来定义对象的类型
interface TableData {
id:number;
status: number;
listOrder: number;
name: string;
remark: string;
dataScope:number;
createdAt: string;
}
interface TableDataState {
tableData: {
data: Array<TableData>;
total: number;
loading: boolean;
param: {
roleName:string;
roleStatus:string;
pageNum: number;
pageSize: number;
};
};
}
export default defineComponent({
name: 'apiV1SystemRoleList',
components: {EditRole},
setup() {
const {proxy} = getCurrentInstance() as any;
const addRoleRef = ref();
const editRoleRef = ref();
const dataScopeRef =ref();
const state = reactive<TableDataState>({
tableData: {
data: [],
total: 0,
loading: false,
param: {
roleName:'',
roleStatus:'',
pageNum: 1,
pageSize: 10,
},
},
});
// 初始化表格数据
const initTableData = () => {
roleList()
};
const roleList = ()=>{
const data: Array<TableData> = [];
getRoleList(state.tableData.param).then(res=>{
const list = res.data.list??[]
list.map((item:TableData)=>{
data.push({
id:item.id,
status: item.status,
listOrder: item.listOrder,
name: item.name,
remark: item.remark,
dataScope:item.dataScope,
createdAt: item.createdAt,
});
})
state.tableData.data = data;
state.tableData.total = res.data.total;
})
};
// 打开新增角色弹窗
const onOpenAddRole = () => {
editRoleRef.value.openDialog();
};
// 打开修改角色弹窗
const onOpenEditRole = (row: Object) => {
editRoleRef.value.openDialog(toRaw(row));
};
// 删除角色
const onRowDel = (row: any) => {
ElMessageBox.confirm(`此操作将永久删除角色:“${row.name}”,是否继续?`, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteRole(row.id).then(()=>{
ElMessage.success('删除成功');
proxy.$refs['editRoleRef'].resetMenuSession();
roleList();
})
})
.catch(() => {});
};
// 分页改变
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;
};
// 分页改变
const onHandleCurrentChange = (val: number) => {
state.tableData.param.pageNum = val;
};
// 页面加载时
onMounted(() => {
initTableData();
});
return {
addRoleRef,
editRoleRef,
dataScopeRef,
onOpenAddRole,
onOpenEditRole,
onRowDel,
onHandleSizeChange,
onHandleCurrentChange,
roleList,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,285 @@
<template>
<div class="system-edit-user-container">
<el-dialog :title="(ruleForm.userId!==0?'修改':'添加')+'用户'" v-model="isShowDialog" width="769px">
<el-form ref="formRef" :model="ruleForm" :rules="rules" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20" v-if="ruleForm.userId===0">
<el-form-item label="用户名" prop="userName">
<el-input v-model="ruleForm.userName" placeholder="请输入账户名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20" v-if="ruleForm.userId===0">
<el-form-item label="账户密码" prop="password">
<el-input v-model="ruleForm.password" placeholder="请输入" type="password" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="ruleForm.nickName" placeholder="请输入用户昵称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="关联角色" prop="roleIds">
<el-select v-model="ruleForm.roleIds" placeholder="请选择" clearable class="w100" multiple>
<el-option
v-for="role in roleList"
:key="'role-'+role.id"
:label="role.name"
:value="role.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="部门" prop="deptId">
<el-cascader
:options="deptData"
:props="{ checkStrictly: true,emitPath: false, value: 'deptId', label: 'deptName' }"
placeholder="请选择部门"
clearable
class="w100"
v-model="ruleForm.deptId"
>
<template #default="{ node, data }">
<span>{{ data.deptName }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="手机号" prop="mobile">
<el-input v-model="ruleForm.mobile" placeholder="请输入手机号" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="邮箱" prop="email">
<el-input v-model="ruleForm.email" placeholder="请输入" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="性别" prop="sex">
<el-select v-model="ruleForm.sex" placeholder="请选择" clearable class="w100">
<el-option
v-for="gender in genderData"
:key="'gender-'+gender.value"
:label="gender.label"
:value="gender.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="岗位" prop="postIds">
<el-select v-model="ruleForm.postIds" placeholder="请选择" clearable class="w100" multiple>
<el-option
v-for="post in postList"
:key="'post-'+post.postId"
:label="post.postName"
:value="post.postId">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="用户状态">
<el-switch v-model="ruleForm.status" inline-prompt :active-value="1" :inactive-value="0" active-text="启" inactive-text="禁"></el-switch>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="用户类型">
<el-radio-group v-model="ruleForm.isAdmin">
<el-radio
:label="1"
>后台管理员</el-radio>
<el-radio
:label="0"
>前台用户</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="用户描述">
<el-input v-model="ruleForm.remark" type="textarea" placeholder="请输入用户描述" maxlength="150"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ruleForm.userId!==0?'修 改':'添 加'}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, onMounted, defineComponent,ref,unref } from 'vue';
import {getParams, addUser, editUser, getEditUser} from "/@/api/system/user";
import {ElMessage} from "element-plus";
export default defineComponent({
name: 'systemEditUser',
props:{
deptData:{
type:Array,
default:()=>[]
},
genderData:{
type:Array,
default:()=>[]
}
},
setup(prop,{emit}) {
const roleList = ref([]);
const postList = ref([]);
const formRef = ref<HTMLElement | null>(null);
const state = reactive({
isShowDialog: false,
ruleForm: {
userId: 0,
deptId: 0,
userName: '',
nickName: '',
password: '',
mobile:'',
email: '',
sex: '',
status: 1,
remark: '',
postIds: [],
roleIds: [],
isAdmin:0,
},
//表单校验
rules: {
userName: [
{ required: true, message: "用户名称不能为空", trigger: "blur" }
],
nickName: [
{ required: true, message: "用户昵称不能为空", trigger: "blur" }
],
deptId: [
{ required: true, message: "归属部门不能为空", trigger: "blur" }
],
password: [
{ required: true, message: "用户密码不能为空", trigger: "blur" }
],
email: [
{
type: "email",
message: "'请输入正确的邮箱地址",
trigger: ["blur", "change"]
}
],
mobile: [
{ required: true, message: "手机号码不能为空", trigger: "blur" },
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur"
}
]
}
});
// 打开弹窗
const openDialog = (row?:any) => {
resetForm()
if(row) {
getEditUser(row.id).then((res:any)=>{
const user = res.data.user;
state.ruleForm = {
userId: user.id,
deptId: user.deptId,
userName: user.userName,
nickName: user.userNickname,
password: '-',
mobile:user.mobile,
email: user.userEmail,
sex: String(user.sex),
status: user.userStatus,
remark: user.remark,
postIds: res.data.checkedPosts??[],
roleIds: res.data.checkedRoleIds??[],
isAdmin:user.isAdmin,
};
})
}
state.isShowDialog = true;
};
// 关闭弹窗
const closeDialog = () => {
state.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 新增
const onSubmit = () => {
const formWrap = unref(formRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
if(state.ruleForm.userId===0){
//添加
addUser(state.ruleForm).then(()=>{
ElMessage.success('用户添加成功');
closeDialog(); // 关闭弹窗
emit('getUserList')
});
}else{
//修改
editUser(state.ruleForm).then(()=>{
ElMessage.success('用户修改成功');
closeDialog(); // 关闭弹窗
emit('getUserList')
});
}
}
});
};
// 初始化部门数据
const initTableData = () => {
//获取角色岗位选项
getParams().then((res:any)=>{
roleList.value = res.data.roleList??[];
postList.value = res.data.posts??[];
});
};
// 页面加载时
onMounted(() => {
initTableData();
});
const resetForm = ()=>{
state.ruleForm = {
userId: 0,
deptId: 0,
userName: '',
nickName: '',
password: '',
mobile:'',
email: '',
sex: '',
status: 1,
remark: '',
postIds: [],
roleIds: [],
isAdmin:0,
}
};
return {
openDialog,
closeDialog,
onCancel,
onSubmit,
roleList,
postList,
formRef,
...toRefs(state),
};
},
});
</script>

View File

@@ -0,0 +1,362 @@
<template>
<div class="system-user-container">
<el-row :gutter="10" style="width: 100%;">
<el-col :span="4">
<el-card shadow="hover">
<el-aside>
<el-scrollbar>
<el-input :prefix-icon="search" v-model="filterText" placeholder="请输入部门名称" clearable size="default" style="width: 80%;"/>
<el-tree
ref="treeRef"
class="filter-tree"
:data="deptData"
:props="deptProps"
default-expand-all
:filter-node-method="deptFilterNode"
@node-click="handleNodeClick"
/>
</el-scrollbar>
</el-aside>
</el-card>
</el-col>
<el-col :span="20">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-form :model="tableData.param" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="关键字" prop="keyWords">
<el-input
v-model="tableData.param.keyWords"
placeholder="请输入用户账号或姓名"
clearable
size="default"
style="width: 240px"
@keyup.enter.native="userList"
/>
</el-form-item>
<el-form-item label="手机号码" prop="mobile">
<el-input
v-model="tableData.param.mobile"
placeholder="请输入手机号码"
clearable
size="default"
style="width: 240px"
@keyup.enter.native="userList"
/>
</el-form-item>
<el-form-item label="状态" prop="status" style="width: 200px;">
<el-select
v-model="tableData.param.status"
placeholder="用户状态"
clearable
size="default"
style="width: 240px"
>
<el-option label="启用" :value="1"/>
<el-option label="禁用" :value="0"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="dateRange">
<el-date-picker
v-model="tableData.param.dateRange"
size="default"
style="width: 240px"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button size="default" type="primary" class="ml10" @click="userList">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="default" @click="resetQuery(queryRef)">
<el-icon>
<ele-Refresh />
</el-icon>
重置
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddUser">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增用户
</el-button>
<el-button size="default" type="danger" class="ml10" @click="onRowDel(null)">
<el-icon>
<ele-Delete />
</el-icon>
删除用户
</el-button>
</el-form-item>
</el-form>
</div>
<el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="userName" label="账户名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="userNickname" label="用户昵称" show-overflow-tooltip></el-table-column>
<el-table-column prop="dept.deptName" label="部门" show-overflow-tooltip></el-table-column>
<el-table-column label="角色" align="center" prop="roleInfo" :show-overflow-tooltip="true" >
<template #default="scope">
<span v-for="(item,index) of scope.row.roleInfo" :key="'role-'+index"> {{item.name+' '}} </span>
</template>
</el-table-column>
<el-table-column prop="mobile" label="手机号" show-overflow-tooltip></el-table-column>
<el-table-column prop="userStatus" label="用户状态" show-overflow-tooltip>
<template #default="scope">
<el-switch
v-model="scope.row.userStatus"
inline-prompt
:active-value="1"
:inactive-value="0"
active-text=""
inactive-text=""
@change="handleStatusChange(scope.row)">
</el-switch>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditUser(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
<el-button size="small" text type="primary" @click="handleResetPwd(scope.row)">重置</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="tableData.total>0"
:total="tableData.total"
v-model:page="tableData.param.pageNum"
v-model:limit="tableData.param.pageSize"
@pagination="userList"
/>
</el-card>
</el-col>
</el-row>
<EditUser ref="editUserRef" :dept-data="deptData" :gender-data="sys_user_sex" @getUserList="userList"/>
</div>
</template>
<script lang="ts">
import {toRefs, reactive, onMounted, ref, defineComponent, watch, getCurrentInstance} from 'vue';
import {ElMessageBox, ElMessage, ElTree,FormInstance} from 'element-plus';
import { Search } from '@element-plus/icons-vue'
import EditUser from '/@/views/system/user/component/editUser.vue';
import {getUserList, getDeptTree, resetUserPwd, changeUserStatus, deleteUser} from '/@/api/system/user/index';
interface TableDataState {
ids:number[];
deptProps:{};
deptData:any[];
tableData: {
data: any[];
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
deptId:string;
mobile:string;
status:string;
keyWords:string;
dateRange: string[];
};
};
}
export default defineComponent({
name: 'systemUser',
components: { EditUser },
setup() {
const {proxy} = <any>getCurrentInstance();
const {sys_user_sex} = proxy.useDict('sys_user_sex')
const editUserRef = ref();
const queryRef = ref();
const filterText = ref('');
const treeRef = ref<InstanceType<typeof ElTree>>();
const search = Search
const state = reactive<TableDataState>({
ids:[],
deptProps:{
id:"deptId",
children: "children",
label: "deptName"
},
deptData:[
{
label: '集团总部',
children: [
{
label: '曲靖分部',
children: [
{
label: '总经办',
},
{
label: '市场部',
},
{
label: '研发部',
},
],
},
],
},
],
tableData: {
data: [],
total: 0,
loading: false,
param: {
pageNum: 1,
pageSize: 10,
deptId:'',
mobile:'',
status:'',
keyWords:'',
dateRange:[]
},
},
});
// 初始化表格数据
const initTableData = () => {
getDeptTree().then((res:any)=>{
state.deptData = res.data.deps
})
userList();
};
const userList = ()=>{
getUserList(state.tableData.param).then((res:any)=>{
state.tableData.data = res.data.userList??[];
state.tableData.total = res.data.total;
});
};
// 打开新增用户弹窗
const onOpenAddUser = () => {
editUserRef.value.openDialog();
};
// 打开修改用户弹窗
const onOpenEditUser = (row:any) => {
editUserRef.value.openDialog(row);
};
// 删除用户
const onRowDel = (row:any) => {
let msg = '你确定要删除所选用户?';
let ids:number[] = [] ;
if(row){
msg = `此操作将永久删除用户:“${row.userName}”,是否继续?`
ids = [row.id]
}else{
ids = state.ids
}
if(ids.length===0){
ElMessage.error('请选择要删除的数据。');
return
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteUser(ids).then(()=>{
ElMessage.success('删除成功');
userList();
})
})
.catch(() => {});
};
// 分页改变
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;
};
// 分页改变
const onHandleCurrentChange = (val: number) => {
state.tableData.param.pageNum = val;
};
// 页面加载时
onMounted(() => {
initTableData();
});
watch(filterText, (val) => {
treeRef.value!.filter(val)
});
const deptFilterNode = (value: string, data:any) => {
if (!value) return true;
return data.deptName.includes(value)
};
// 多选框选中数据
const handleSelectionChange = (selection:any[])=> {
state.ids = selection.map(item => item.id)
};
// 节点单击事件
const handleNodeClick = (data:any) => {
state.tableData.param.deptId = data.deptId;
userList();
};
/** 重置密码按钮操作 */
const handleResetPwd = (row:any)=> {
ElMessageBox.prompt('请输入"' + row.userName + '"的新密码', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消"
}).then(({ value }) => {
if(!value || value==''){
ElMessage.success('密码不能为空');
return
}
resetUserPwd(row.id, value).then(() => {
ElMessage.success("修改成功,新密码是:" + value);
});
}).catch(() => {});
};
// 用户状态修改
const handleStatusChange = (row:any)=> {
let text = row.userStatus === 1 ? "启用" : "停用";
ElMessageBox.confirm('确认要"' + text + '""' + row.userName + '"用户吗?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return changeUserStatus(row.id, row.userStatus);
}).then(() => {
ElMessage.success(text + "成功");
}).catch(function() {
row.userStatus =row.userStatus === 0 ?1 : 0;
});
};
/** 重置按钮操作 */
const resetQuery = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
userList()
};
return {
queryRef,
editUserRef,
onOpenAddUser,
onOpenEditUser,
onRowDel,
onHandleSizeChange,
onHandleCurrentChange,
deptFilterNode,
filterText,
treeRef,
search,
sys_user_sex,
userList,
handleSelectionChange,
handleNodeClick,
handleResetPwd,
handleStatusChange,
resetQuery,
...toRefs(state),
};
},
});
</script>