角色管理
This commit is contained in:
parent
d5789311da
commit
9f07ceda4a
|
|
@ -1,9 +1,7 @@
|
|||
const files = import.meta.glob('./model/*.ts', {
|
||||
eager: true,
|
||||
import: 'default' // 可选,指定要导入的 export
|
||||
})
|
||||
const modules = {}
|
||||
for (const path in files) {
|
||||
modules[path.replace(/(\.\/model\/|\.ts)/g, '')] = files[path]
|
||||
import auth from "@/api/model/auth"
|
||||
import system from "@/api/model/system"
|
||||
|
||||
export default {
|
||||
auth,
|
||||
system
|
||||
}
|
||||
export default modules
|
||||
|
|
|
|||
|
|
@ -20,5 +20,39 @@ export default {
|
|||
quick: async function(data = {}){
|
||||
return await http.post("menu/quick", data);
|
||||
},
|
||||
}
|
||||
},
|
||||
dept: {
|
||||
list: async function (data = {}) {
|
||||
return await http.get("dept/list", data);
|
||||
},
|
||||
add: async function (data = {}) {
|
||||
return await http.post("dept/add", data);
|
||||
},
|
||||
edit: async function (data = {}) {
|
||||
return await http.put("dept/edit", data);
|
||||
},
|
||||
del: async function (data = {}) {
|
||||
return await http.delete("dept/del", data);
|
||||
},
|
||||
option: async function (data = {}) {
|
||||
return await http.get("dept/option", data);
|
||||
},
|
||||
},
|
||||
role: {
|
||||
list: async function (data = {}) {
|
||||
return await http.get("role/list", data);
|
||||
},
|
||||
add: async function (data = {}) {
|
||||
return await http.post("role/add", data);
|
||||
},
|
||||
edit: async function (data = {}) {
|
||||
return await http.put("role/edit", data);
|
||||
},
|
||||
del: async function (data = {}) {
|
||||
return await http.delete("role/del", data);
|
||||
},
|
||||
option: async function (data = {}) {
|
||||
return await http.get("role/option", data);
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg t="1750659054773" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10383" width="256" height="256"><path d="M298.666667 554.666667l-4.992 0.298666A42.666667 42.666667 0 0 0 256 597.333333v85.333334h42.666667a42.666667 42.666667 0 0 1 42.368 37.674666L341.333333 725.333333v170.666667a42.666667 42.666667 0 0 1-42.666666 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667v-170.666667a42.666667 42.666667 0 0 1 42.666667-42.666666h42.666667v-85.333334l0.213333-7.509333A128 128 0 0 1 298.666667 469.333333h170.666666V341.333333H341.333333a42.666667 42.666667 0 0 1-42.368-37.674666L298.666667 298.666667V128a42.666667 42.666667 0 0 1 42.666666-42.666667h341.333334a42.666667 42.666667 0 0 1 42.666666 42.666667v170.666667a42.666667 42.666667 0 0 1-42.666666 42.666666h-128v128h170.666666a128 128 0 0 1 127.786667 120.490667L853.333333 597.333333v85.333334h42.666667a42.666667 42.666667 0 0 1 42.368 37.674666L938.666667 725.333333v170.666667a42.666667 42.666667 0 0 1-42.666667 42.666667h-170.666667a42.666667 42.666667 0 0 1-42.666666-42.666667v-170.666667a42.666667 42.666667 0 0 1 42.666666-42.666666h42.666667v-85.333334a42.666667 42.666667 0 0 0-37.674667-42.368L725.333333 554.666667H298.666667z" p-id="10384"></path></svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg t="1750659265544" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13130" width="256" height="256"><path d="M844.8 236.2H712.7V109.4c0-23.9-19.4-43.3-43.3-43.3h-316c-23.9 0-43.3 19.4-43.3 43.3v126.8H178c-62.1 0-112.6 50.5-112.6 112.6v498.4c0 62.1 50.5 112.6 112.6 112.6h666.8c62.1 0 112.6-50.5 112.6-112.6V348.8c0-62.1-50.5-112.6-112.6-112.6zM362 118.1h298.7v118.1H362V118.1zM178 288.2h666.9c33.4 0 60.6 27.2 60.6 60.6v136.6H117.4V348.8c0-33.4 27.1-60.6 60.6-60.6zM612.1 545c0 55.5-45.2 100.7-100.7 100.7S410.7 600.6 410.7 545c0-2.5 0.1-5.1 0.3-7.6h200.8c0.2 2.5 0.3 5.1 0.3 7.6z m232.7 362.9H178c-33.4 0-60.6-27.2-60.6-60.6V537.4H359c-0.1 2.5-0.2 5.1-0.2 7.6 0 84.2 68.5 152.7 152.7 152.7S664.1 629.2 664.1 545c0-2.5-0.1-5.1-0.2-7.6h241.6v309.9c-0.1 33.4-27.3 60.6-60.7 60.6z" p-id="13131"></path></svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg t="1750661439662" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8602" width="256" height="256"><path d="M512 87.04c-136.704 0-247.808 111.104-247.808 247.808 0 98.816 58.368 184.32 142.336 223.744-155.136 45.568-268.8 188.928-268.8 358.912 0 10.752 8.704 19.456 19.456 19.456 10.752 0 19.456-8.704 19.456-19.456 0-184.832 150.528-335.36 335.36-335.36 136.704 0 247.808-111.104 247.808-247.808S648.704 87.04 512 87.04z m0 456.192c-115.2 0-208.384-93.696-208.384-208.384 0-115.2 93.696-208.384 208.384-208.384 115.2 0 208.384 93.696 208.384 208.384 0 114.688-93.184 208.384-208.384 208.384z m226.816 76.8c-8.704-6.656-20.992-5.12-27.648 3.584-6.656 8.704-5.12 20.992 3.584 27.648 83.968 64 132.096 161.28 132.096 266.752 0 10.752 8.704 19.456 19.456 19.456 10.752 0 19.456-8.704 19.456-19.456 0.512-118.272-53.248-226.816-146.944-297.984z" p-id="8603"></path></svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { h, resolveComponent } from 'vue'
|
||||
|
||||
export default {
|
||||
render() {
|
||||
return h (
|
||||
resolveComponent("el-table-column"),
|
||||
{
|
||||
index: this.index,
|
||||
...this.$attrs
|
||||
},
|
||||
this.$slots
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
index(index){
|
||||
if(this.$attrs.type=="index"){
|
||||
let page = this.$parent.$parent.currentPage
|
||||
let pageSize = this.$parent.$parent.pageSize
|
||||
return (page - 1) * pageSize + index + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<div v-if="usercolumn.length>0" class="setting-column" v-loading="isSave">
|
||||
<div class="setting-column__title">
|
||||
<span class="move_b"></span>
|
||||
<span class="show_b">显示</span>
|
||||
<span class="name_b">名称</span>
|
||||
<span class="width_b">宽度</span>
|
||||
<span class="sortable_b">排序</span>
|
||||
<span class="fixed_b">固定</span>
|
||||
</div>
|
||||
<div class="setting-column__list" ref="list">
|
||||
<ul>
|
||||
<li v-for="item in usercolumn" :key="item.prop">
|
||||
<span class="move_b">
|
||||
<el-tag class="move" style="cursor: move;"><el-icon-d-caret style="width: 1em; height: 1em;"/></el-tag>
|
||||
</span>
|
||||
<span class="show_b">
|
||||
<el-switch v-model="item.hide" :active-value="false" :inactive-value="true"></el-switch>
|
||||
</span>
|
||||
<span class="name_b" :title="item.prop">{{ item.label }}</span>
|
||||
<span class="width_b">
|
||||
<el-input v-model="item.width" placeholder="auto" size="small"></el-input>
|
||||
</span>
|
||||
<span class="sortable_b">
|
||||
<el-switch v-model="item.sortable"></el-switch>
|
||||
</span>
|
||||
<span class="fixed_b">
|
||||
<el-switch v-model="item.fixed"></el-switch>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-column__bottom">
|
||||
<el-button @click="backDefaul" :disabled="isSave">重置</el-button>
|
||||
<el-button @click="save" type="primary">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无可配置的列" :image-size="80"></el-empty>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Sortable
|
||||
},
|
||||
props: {
|
||||
column: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isSave: false,
|
||||
usercolumn: JSON.parse(JSON.stringify(this.column||[]))
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
usercolumn: {
|
||||
handler(){
|
||||
this.$emit('userChange', this.usercolumn)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.usercolumn.length>0 && this.rowDrop()
|
||||
},
|
||||
methods: {
|
||||
rowDrop(){
|
||||
const _this = this
|
||||
const tbody = this.$refs.list.querySelector('ul')
|
||||
Sortable.create(tbody, {
|
||||
handle: ".move",
|
||||
animation: 300,
|
||||
ghostClass: "ghost",
|
||||
onEnd({ newIndex, oldIndex }) {
|
||||
const tableData = _this.usercolumn
|
||||
const currRow = tableData.splice(oldIndex, 1)[0]
|
||||
tableData.splice(newIndex, 0, currRow)
|
||||
}
|
||||
})
|
||||
},
|
||||
backDefaul(){
|
||||
this.$emit('back', this.usercolumn)
|
||||
},
|
||||
save(){
|
||||
this.$emit('save', this.usercolumn)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.setting-column {}
|
||||
|
||||
.setting-column__title {border-bottom: 1px solid #EBEEF5;padding-bottom:15px;}
|
||||
.setting-column__title span {display: inline-block;font-weight: bold;color: #909399;font-size: 12px;}
|
||||
.setting-column__title span.move_b {width: 30px;margin-right:15px;}
|
||||
.setting-column__title span.show_b {width: 60px;}
|
||||
.setting-column__title span.name_b {width: 140px;}
|
||||
.setting-column__title span.width_b {width: 60px;margin-right:15px;}
|
||||
.setting-column__title span.sortable_b {width: 60px;}
|
||||
.setting-column__title span.fixed_b {width: 60px;}
|
||||
|
||||
.setting-column__list {max-height:314px;overflow: auto;}
|
||||
.setting-column__list li {list-style: none;margin:10px 0;display: flex;align-items: center;}
|
||||
.setting-column__list li>span {display: inline-block;font-size: 12px;}
|
||||
.setting-column__list li span.move_b {width: 30px;margin-right:15px;}
|
||||
.setting-column__list li span.show_b {width: 60px;}
|
||||
.setting-column__list li span.name_b {width: 140px;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;cursor:default;}
|
||||
.setting-column__list li span.width_b {width: 60px;margin-right:15px;}
|
||||
.setting-column__list li span.sortable_b {width: 60px;}
|
||||
.setting-column__list li span.fixed_b {width: 60px;}
|
||||
.setting-column__list li.ghost {opacity: 0.3;}
|
||||
|
||||
.setting-column__bottom {border-top: 1px solid #EBEEF5;padding-top:15px;text-align: right;}
|
||||
|
||||
.dark .setting-column__title {border-color: var(--el-border-color-light);}
|
||||
.dark .setting-column__bottom {border-color: var(--el-border-color-light);}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,472 @@
|
|||
<template>
|
||||
<el-container>
|
||||
<el-header>
|
||||
<div class="left-panel">
|
||||
<slot name="do"></slot>
|
||||
</div>
|
||||
<div class="right-panel">
|
||||
<slot name="search"></slot>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main class="nopadding">
|
||||
<div :style="{'height':_height}" v-loading="loading">
|
||||
<div class="pi-table" :style="{'height':_table_height}">
|
||||
<el-table v-bind="$attrs" :data="tableData" :row-key="rowKey" :key="toggleIndex" ref="piTableRef"
|
||||
:height="height=='auto'?null:'100%'" :size="_config.size" :border="_config.border"
|
||||
:stripe="_config.stripe" :summary-method="remoteSummary?remoteSummaryMethod:summaryMethod"
|
||||
@sort-change="sortChange" @filter-change="filterChange">
|
||||
<slot></slot>
|
||||
<template v-for="(item, index) in userColumn" :key="index">
|
||||
<el-table-column v-if="!item.hide" :column-key="item.prop" :label="item.label"
|
||||
:prop="item.prop" :width="item.width" :sortable="item.sortable"
|
||||
:fixed="item.fixed" :filters="item.filters"
|
||||
:filter-method="remoteFilter||!item.filters?null:filterHandler"
|
||||
:show-overflow-tooltip="item.showOverflowTooltip">
|
||||
<template #default="scope">
|
||||
<slot :name="item.prop" v-bind="scope">
|
||||
{{ scope.row[item.prop] }}
|
||||
</slot>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<el-table-column min-width="1"></el-table-column>
|
||||
<template #empty>
|
||||
<el-empty :description="emptyText" :image-size="100"></el-empty>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="pi-table-page" v-if="!hidePagination || !hideDo">
|
||||
<div class="pi-table-pagination">
|
||||
<el-pagination v-if="!hidePagination" background size="small" :layout="paginationLayout"
|
||||
:total="total" :page-size="piPageSize" :page-sizes="pageSizes"
|
||||
v-model:currentPage="currentPage" @current-change="paginationChange"
|
||||
@update:page-size="pageSizeChange"></el-pagination>
|
||||
</div>
|
||||
<div class="pi-table-do" v-if="!hideDo">
|
||||
<el-button v-if="!hideRefresh" @click="refresh" icon="el-icon-refresh" circle
|
||||
style="margin-left:15px"></el-button>
|
||||
<el-popover v-if="column" placement="top" title="列设置" :width="500" trigger="click"
|
||||
:hide-after="0" @show="customColumnShow=true" @after-leave="customColumnShow=false">
|
||||
<template #reference>
|
||||
<el-button icon="el-icon-set-up" circle style="margin-left:15px"></el-button>
|
||||
</template>
|
||||
<columnSetting v-if="customColumnShow" ref="columnSettingRef" @userChange="columnSettingChange"
|
||||
@save="columnSettingSave" @back="columnSettingBack"
|
||||
:column="userColumn"></columnSetting>
|
||||
</el-popover>
|
||||
<el-popover v-if="!hideSetting" placement="top" title="表格设置" :width="400" trigger="click"
|
||||
:hide-after="0">
|
||||
<template #reference>
|
||||
<el-button icon="el-icon-setting" circle style="margin-left:15px"></el-button>
|
||||
</template>
|
||||
<el-form label-width="80px" label-position="left">
|
||||
<el-form-item label="表格尺寸">
|
||||
<el-radio-group v-model="_config.size" size="small" @change="configSizeChange">
|
||||
<el-radio-button value="large">大</el-radio-button>
|
||||
<el-radio-button value="default">正常</el-radio-button>
|
||||
<el-radio-button value="small">小</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="样式">
|
||||
<el-checkbox v-model="_config.border" label="纵向边框"></el-checkbox>
|
||||
<el-checkbox v-model="_config.stripe" label="斑马纹"></el-checkbox>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import columnSetting from './columnSetting'
|
||||
import config from "@/config/table"
|
||||
import {ref, watch, computed, onMounted, onActivated, onDeactivated, getCurrentInstance} from "vue"
|
||||
import tools from "@/utils/tools.js";
|
||||
|
||||
defineOptions({
|
||||
name: 'piTable'
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
refresh,
|
||||
upData,
|
||||
reload
|
||||
})
|
||||
|
||||
const {proxy} = getCurrentInstance()
|
||||
const emit = defineEmits(['dataChange'])
|
||||
|
||||
const props = defineProps({
|
||||
tableName: {type: String, default: ""},
|
||||
apiObj: {
|
||||
type: Function, default: () => {}
|
||||
},
|
||||
workbench: {type: Boolean, default: false},
|
||||
params: {type: Object, default: () => ({})},
|
||||
data: {
|
||||
type: Object, default: () => {}
|
||||
},
|
||||
height: {type: [String, Number], default: "100%"},
|
||||
size: {type: String, default: "default"},
|
||||
border: {type: Boolean, default: false},
|
||||
stripe: {type: Boolean, default: false},
|
||||
pageSize: {type: Number, default: config.pageSize},
|
||||
pageSizes: {type: Array, default: config.pageSizes},
|
||||
rowKey: {type: String, default: ""},
|
||||
summaryMethod: {type: Function, default: null},
|
||||
column: {
|
||||
type: Object, default: () => {}
|
||||
},
|
||||
remoteSort: {type: Boolean, default: false},
|
||||
remoteFilter: {type: Boolean, default: false},
|
||||
remoteSummary: {type: Boolean, default: false},
|
||||
hidePagination: {type: Boolean, default: false},
|
||||
hideDo: {type: Boolean, default: false},
|
||||
hideRefresh: {type: Boolean, default: false},
|
||||
hideSetting: {type: Boolean, default: false},
|
||||
paginationLayout: {type: String, default: config.paginationLayout},
|
||||
})
|
||||
|
||||
const piTableRef = ref(null)
|
||||
const columnSettingRef = ref(null)
|
||||
|
||||
let piPageSize = ref(props.pageSize)
|
||||
let isActive = ref(true)
|
||||
let emptyText = ref("暂无数据")
|
||||
let toggleIndex = ref(0)
|
||||
let tableData = ref([])
|
||||
let total = ref(0)
|
||||
let currentPage = ref(0)
|
||||
let prop = ref(null)
|
||||
let order = ref(null)
|
||||
let loading = ref(false)
|
||||
let tableHeight = ref('100%')
|
||||
let tableParams = ref(props.params)
|
||||
let userColumn = ref([])
|
||||
let customColumnShow = ref(false)
|
||||
let summary = ref({})
|
||||
let _config = ref({
|
||||
size: props.size,
|
||||
border: props.border,
|
||||
stripe: props.stripe
|
||||
})
|
||||
let nowWork = ref(null)
|
||||
|
||||
watch(() => props.data, () => {
|
||||
tableData.value = props.data;
|
||||
total.value = tableData.value.length;
|
||||
})
|
||||
|
||||
watch(() => props.apiObj, () => {
|
||||
tableParams.value = props.params;
|
||||
refresh();
|
||||
})
|
||||
|
||||
const _height = computed(() => {
|
||||
return Number(props.height) ? Number(props.height) + 'px' : props.height
|
||||
})
|
||||
|
||||
const _table_height = computed(() => {
|
||||
return props.hidePagination && props.hideDo ? "100%" : "calc(100% - 50px)"
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
//判断是否开启自定义列
|
||||
if (props.column) {
|
||||
getCustomColumn()
|
||||
} else {
|
||||
userColumn = props.column
|
||||
}
|
||||
//判断是否静态数据
|
||||
if (props.apiObj) {
|
||||
getData()
|
||||
} else if (data.value) {
|
||||
tableData.value = data.value
|
||||
total.value = tableData.value.length
|
||||
}
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
if (!isActive.value) {
|
||||
piTableRef.value.doLayout()
|
||||
}
|
||||
})
|
||||
|
||||
onDeactivated(() => {
|
||||
isActive.value = false
|
||||
})
|
||||
|
||||
async function getCustomColumn() {
|
||||
userColumn.value = await config.columnSettingGet(props.tableName, props.column)
|
||||
}
|
||||
|
||||
async function getData() {
|
||||
loading.value = true;
|
||||
var reqData = {
|
||||
[config.request.page]: currentPage.value,
|
||||
[config.request.pageSize]: piPageSize.value,
|
||||
[config.request.prop]: prop.value,
|
||||
[config.request.order]: order.value
|
||||
}
|
||||
if (props.hidePagination) {
|
||||
delete reqData[config.request.page]
|
||||
delete reqData[config.request.pageSize]
|
||||
}
|
||||
Object.assign(reqData, tableParams.value)
|
||||
try {
|
||||
var res = await props.apiObj(reqData)
|
||||
} catch (error) {
|
||||
loading.value = false;
|
||||
emptyText.value = error.statusText;
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
var response = config.parseData(res);
|
||||
} catch (error) {
|
||||
loading.value = false;
|
||||
emptyText.value = "数据格式错误";
|
||||
return false;
|
||||
}
|
||||
if (response.code !== config.successCode) {
|
||||
loading.value = false;
|
||||
emptyText.value = response.msg;
|
||||
} else {
|
||||
emptyText.value = "暂无数据";
|
||||
if (props.hidePagination) {
|
||||
tableData.value = response.data || [];
|
||||
} else {
|
||||
tableData.value = response.rows || [];
|
||||
}
|
||||
if (props.rowKey) {
|
||||
tableData.value = tools.makeTreeData(tableData.value, 0, props.rowKey)
|
||||
}
|
||||
total.value = response.total || 0;
|
||||
summary.value = response.summary || {};
|
||||
loading.value = false;
|
||||
}
|
||||
piTableRef.value.setScrollTop(0)
|
||||
emit('dataChange', res, tableData.value)
|
||||
}
|
||||
|
||||
//分页点击
|
||||
function paginationChange(){
|
||||
getData();
|
||||
}
|
||||
|
||||
//条数变化
|
||||
function pageSizeChange(size){
|
||||
piPageSize.value = size
|
||||
getData()
|
||||
}
|
||||
|
||||
//刷新数据
|
||||
function refresh(){
|
||||
piTableRef.value.clearSelection()
|
||||
getData()
|
||||
}
|
||||
//更新数据 合并上一次params
|
||||
function upData(params, page=1){
|
||||
currentPage.value = page;
|
||||
piTableRef.value.clearSelection();
|
||||
Object.assign(tableParams.value, params || {})
|
||||
getData()
|
||||
}
|
||||
//重载数据 替换params
|
||||
function reload(params, page=1){
|
||||
currentPage.value = page;
|
||||
tableParams.value = params || {}
|
||||
piTableRef.value.clearSelection();
|
||||
piTableRef.value.clearSort()
|
||||
piTableRef.value.clearFilter()
|
||||
getData()
|
||||
}
|
||||
//自定义变化事件
|
||||
function columnSettingChange(column){
|
||||
userColumn.value = column;
|
||||
toggleIndex.value += 1;
|
||||
}
|
||||
//自定义列保存
|
||||
async function columnSettingSave(column){
|
||||
columnSettingRef.value.isSave = true
|
||||
try {
|
||||
await config.columnSettingSave(props.tableName, column)
|
||||
}catch(error){
|
||||
proxy.$message.error('保存失败')
|
||||
columnSettingRef.value.isSave = false
|
||||
}
|
||||
proxy.$message.success('保存成功')
|
||||
columnSettingRef.value.isSave = false
|
||||
}
|
||||
//自定义列重置
|
||||
async function columnSettingBack(){
|
||||
columnSettingRef.value.isSave = true
|
||||
try {
|
||||
const column = await config.columnSettingReset(props.tableName, props.column)
|
||||
userColumn.value = column
|
||||
columnSettingRef.value.usercolumn = tools.objCopy(userColumn.value)
|
||||
}catch(error){
|
||||
proxy.$message.error('重置失败')
|
||||
columnSettingRef.value.isSave = false
|
||||
}
|
||||
columnSettingRef.value.isSave = false
|
||||
}
|
||||
//排序事件
|
||||
function sortChange(obj){
|
||||
if(!proxy.remoteSort){
|
||||
return false
|
||||
}
|
||||
if(obj.column && obj.prop){
|
||||
prop.value = obj.prop
|
||||
order.value = obj.order
|
||||
}else{
|
||||
prop.value = null
|
||||
order.value = null
|
||||
}
|
||||
getData()
|
||||
}
|
||||
//本地过滤
|
||||
function filterHandler(value, row, column){
|
||||
const property = column.property;
|
||||
return row[property] === value;
|
||||
}
|
||||
//过滤事件
|
||||
function filterChange(filters){
|
||||
if(!props.remoteFilter){
|
||||
return false
|
||||
}
|
||||
Object.keys(filters).forEach(key => {
|
||||
filters[key] = filters[key].join(',')
|
||||
})
|
||||
upData(filters)
|
||||
}
|
||||
//远程合计行处理
|
||||
function remoteSummaryMethod(param){
|
||||
const {columns} = param
|
||||
const sums = []
|
||||
columns.forEach((column, index) => {
|
||||
if(index === 0) {
|
||||
sums[index] = '合计'
|
||||
return
|
||||
}
|
||||
const values = summary.value[column.property]
|
||||
if(values){
|
||||
sums[index] = values
|
||||
}else{
|
||||
sums[index] = ''
|
||||
}
|
||||
})
|
||||
return sums
|
||||
}
|
||||
function configSizeChange(){
|
||||
piTableRef.value.doLayout()
|
||||
}
|
||||
//插入行 unshiftRow
|
||||
function unshiftRow(row){
|
||||
tableData.value.unshift(row)
|
||||
}
|
||||
//插入行 pushRow
|
||||
function pushRow(row){
|
||||
tableData.value.push(row)
|
||||
}
|
||||
//根据key覆盖数据
|
||||
function updateKey(row, rowKey=props.rowKey){
|
||||
tableData.value.filter(item => item[rowKey]===row[rowKey] ).forEach(item => {
|
||||
Object.assign(item, row)
|
||||
})
|
||||
}
|
||||
//根据index覆盖数据
|
||||
function updateIndex(row, index){
|
||||
Object.assign(tableData.value[index], row)
|
||||
}
|
||||
//根据index删除
|
||||
function removeIndex(index){
|
||||
tableData.value.splice(index, 1)
|
||||
}
|
||||
//根据index批量删除
|
||||
function removeIndexes(indexes=[]){
|
||||
indexes.forEach(index => {
|
||||
tableData.value.splice(index, 1)
|
||||
})
|
||||
}
|
||||
//根据key删除
|
||||
function removeKey(key, rowKey=props.rowKey){
|
||||
tableData.value.splice(tableData.value.findIndex(item => item[rowKey]===key), 1)
|
||||
}
|
||||
//根据keys批量删除
|
||||
function removeKeys(keys=[], rowKey=props.rowKey){
|
||||
keys.forEach(key => {
|
||||
tableData.value.splice(tableData.value.findIndex(item => item[rowKey]===key), 1)
|
||||
})
|
||||
}
|
||||
//原生方法转发
|
||||
function clearSelection(){
|
||||
piTableRef.value.clearSelection()
|
||||
}
|
||||
function toggleRowSelection(row, selected){
|
||||
piTableRef.value.toggleRowSelection(row, selected)
|
||||
}
|
||||
|
||||
|
||||
function toggleAllSelection(){
|
||||
piTableRef.value.toggleAllSelection()
|
||||
}
|
||||
|
||||
function toggleRowExpansion(row, expanded){
|
||||
piTableRef.value.toggleRowExpansion(row, expanded)
|
||||
}
|
||||
|
||||
function setCurrentRow(row){
|
||||
piTableRef.value.setCurrentRow(row)
|
||||
}
|
||||
|
||||
function clearSort(){
|
||||
piTableRef.value.clearSort()
|
||||
}
|
||||
|
||||
function clearFilter(columnKey){
|
||||
piTableRef.value.clearFilter(columnKey)
|
||||
}
|
||||
|
||||
function doLayout(){
|
||||
piTableRef.value.doLayout()
|
||||
}
|
||||
|
||||
function sort(prop, order){
|
||||
piTableRef.value.sort(prop, order)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pi-table {
|
||||
height: calc(100% - 50px);
|
||||
}
|
||||
|
||||
.pi-table-page {
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.pi-table-do {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pi-table:deep(.el-table__footer) .cell {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pi-table:deep(.el-table__body-wrapper) .el-scrollbar__bar.is-horizontal {
|
||||
height: 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.pi-table:deep(.el-table__body-wrapper) .el-scrollbar__bar.is-vertical {
|
||||
width: 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import tools from '@/utils/tools'
|
||||
|
||||
export default {
|
||||
successCode: 0, //请求完成代码
|
||||
pageSize: 20, //表格每一页条数
|
||||
pageSizes: [10, 20, 30, 40, 50], //表格可设置的一页条数
|
||||
paginationLayout: "total, sizes, prev, pager, next, jumper", //表格分页布局,可设置"total, sizes, prev, pager, next, jumper"
|
||||
parseData: function (res) { //数据分析
|
||||
return {
|
||||
data: res.data, //分析无分页的数据字段结构
|
||||
rows: res.data, //分析行数据字段结构
|
||||
total: res.count, //分析总数字段结构
|
||||
summary: res.summary, //分析合计行字段结构
|
||||
msg: res.msg, //分析描述字段结构
|
||||
code: res.code //分析状态字段结构
|
||||
}
|
||||
},
|
||||
request: { //请求规定字段
|
||||
page: 'page', //规定当前分页字段
|
||||
pageSize: 'limit', //规定一页条数字段
|
||||
prop: 'prop', //规定排序字段名字段
|
||||
order: 'order' //规定排序规格字段
|
||||
},
|
||||
/**
|
||||
* 自定义列保存处理
|
||||
* @tableName scTable组件的props->tableName
|
||||
* @column 用户配置好的列
|
||||
*/
|
||||
columnSettingSave: function (tableName, column) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(()=>{
|
||||
//这里为了演示使用了session和setTimeout演示,开发时应用数据请求
|
||||
tools.data.set(tableName, column)
|
||||
resolve(true)
|
||||
},1000)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取自定义列
|
||||
* @tableName scTable组件的props->tableName
|
||||
* @column 组件接受到的props->column
|
||||
*/
|
||||
columnSettingGet: function (tableName, column) {
|
||||
return new Promise((resolve) => {
|
||||
//这里为了演示使用了session和setTimeout演示,开发时应用数据请求
|
||||
const userColumn = tools.data.get(tableName)
|
||||
if(userColumn){
|
||||
resolve(userColumn)
|
||||
}else{
|
||||
resolve(column)
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 重置自定义列
|
||||
* @tableName scTable组件的props->tableName
|
||||
* @column 组件接受到的props->column
|
||||
*/
|
||||
columnSettingReset: function (tableName, column) {
|
||||
return new Promise((resolve) => {
|
||||
//这里为了演示使用了session和setTimeout演示,开发时应用数据请求
|
||||
setTimeout(()=>{
|
||||
tools.data.remove(tableName)
|
||||
resolve(column)
|
||||
},1000)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -100,16 +100,16 @@
|
|||
defineOptions({
|
||||
name: "userBar"
|
||||
})
|
||||
import {ref, onMounted, getCurrentInstance} from "vue";
|
||||
import {ref, onMounted, getCurrentInstance} from "vue"
|
||||
import search from './search.vue'
|
||||
import setting from './setting.vue'
|
||||
import tasks from './tasks.vue'
|
||||
import websocket from "@/utils/websocket";
|
||||
import {ElNotification} from 'element-plus';
|
||||
import tools from "@/utils/tools.js";
|
||||
import api from "@/api/index.js";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import websocket from "@/utils/websocket"
|
||||
import {ElNotification} from 'element-plus'
|
||||
import tools from "@/utils/tools"
|
||||
import api from "@/api/index"
|
||||
import {useRouter} from "vue-router"
|
||||
import {useI18n} from "vue-i18n"
|
||||
|
||||
const {proxy} = getCurrentInstance()
|
||||
const router = useRouter()
|
||||
|
|
|
|||
|
|
@ -10,11 +10,15 @@ import drag from './directives/drag'
|
|||
import errorHandler from "@/utils/errorHandler";
|
||||
|
||||
import piDialog from "@/components/piDialog"
|
||||
import piTable from "@/components/piTable"
|
||||
import piPage from "@/components/piPage"
|
||||
|
||||
export default {
|
||||
install(app: App) {
|
||||
// 注册全局组件
|
||||
app.component('piDialog', piDialog)
|
||||
app.component('piTable', piTable)
|
||||
app.component('piPage', piPage)
|
||||
|
||||
//注册全局指令
|
||||
app.directive('auth', auth)
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ const tools = {
|
|||
},
|
||||
/* 复制对象 */
|
||||
objCopy: function (obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
return JSON.parse(JSON.stringify(obj || []));
|
||||
},
|
||||
/* 日期格式化 */
|
||||
dateFormat: function (date, fmt='yyyy-MM-dd hh:mm:ss') {
|
||||
|
|
@ -128,6 +128,21 @@ const tools = {
|
|||
}
|
||||
}
|
||||
return fmt;
|
||||
},
|
||||
makeTreeData: function (data, pid = 0, key = "id") {
|
||||
const arr = [];
|
||||
for (let item of data) {
|
||||
if(item.pid == pid){
|
||||
// 数据格式处理
|
||||
const tmp = item;
|
||||
const children = tools.makeTreeData(data, item[key], key);
|
||||
if (children.length > 0) {
|
||||
tmp['children'] = children
|
||||
}
|
||||
arr.push(tmp)
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ async function delMenu() {
|
|||
}
|
||||
menuloading.value = true
|
||||
var ids = CheckedNodes.map(item => item.menu_id)
|
||||
var res = await api.system.menu.del({menu_id: ids.toString()})
|
||||
var res = await api.system.menu.del({ids: ids.toString()})
|
||||
menuloading.value = false
|
||||
proxy.$message.success(res.msg)
|
||||
CheckedNodes.forEach(item => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
<template>
|
||||
<pi-table ref="tableRef" :apiObj="api.system.role.list" @selection-change="selectionChange" stripe>
|
||||
<template #do>
|
||||
<el-button v-auth="'role:add'" type="primary" icon="el-icon-plus" @click="add"></el-button>
|
||||
<el-button v-auth="'role:del'" type="danger" plain icon="el-icon-delete" :disabled="selection.length==0" @click="batch_del"></el-button>
|
||||
</template>
|
||||
<template #search>
|
||||
<el-input v-model="search.role_name" placeholder="角色名称" clearable style="width: 200px;"></el-input>
|
||||
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
|
||||
</template>
|
||||
<el-table-column type="selection" width="50"></el-table-column>
|
||||
<el-table-column label="#" type="index" width="50"></el-table-column>
|
||||
<el-table-column label="名称" prop="role_name"></el-table-column>
|
||||
<el-table-column label="状态" prop="status">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status===1" type="success">启用</el-tag>
|
||||
<el-tag v-if="scope.row.status===0" type="danger">停用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="rank"></el-table-column>
|
||||
<el-table-column label="创建时间" prop="create_time"></el-table-column>
|
||||
<el-table-column label="操作" fixed="right" align="right" width="170">
|
||||
<template #default="scope">
|
||||
<el-button-group>
|
||||
<el-button text type="primary" size="small" @click="table_show(scope.row, scope.$index)">查看</el-button>
|
||||
<el-button v-auth="'role:edit'" text type="primary" size="small" @click="table_edit(scope.row, scope.$index)">编辑</el-button>
|
||||
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
|
||||
<template #reference>
|
||||
<el-button v-auth="'role:del'" text type="primary" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</pi-table>
|
||||
<save-dialog v-if="dialogShow" ref="saveRef" @success="tableRef.refresh()" @closed="dialogShow = false"></save-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import saveDialog from './save'
|
||||
import {getCurrentInstance, nextTick, ref} from "vue";
|
||||
import api from "@/api/index";
|
||||
|
||||
defineOptions({
|
||||
name: "systemRole"
|
||||
})
|
||||
|
||||
const {proxy} = getCurrentInstance()
|
||||
const tableRef = ref(null)
|
||||
const saveRef = ref(null)
|
||||
|
||||
let search = ref({
|
||||
real_name: ''
|
||||
})
|
||||
let selection = ref([])
|
||||
let dialogShow = ref(false)
|
||||
|
||||
//添加
|
||||
function add() {
|
||||
dialogShow.value = true
|
||||
nextTick(() => {
|
||||
saveRef.value.open()
|
||||
})
|
||||
}
|
||||
//编辑
|
||||
function table_edit(row){
|
||||
dialogShow.value = true
|
||||
nextTick(() => {
|
||||
saveRef.value.open('edit')
|
||||
saveRef.value.setData(row)
|
||||
})
|
||||
}
|
||||
//查看
|
||||
function table_show(row){
|
||||
dialogShow.value = true
|
||||
nextTick(() => {
|
||||
saveRef.value.open('show')
|
||||
saveRef.value.setData(row)
|
||||
})
|
||||
}
|
||||
//删除
|
||||
async function table_del(row){
|
||||
const loading = proxy.$loading();
|
||||
var res = await api.system.role.del({ids: row.role_id});
|
||||
tableRef.value.refresh()
|
||||
loading.close();
|
||||
proxy.$message.success(res.msg)
|
||||
}
|
||||
//批量删除
|
||||
async function batch_del(){
|
||||
proxy.$confirm(`确定删除选中的 ${selection.value.length} 项吗?如果删除项中含有子集将会被一并删除`, '提示', {
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
const loading = proxy.$loading();
|
||||
const res = await api.system.role.del({ids: selection.value.map(item => item.role_id).toString()});
|
||||
tableRef.value.refresh()
|
||||
loading.close();
|
||||
proxy.$message.success(res.msg)
|
||||
})
|
||||
}
|
||||
//表格选择后回调事件
|
||||
function selectionChange(val){
|
||||
selection.value = val;
|
||||
}
|
||||
//搜索
|
||||
function upsearch(){
|
||||
tableRef.value.upData(search.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<pi-dialog :title="titleMap[mode]" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
|
||||
<el-form :model="form" :rules="rules" :disabled="mode==='show'" ref="formRef" label-width="100px"
|
||||
label-position="right">
|
||||
<el-form-item label="名称" prop="role_name">
|
||||
<el-input v-model="form.role_name" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="权限" prop="alias">
|
||||
<el-tree style="width: 100%;" ref="menuRef" node-key="menu_id" :data="menu.list"
|
||||
:default-checked-keys="menu.checked"
|
||||
:props="menu.props" show-checkbox></el-tree>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="rank">
|
||||
<el-input-number v-model="form.rank" controls-position="right" :min="1"
|
||||
style="width: 100%;"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否有效" prop="status">
|
||||
<el-switch v-model="form.status" :active-value="1" :inactive-value="0"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="visible=false">取 消</el-button>
|
||||
<el-button v-if="mode!=='show'" type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
|
||||
</template>
|
||||
</pi-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, onMounted, getCurrentInstance} from "vue"
|
||||
import tools from "@/utils/tools"
|
||||
import api from "@/api/index"
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
setData
|
||||
})
|
||||
const emit = defineEmits(['success', 'closed'])
|
||||
|
||||
const formRef = ref(null)
|
||||
const menuRef = ref(null)
|
||||
const {proxy} = getCurrentInstance()
|
||||
|
||||
let mode = ref('add')
|
||||
let titleMap = ref({
|
||||
add: '新增',
|
||||
edit: '编辑',
|
||||
show: '查看'
|
||||
})
|
||||
let visible = ref(false)
|
||||
let isSaveing = ref(false)
|
||||
let form = ref({
|
||||
role_id: "",
|
||||
role_name: "",
|
||||
menus: [],
|
||||
checked_menus: "",
|
||||
rank: 1,
|
||||
status: 1
|
||||
})
|
||||
let menu = ref({
|
||||
list: [],
|
||||
props: {
|
||||
label: (data) => {
|
||||
return data.title
|
||||
}
|
||||
},
|
||||
checked: []
|
||||
})
|
||||
let rules = ref({
|
||||
rank: [
|
||||
{required: true, message: '请输入排序', trigger: 'change'}
|
||||
],
|
||||
role_name: [
|
||||
{required: true, message: '请输入角色名称'}
|
||||
]
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getMenu()
|
||||
})
|
||||
|
||||
async function getMenu() {
|
||||
const res = await api.system.menu.list();
|
||||
menu.value.list = tools.makeTreeData(res.data, 0, "menu_id");
|
||||
}
|
||||
|
||||
//显示
|
||||
function open(m = 'add') {
|
||||
mode.value = m
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
//表单提交方法
|
||||
async function submit() {
|
||||
let checked = menuRef.value.getCheckedNodes().map(item => item.menu_id);
|
||||
let allChecked = checked.concat(menuRef.value.getHalfCheckedNodes().map(item => item.menu_id))
|
||||
if (allChecked.length <= 0) return proxy.$message.error("请至少权限")
|
||||
form.value.menus = allChecked
|
||||
form.value.checked_menus = checked.toString()
|
||||
// 校验登录
|
||||
const validate = await formRef.value.validate().catch(() => {
|
||||
});
|
||||
if (!validate) {
|
||||
return false
|
||||
}
|
||||
isSaveing.value = true;
|
||||
const res = form.value.role_id ? await api.system.role.edit(form.value) : await api.system.role.add(form.value);
|
||||
isSaveing.value = false;
|
||||
emit('success', form.value, mode.value)
|
||||
visible.value = false;
|
||||
proxy.$message.success(res.msg)
|
||||
}
|
||||
|
||||
//表单注入数据
|
||||
function setData(data) {
|
||||
Object.assign(form.value, data)
|
||||
if (data.checked_menus) {
|
||||
menu.value.checked = data.checked_menus.split(",")
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<el-main>
|
||||
<el-row :gutter="20">
|
||||
<el-col :lg="8" :xs="24" :sm="24" :md="24">
|
||||
<el-card shadow="never" class="pi-left">
|
||||
<el-avatar :src="userInfo.avatar" :size="64">{{nicknameF}}</el-avatar>
|
||||
<p style="font-size: 20px;font-weight: bold;margin: 8px;">{{userInfo.nickname||userInfo.username}}</p>
|
||||
<p>{{userInfo.bio}}</p>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="16" :xs="24" :sm="24" :md="24">
|
||||
<el-card shadow="never">
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-main>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import tools from "@/utils/tools"
|
||||
import api from "@/api/index.js";
|
||||
import {ref, onMounted} from "vue";
|
||||
|
||||
defineOptions({
|
||||
name: "systemUser"
|
||||
})
|
||||
|
||||
let userInfo = ref(tools.data.get("USER_INFO"));
|
||||
|
||||
let nickname = userInfo.value.nickname || userInfo.value.username;
|
||||
let nicknameF = nickname.substring(0, 1);
|
||||
|
||||
onMounted(() => {
|
||||
loadUser()
|
||||
})
|
||||
|
||||
async function loadUser() {
|
||||
const res = await api.auth.info()
|
||||
userInfo.value = res.data
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pi-left {text-align: center;}
|
||||
</style>
|
||||
Loading…
Reference in New Issue