@@ -27,7 +27,7 @@ export function getMenu(params) { | |||
* 添加菜单 | |||
* params | |||
*/ | |||
export function addMenu(data) { | |||
export function menuCreate(data) { | |||
return request({ | |||
url: '/menu/add', | |||
method: 'POST', | |||
@@ -39,7 +39,30 @@ export function addMenu(data) { | |||
* 编辑菜单 | |||
* params | |||
*/ | |||
export function editMenu(data) { | |||
export function menuUpdate(data) { | |||
return request({ | |||
url: '/menu/edit', | |||
method: 'PUT', | |||
data | |||
}) | |||
} | |||
/** | |||
* 添加按钮 | |||
* params | |||
*/ | |||
export function permissionCreate(data) { | |||
return request({ | |||
url: '/menu/add', | |||
method: 'POST', | |||
data | |||
}) | |||
} | |||
/** | |||
* 编辑按钮 | |||
* params | |||
*/ | |||
export function permissionUpdate(data) { | |||
return request({ | |||
url: '/menu/edit', | |||
method: 'PUT', | |||
@@ -57,3 +80,49 @@ export function deleteMenu(id) { | |||
method: 'DELETE' | |||
}) | |||
} | |||
/** | |||
* 添加按钮 | |||
* params | |||
*/ | |||
export function mpMenuCreate(data) { | |||
return request({ | |||
url: '/menu/add', | |||
method: 'POST', | |||
data | |||
}) | |||
} | |||
/** | |||
* 编辑菜单 | |||
* params | |||
*/ | |||
export function mpMenuUpdate(data) { | |||
return request({ | |||
url: '/menu/edit', | |||
method: 'PUT', | |||
data | |||
}) | |||
} | |||
/** | |||
* 删除菜单 | |||
* params | |||
*/ | |||
export function deleteMpMenu(id) { | |||
return request({ | |||
url: `/menu/delete/${id}`, | |||
method: 'DELETE' | |||
}) | |||
} | |||
/** | |||
* 删除按钮 | |||
* params | |||
*/ | |||
export function deletePermission(ids) { | |||
return request({ | |||
url: `/menu/delete/${ids}`, | |||
method: 'DELETE' | |||
}) | |||
} |
@@ -27,7 +27,7 @@ export function getRoleAll(params) { | |||
* 添加角色 | |||
* params | |||
*/ | |||
export function addRole(data) { | |||
export function roleCreate(data) { | |||
return request({ | |||
url: '/role/add', | |||
method: 'POST', | |||
@@ -39,7 +39,7 @@ export function addRole(data) { | |||
* 编辑角色 | |||
* params | |||
*/ | |||
export function editRole(data) { | |||
export function roleUpdate(data) { | |||
return request({ | |||
url: '/role/edit', | |||
method: 'PUT', | |||
@@ -63,12 +63,34 @@ export function setRoleStatus(data) { | |||
* 删除角色 | |||
* params | |||
*/ | |||
export function deleteRole(data) { | |||
export function roleDelete(data) { | |||
return request({ | |||
url: `/role/delete/${data}`, | |||
method: 'DELETE' | |||
}) | |||
} | |||
/** | |||
* 获取角色菜单 | |||
* params | |||
*/ | |||
export function getRoleMenu(params) { | |||
return request({ | |||
url: `/role/getMenuList`, | |||
method: 'GET', | |||
params | |||
}) | |||
} | |||
/** | |||
* 保存角色选中的菜单 | |||
* params | |||
*/ | |||
export function saveRoleMenu(data) { | |||
return request({ | |||
url: `/role/savePermission`, | |||
method: 'POST', | |||
data | |||
}) | |||
} | |||
/** | |||
* 获取角色权限数据 |
@@ -21,6 +21,7 @@ | |||
<script> | |||
import { defineComponent, computed, toRaw, reactive } from 'vue' | |||
import { usePermissionStore } from '@/store/modules/permission' | |||
export default defineComponent({ | |||
name: 'TableAction', | |||
props: { | |||
@@ -42,17 +43,24 @@ export default defineComponent({ | |||
} | |||
}, | |||
setup(props, { emit }) { | |||
const permissionStore = usePermissionStore() | |||
const data = reactive({ | |||
permissionList: [ | |||
'basic_list' | |||
] | |||
permissionList: permissionStore.accessPermissionCodes | |||
}) | |||
const getPermissionLabel = (code) => { | |||
return permissionStore.accessPermissions.find((item) => { | |||
return item.code === code | |||
})?.name || '' | |||
} | |||
const getActions = computed(() => { | |||
return (toRaw(props.actions) || []) | |||
.filter((action) => { | |||
if (!Object.keys(action).includes('show')) { | |||
action.show = Object.keys(action).includes('hidden') ? !action.hidden : true | |||
} | |||
action.label = action.label || getPermissionLabel(action.auth) | |||
return (data.permissionList.includes(action.auth) || action.auth === '') && action.show | |||
}) | |||
}) |
@@ -5,17 +5,16 @@ | |||
<span class="sp">欢迎回来, {{ getUserInfo.realname }}</span> | |||
</div> | |||
<n-dropdown v-if="getUserInfo.hasLogin" trigger="hover" :options="options" @select="handleSelect"> | |||
<n-dropdown trigger="hover" :options="options" @select="handleSelect"> | |||
<div class="user_msg"> | |||
<!-- <n-image | |||
<n-image | |||
class="user_avatar" | |||
:src="getUserInfo.avatar" | |||
preview-disabled | |||
/> --> | |||
/> | |||
<span class="user_name">{{ getUserInfo.realname }}</span> | |||
</div> | |||
</n-dropdown> | |||
<div v-if="!getUserInfo.hasLogin" class="header__login" @click="handleLogin">登录</div> | |||
</n-layout-header> | |||
</template> | |||
@@ -1,15 +1,17 @@ | |||
<template> | |||
<n-menu | |||
:mode="menuMode" | |||
:value="(currentRoute.title && currentRoute.meta.activeMenu) || currentRoute.title" | |||
:value="currentRoute.title ?? currentRoute.meta.title" | |||
:options="getMenuOptions" | |||
:default-expanded-keys="defaultExpandedKeys" | |||
:watch-props="['defaultExpandedKeys']" | |||
@update:value="handleMenuSelect" | |||
/> | |||
</template> | |||
<script setup> | |||
import { useRouter } from 'vue-router' | |||
import { computed, defineProps, toRaw } from 'vue' | |||
import { useRouter, useRoute } from 'vue-router' | |||
import { computed, defineProps, toRaw, ref } from 'vue' | |||
import { isExternal } from '@/utils/is.js' | |||
import { usePermissionStore } from '@/store/modules/permission' | |||
@@ -20,6 +22,18 @@ const props = defineProps({ | |||
} | |||
}) | |||
// 刷新展开相应路由 | |||
const route = useRoute() | |||
const defaultExpandedKeys = ref([]) | |||
const menuInstRef = ref(null) | |||
setTimeout(() => { | |||
if (route.meta.isHidden) { // 没有菜单的路由 | |||
defaultExpandedKeys.value = [route.matched[0]?.meta?.title] // 展开 父级菜单 | |||
} else { // 有菜单的路由 | |||
menuInstRef.value?.showOption(route.meta?.title) // 选中该菜单 | |||
} | |||
}) | |||
const menuMode = toRaw(props.menuMode) | |||
const router = useRouter() | |||
@@ -47,7 +61,8 @@ function generateOptions(routes, basePath) { | |||
const curOption = { | |||
label: (route.meta && route.meta.title) || route.title, | |||
key: route.title, | |||
path: resolvePath(basePath, route.path) | |||
path: resolvePath(basePath, route.path), | |||
icons: route.icons ?? '' | |||
} | |||
if (route.children && route.children.length && !route.meta?.hideChild) { | |||
curOption.children = generateOptions(route.children, resolvePath(basePath, route.path)) |
@@ -20,7 +20,6 @@ export function createPermissionGuard(router) { | |||
return item.clientId === VITE_CLIENT_ID | |||
}) | |||
const routes = await permissionStore.generateRoutes(roleId) | |||
console.log(routes, '==================') | |||
routes.forEach((item) => { | |||
router.addRoute(item) | |||
}) |
@@ -124,7 +124,9 @@ function dealPermissions(permissionsList) { | |||
export const usePermissionStore = defineStore('permission', { | |||
state() { | |||
return { | |||
accessRoutes: [] | |||
accessRoutes: [], | |||
accessPermissions: [], | |||
accessPermissionCodes: [] | |||
} | |||
}, | |||
getters: { | |||
@@ -133,6 +135,12 @@ export const usePermissionStore = defineStore('permission', { | |||
}, | |||
permissionRoutes() { | |||
return this.accessRoutes | |||
}, | |||
validatePermission() { | |||
return this.accessPermissions | |||
}, | |||
validatePermissionCode() { | |||
return this.accessPermissionCodes | |||
} | |||
}, | |||
actions: { | |||
@@ -144,6 +152,7 @@ export const usePermissionStore = defineStore('permission', { | |||
async generateRoutes(roleId) { | |||
try { | |||
const res = await fetchPermission({ roleId, clientId: 'tuoheng-pilot-admin' }) | |||
// const res = await fetchPermission(3) | |||
if (res.code === 0) { | |||
const { opMenusList, permissionsList } = res.data | |||
const menus = dealRoutes(opMenusList) | |||
@@ -152,6 +161,9 @@ export const usePermissionStore = defineStore('permission', { | |||
this.accessPermissions = permissionsList | |||
this.accessPermissionCodes = permissionCodes | |||
return Promise.resolve(menus) | |||
// const accessRoutes = filterAsyncRoutes(asyncRoutes) | |||
// this.accessRoutes = accessRoutes | |||
// return Promise.resolve(accessRoutes) | |||
} else { | |||
return Promise.reject(res.message) | |||
} |
@@ -60,6 +60,19 @@ export const FILE_TYPE = [ | |||
{ label: '御2', value: 2 }, | |||
{ label: '御3E', value: 3 } | |||
] | |||
/** | |||
* 角色状态 | |||
*/ | |||
export const ROLE_STATUS = [ | |||
{ | |||
label: '正常', | |||
value: 1 | |||
}, | |||
{ | |||
label: '禁用', | |||
value: 0 | |||
} | |||
] | |||
/** | |||
* 状态 |
@@ -1,9 +1,9 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-close="handleClose" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
@@ -19,31 +19,32 @@ | |||
<template v-for="(item,index) in getFormOptions" :key="index"> | |||
<n-gi> | |||
<n-form-item :label="item.label" :path="item.key"> | |||
<n-input v-if="item.type === 'input'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
<n-select v-if="item.type === 'select'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
<n-input-number v-if="item.type === 'number'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
<n-tree-select v-if="item.type === 'tree-select'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
<n-radio-group v-if="item.type === 'radio'" v-model:value="menuForm[item.key]" :name="item.key"> | |||
<n-space> | |||
<n-radio v-for="(cItem,cIndex) in item.options" :key="`${item.key}_${cIndex}`" :value="cItem.value"> {{ cItem.label }}</n-radio> | |||
</n-space> | |||
</n-radio-group> | |||
<n-input v-if="item.type === 'input'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
<n-select v-if="item.type === 'select'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
<n-input-number v-if="item.type === 'number'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
</n-form-item> | |||
</n-gi> | |||
</template> | |||
</n-grid> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form, changeMenuType, getMenuOptions } from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, watch, toRefs } from 'vue' | |||
import { form, setMenuOptions, changeMenuType } from '../tools/form.js' | |||
import Modal from '@/components/Modal/index.vue' | |||
import { addMenu, editMenu } from '@/api/system/menu/index.js' | |||
import { menuCreate, menuUpdate, permissionCreate, permissionUpdate } from '@/api/system/menu/index.js' | |||
import { defineComponent, ref, reactive, computed, toRefs, watch } from 'vue' | |||
export default defineComponent({ | |||
name: 'MenuModal', | |||
name: 'UserModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
@@ -57,6 +58,10 @@ export default defineComponent({ | |||
data: { | |||
type: Object, | |||
default: () => {} | |||
}, | |||
source: { | |||
type: Array, | |||
default: () => [] | |||
} | |||
}, | |||
emits: { | |||
@@ -69,9 +74,9 @@ export default defineComponent({ | |||
'preview': '菜单详情', | |||
'update': '编辑菜单' | |||
} | |||
getMenuOptions() | |||
const { menuForm, menuRules } = form | |||
const formRef = ref() | |||
setMenuOptions(props.source) | |||
const data = reactive({ | |||
menuForm: { | |||
...menuForm, | |||
@@ -83,24 +88,15 @@ export default defineComponent({ | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
watch(() => data.menuForm.type, | |||
(val) => { | |||
switch (val) { | |||
case 0: | |||
data.menuForm.permission = '' | |||
break | |||
case 1: | |||
data.menuForm.path = '' | |||
data.menuForm.component = '' | |||
break | |||
} | |||
changeMenuType(val) | |||
}) | |||
changeMenuType(data.menuForm.type) | |||
watch(() => data.menuForm.type, (value) => { | |||
changeMenuType(value) | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
title: MODAL_TYPE[props.type], | |||
width: 800, | |||
show: props.visible, | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
@@ -108,7 +104,6 @@ export default defineComponent({ | |||
}) | |||
const getFormOptions = computed(() => { | |||
console.log('change') | |||
return { | |||
...form.formItem | |||
} | |||
@@ -119,24 +114,44 @@ export default defineComponent({ | |||
if (!errors) { | |||
const params = { ...data.menuForm } | |||
if (params.id) { | |||
editMenu(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
if (params.type === 0) { | |||
menuUpdate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} else { | |||
permissionUpdate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
} else { | |||
addMenu(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
if (params.type === 0) { | |||
params.parentId = params.parentId || 0 | |||
menuCreate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
params.menuId = params.parentId || 0 | |||
permissionCreate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
@@ -157,5 +172,9 @@ export default defineComponent({ | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-input-number{ | |||
width: 100%; | |||
} | |||
</style> |
@@ -1,49 +1,53 @@ | |||
<template> | |||
<div> | |||
<n-card> | |||
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<data-table | |||
<HeadSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<DataTable | |||
ref="tableRef" | |||
:columns="columns" | |||
:pagination="false" | |||
data-type="tree" | |||
:request="loadDataTable" | |||
:data="tableData" | |||
:row-key="(row) => row.id" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 添加菜单 </n-button> | |||
<n-button | |||
v-if="permissionList.includes('system:menu:create')" | |||
type="primary" | |||
@click="handleModal" | |||
><n-icon size="18"><AddOutline /></n-icon> {{ getPermissionLabel('system:menu:create') }} </n-button> | |||
</template> | |||
</data-table> | |||
</DataTable> | |||
</n-card> | |||
</div> | |||
<MenuModal v-if="modalShow" v-model:visible="modalShow" :data="rowData" :type="modalType" @reload="handleSearch" /> | |||
<!-- 新增、编辑弹窗 --> | |||
<MenuModal v-if="modalShow" v-model:visible="modalShow" :source="menuSource" :data="rowData" :type="modalType" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import search from './tools/search.js' | |||
import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import HeadSearch from '@/components/Search/index.vue' | |||
import DataTable from '@/components/DataTable/index.vue' | |||
import MenuModal from './components/MenuModal.vue' | |||
import { getMenuList } from '@/api/system/menu/index.js' | |||
import { unref, reactive, toRefs, onUnmounted } from 'vue' | |||
import { toRefs, reactive, onUnmounted } from 'vue' | |||
import { usePermissionStore } from '@/store/modules/permission' | |||
export default { | |||
name: 'MenuPage', | |||
components: { dataTable, MenuModal, headSearch }, | |||
components: { DataTable, MenuModal, HeadSearch }, | |||
setup() { | |||
const permissionStore = usePermissionStore() | |||
const data = reactive({ | |||
...toRefs(table), | |||
search | |||
search, | |||
permissionList: permissionStore.accessPermissionCodes | |||
}) | |||
table.fetchList() | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await getMenuList(_params) | |||
const getPermissionLabel = (code) => { | |||
return permissionStore.accessPermissions.find((item) => { | |||
return item.code === code | |||
})?.name || '' | |||
} | |||
// 打开新增弹框 | |||
@@ -54,16 +58,19 @@ export default { | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
data.searchParams = { } | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal | |||
handleModal, | |||
getPermissionLabel | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -1,53 +1,50 @@ | |||
import { ref, reactive, watch } from 'vue' | |||
import { MENU_TYPE, MENU_OPEN, MENU_VISIBLE, MENU_STATUS } from '@/utils/dictionary.js' | |||
import { getMenu } from '@/api/system/menu/index.js' | |||
import { dataToSelect } from '@/utils/handleData.js' | |||
import { ref, reactive } from 'vue' | |||
import { MENU_TYPE, MENU_STATUS, MENU_VISIBLE } from '@/utils/dictionary.js' | |||
const menuOptions = ref() | |||
const menuType0 = ref(true) | |||
const menuType1 = ref(false) | |||
const disCode = ref(true) | |||
const disMenu = ref(false) | |||
export const form = reactive({ | |||
menuForm: { | |||
pid: null, | |||
parentId: null, | |||
type: 0, | |||
title: '', | |||
target: '1', | |||
icon: '', | |||
permission: '', | |||
path: '', | |||
sort: null, | |||
component: '', | |||
hide: 0, | |||
status: 1 | |||
name: null, | |||
code: null, | |||
path: null, | |||
component: null, | |||
isHidden: 0, | |||
status: 1, | |||
clientId: 'tuoheng-pilot-admin' | |||
}, | |||
menuRules: { | |||
title: [{ required: true, message: '请输入菜单名称', trigger: 'blur' }], | |||
sort: [{ required: true, type: 'number', message: '请输入排序号', trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
{ type: 'select', key: 'pid', label: '上级菜单', props: { options: menuOptions, placeholder: '请选择上级菜单' }}, | |||
{ type: 'tree-select', key: 'parentId', label: '上级菜单', props: { options: menuOptions, labelField: 'name', keyField: 'id', placeholder: '请选择上级菜单', clearable: true }}, | |||
{ type: 'radio', key: 'type', label: '菜单类型', options: MENU_TYPE }, | |||
{ type: 'input', key: 'title', label: '菜单名称', props: { maxlength: '20', placeholder: '请输入菜单名称', clearable: true }}, | |||
{ type: 'radio', key: 'target', label: '打开方式', options: MENU_OPEN }, | |||
{ type: 'input', key: 'icon', label: '菜单图标', props: { maxlength: '20', placeholder: '请选择菜单图标', clearable: true }}, | |||
{ type: 'input', key: 'permission', label: '权限标识', props: { maxlength: '20', placeholder: '请输入部门名称', disabled: menuType0, clearable: true }}, | |||
{ type: 'input', key: 'path', label: '路由地址', props: { maxlength: '20', placeholder: '请输入部门名称', disabled: menuType1, clearable: true }}, | |||
{ type: 'number', key: 'sort', label: '排序号', props: { min: 0, placeholder: '请输入排序号', clearable: true }}, | |||
{ type: 'input', key: 'component', label: '组件路径', props: { maxlength: '200', placeholder: '请输入部门名称', disabled: menuType1, clearable: true }}, | |||
{ type: 'radio', key: 'hide', label: '是否可见', options: MENU_VISIBLE }, | |||
{ type: 'input', key: 'name', label: '菜单名称', props: { maxlength: '20', placeholder: '请输入菜单名称', clearable: true }}, | |||
{ type: 'input', key: 'code', label: '权限标识', props: { maxlength: '200', disabled: disCode, placeholder: '请输入权限标识', clearable: true }}, | |||
{ type: 'input', key: 'path', label: '路由地址', props: { maxlength: '200', disabled: disMenu, placeholder: '请输入路由地址', clearable: true }}, | |||
{ type: 'input', key: 'component', label: '组件路径', props: { maxlength: '200', disabled: disMenu, placeholder: '请输入组件路径', clearable: true }}, | |||
{ type: 'number', key: 'sort', label: '排序号', props: { min: 0, placeholder: '请输入排序号', showButton: false, clearable: true }}, | |||
{ type: 'radio', key: 'isHidden', label: '是否可见', options: MENU_VISIBLE }, | |||
{ type: 'radio', key: 'status', label: '菜单状态', options: MENU_STATUS } | |||
] | |||
}) | |||
export const getMenuOptions = async function() { | |||
const res = await getMenu() | |||
setTimeout(() => { | |||
menuOptions.value = dataToSelect(res.data, { label: 'title', value: 'id' }) | |||
}, 3000) | |||
export const setMenuOptions = async function(value) { | |||
menuOptions.value = value | |||
} | |||
export const changeMenuType = function(value) { | |||
menuType0.value = !value | |||
menuType1.value = !menuType0.value | |||
export const changeMenuType = async function(value) { | |||
switch (value) { | |||
case 0: | |||
disCode.value = true | |||
disMenu.value = false | |||
break | |||
case 1: | |||
disCode.value = false | |||
disMenu.value = true | |||
break | |||
} | |||
} | |||
@@ -1,8 +1,9 @@ | |||
import { reactive } from 'vue' | |||
const data = reactive([ | |||
{ | |||
label: '菜单名称', | |||
key: 'title', | |||
key: 'name', | |||
props: { | |||
placeholder: '请输入菜单名称' | |||
} |
@@ -1,16 +1,32 @@ | |||
import { MENU_TYPE, MENU_STATUS, MENU_VISIBLE } from '@/utils/dictionary.js' | |||
import { h, ref, reactive } from 'vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import { h, ref, unref, reactive } from 'vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { deleteMenu } from '@/api/system/menu/index.js' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import { MENU_TYPE, MENU_STATUS, MENU_VISIBLE } from '@/utils/dictionary.js' | |||
import { getMenuList, deleteMenu, deletePermission } from '@/api/system/menu/index.js' | |||
import { toTreeData } from '@/utils/handleData.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
async function fetchList() { | |||
const params = { | |||
...unref(data.searchParams), | |||
menuType: 1 | |||
} | |||
const res = await getMenuList(params) | |||
const { menuListVoList, permissionList } = res.data | |||
menuListVoList.forEach((item) => { item.type = 0; item.value = item.id }) | |||
const sourecData = [...menuListVoList] | |||
permissionList.forEach((item) => { item.type = 1; item.value = `${item.menuId}_${item.id}`; item.parentId = item.menuId }) | |||
const tableData = [...menuListVoList.concat(permissionList)] | |||
data.menuSource = toTreeData(sourecData, 'value', 'parentId', 'children') | |||
data.tableData = toTreeData(tableData, 'value', 'parentId', 'children') | |||
} | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
fetchList() | |||
} | |||
/** | |||
@@ -20,38 +36,61 @@ function handleSearch(params) { | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = type === 'create' ? { pid: row.id } : row | |||
data.rowData = type === 'create' ? { parentId: row.id } : row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
// 删除方法 | |||
function deleteData(id) { | |||
deleteMenu(id) | |||
.then((res) => { | |||
function deleteData({ type, id }) { | |||
if (type === 0) { | |||
deleteMenu(id).then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}) | |||
.catch((e) => { | |||
console.log(e) | |||
}) | |||
.catch((e) => { | |||
console.log(e) | |||
}) | |||
} else { | |||
deletePermission(id) | |||
.then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}) | |||
.catch((e) => { | |||
console.log(e) | |||
}) | |||
} | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
menuSource: [], | |||
tableData: [], | |||
modalType: 'create', | |||
modalShow: false, | |||
handleSearch, | |||
fetchList, | |||
columns: [ | |||
{ | |||
title: '编号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center', | |||
width: 100 | |||
}, | |||
{ | |||
title: '菜单标题', | |||
key: 'title', | |||
key: 'name', | |||
align: 'center', | |||
width: 200 | |||
width: 150 | |||
}, | |||
{ | |||
title: '菜单类型', | |||
@@ -61,7 +100,10 @@ const data = reactive({ | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.type, | |||
filters: MENU_TYPE | |||
filters: MENU_TYPE, | |||
tags: { | |||
bordered: true | |||
} | |||
}) | |||
} | |||
}, | |||
@@ -69,13 +111,19 @@ const data = reactive({ | |||
title: '路由地址', | |||
key: 'path', | |||
align: 'center', | |||
width: 200 | |||
minWidth: 200 | |||
}, | |||
{ | |||
title: '组件路径', | |||
key: 'component', | |||
align: 'center', | |||
width: 200 | |||
minWidth: 300 | |||
}, | |||
{ | |||
title: '权限标识', | |||
key: 'code', | |||
align: 'center', | |||
minWidth: 200 | |||
}, | |||
{ | |||
title: '状态', | |||
@@ -83,35 +131,38 @@ const data = reactive({ | |||
align: 'center', | |||
width: 100, | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.status, | |||
filters: MENU_STATUS | |||
}) | |||
if (row.status) { | |||
return h(TableTags, { | |||
data: row.status, | |||
filters: MENU_STATUS | |||
}) | |||
} | |||
} | |||
}, | |||
{ | |||
title: '排序', | |||
key: 'sort', | |||
align: 'center', | |||
width: 100 | |||
align: 'center' | |||
}, | |||
{ | |||
title: '是否可见', | |||
key: 'hide', | |||
title: '可见', | |||
key: 'isHidden', | |||
align: 'center', | |||
width: 100, | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.hide, | |||
filters: MENU_VISIBLE | |||
}) | |||
if (row.isHidden) { | |||
return h(TableTags, { | |||
data: row.isHidden, | |||
filters: MENU_VISIBLE | |||
}) | |||
} | |||
} | |||
}, | |||
{ | |||
title: '创建时间', | |||
key: 'createTime', | |||
align: 'center', | |||
width: 160 | |||
minWidth: 200 | |||
}, | |||
{ | |||
title: '操作', | |||
@@ -122,37 +173,34 @@ const data = reactive({ | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '添加', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'create') | |||
}, | |||
auth: 'basic_list' | |||
auth: 'system:menu:add' | |||
}, | |||
{ | |||
label: '修改', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
auth: 'system:menu:edit' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
onPositiveClick: deleteData.bind(null, { type: row.type, id: [row.id] }) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
}, | |||
auth: 'system:menu:delete' | |||
} | |||
], | |||
align: 'center' |
@@ -0,0 +1,180 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="menuForm" | |||
:rules="menuRules" | |||
:label-width="80" | |||
label-placement="left" | |||
require-mark-placement="left" | |||
:disabled="disabled" | |||
> | |||
<n-grid x-gap="12" :cols="2"> | |||
<template v-for="(item,index) in getFormOptions" :key="index"> | |||
<n-gi> | |||
<n-form-item :label="item.label" :path="item.key"> | |||
<n-tree-select v-if="item.type === 'tree-select'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
<n-radio-group v-if="item.type === 'radio'" v-model:value="menuForm[item.key]" :name="item.key"> | |||
<n-space> | |||
<n-radio v-for="(cItem,cIndex) in item.options" :key="`${item.key}_${cIndex}`" :value="cItem.value"> {{ cItem.label }}</n-radio> | |||
</n-space> | |||
</n-radio-group> | |||
<n-input v-if="item.type === 'input'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
<n-select v-if="item.type === 'select'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
<n-input-number v-if="item.type === 'number'" v-model:value="menuForm[item.key]" v-bind="item.props" /> | |||
</n-form-item> | |||
</n-gi> | |||
</template> | |||
</n-grid> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form, setMenuOptions, changeMenuType } from '../tools/form.js' | |||
import Modal from '@/components/Modal/index.vue' | |||
import { mpMenuCreate, mpMenuUpdate, permissionCreate, permissionUpdate } from '@/api/system/menu/index.js' | |||
import { defineComponent, ref, reactive, computed, toRefs, watch } from 'vue' | |||
export default defineComponent({ | |||
name: 'UserModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => {} | |||
}, | |||
source: { | |||
type: Array, | |||
default: () => [] | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建菜单', | |||
'preview': '菜单详情', | |||
'update': '编辑菜单' | |||
} | |||
const { menuForm, menuRules } = form | |||
const formRef = ref() | |||
setMenuOptions(props.source) | |||
const data = reactive({ | |||
menuForm: { | |||
...menuForm, | |||
...props.data, | |||
pid: props.data?.pid === 0 ? null : props.data?.pid | |||
}, | |||
menuRules: { | |||
...menuRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
changeMenuType(data.menuForm.type) | |||
watch(() => data.menuForm.type, (value) => { | |||
changeMenuType(value) | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
title: MODAL_TYPE[props.type], | |||
width: 800, | |||
show: props.visible, | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
return { | |||
...form.formItem | |||
} | |||
}) | |||
function handleConfirm() { | |||
formRef.value.validate((errors) => { | |||
if (!errors) { | |||
const params = { ...data.menuForm } | |||
if (params.id) { | |||
if (params.type === 0) { | |||
mpMenuUpdate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} else { | |||
permissionUpdate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
} else { | |||
if (params.type === 0) { | |||
params.parentId = params.parentId || 0 | |||
mpMenuCreate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
params.menuId = params.parentId || 0 | |||
permissionCreate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
getModalOptions, | |||
getFormOptions, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-input-number{ | |||
width: 100%; | |||
} | |||
</style> |
@@ -0,0 +1,72 @@ | |||
<template> | |||
<div> | |||
<n-card> | |||
<headSearch :info="search" @search="loadDataTable" @reset="loadDataTable" /> | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:row-key="(row) => row.id" | |||
:data="tableData" | |||
:pagination="false" | |||
size="large" | |||
> | |||
<template #tableTitle> | |||
<n-button v-if="permissionList.includes('system:mpmenu:create')" type="primary" @click="handleModal"> {{ getPermissionLabel('system:mpmenu:create') }} </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<MenuModal v-if="modalShow" v-model:visible="modalShow" :source="menuSource" :data="rowData" :type="modalType" @reload="loadDataTable" /> | |||
</template> | |||
<script> | |||
import search from './tools/search.js' | |||
import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import MenuModal from './components/MenuModal.vue' | |||
import { toRefs, reactive, onUnmounted } from 'vue' | |||
import { usePermissionStore } from '@/store/modules/permission' | |||
export default { | |||
name: 'MenuPage', | |||
components: { dataTable, MenuModal, headSearch }, | |||
setup() { | |||
const permissionStore = usePermissionStore() | |||
const data = reactive({ | |||
...toRefs(table), | |||
search, | |||
permissionList: permissionStore.accessPermissionCodes | |||
}) | |||
table.fetchList() | |||
const getPermissionLabel = (code) => { | |||
return permissionStore.accessPermissions.find((item) => { | |||
return item.code === code | |||
})?.name || '' | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = { menuType: 2 } | |||
}) | |||
return { | |||
...toRefs(data), | |||
handleModal, | |||
getPermissionLabel | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -0,0 +1,47 @@ | |||
import { ref, reactive } from 'vue' | |||
import { MENU_TYPE, MENU_STATUS, MENU_VISIBLE } from '@/utils/dictionary.js' | |||
const menuOptions = ref() | |||
const disCode = ref(true) | |||
const disMenu = ref(false) | |||
export const form = reactive({ | |||
menuForm: { | |||
parentId: null, | |||
type: 0, | |||
name: null, | |||
path: null, | |||
component: null, | |||
code: null, | |||
apiUrl: null, | |||
clientId: 'tuoheng-pilot-mp' | |||
}, | |||
menuRules: { | |||
name: [{ required: true, message: '请输入菜单名称', trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
{ type: 'tree-select', key: 'parentId', label: '上级菜单', props: { options: menuOptions, labelField: 'name', keyField: 'id', placeholder: '请选择上级菜单', clearable: true }}, | |||
{ type: 'radio', key: 'type', label: '菜单类型', options: MENU_TYPE }, | |||
{ type: 'input', key: 'name', label: '菜单名称', props: { maxlength: '20', placeholder: '请输入菜单名称', clearable: true }}, | |||
{ type: 'input', key: 'path', label: '路由地址', props: { maxlength: '200', disabled: disMenu, placeholder: '请输入路由地址', clearable: true }}, | |||
{ type: 'input', key: 'component', label: '组件路径', props: { maxlength: '200', disabled: disMenu, placeholder: '请输入组件路径', clearable: true }}, | |||
{ type: 'input', key: 'code', label: '权限标识', props: { maxlength: '200', disabled: disCode, placeholder: '请输入权限标识', clearable: true }}, | |||
{ type: 'input', key: 'apiUrl', label: '接口地址', props: { maxlength: '200', disabled: disCode, placeholder: '请输入接口地址', clearable: true }} | |||
] | |||
}) | |||
export const setMenuOptions = async function(value) { | |||
menuOptions.value = value | |||
} | |||
export const changeMenuType = async function(value) { | |||
switch (value) { | |||
case 0: | |||
disCode.value = true | |||
disMenu.value = false | |||
break | |||
case 1: | |||
disCode.value = false | |||
disMenu.value = true | |||
break | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
import { reactive } from 'vue' | |||
const data = reactive([ | |||
{ | |||
label: '菜单名称', | |||
key: 'name', | |||
props: { | |||
placeholder: '请输入菜单名称' | |||
} | |||
} | |||
]) | |||
export default data | |||
@@ -0,0 +1,186 @@ | |||
import { h, ref, reactive, unref } from 'vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import { MENU_TYPE } from '@/utils/dictionary.js' | |||
import { deleteMpMenu, deletePermission, getMenuList } from '@/api/system/menu/index.js' | |||
import { toTreeData } from '@/utils/handleData.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref({ menuType: 2 }) | |||
async function fetchList() { | |||
const params = { | |||
...unref(data.searchParams), | |||
menuType: 1 | |||
} | |||
const res = await getMenuList(params) | |||
const { menuListVoList, permissionList } = res.data | |||
menuListVoList.forEach((item) => { item.type = 0; item.value = item.id }) | |||
const sourecData = [...menuListVoList] | |||
permissionList.forEach((item) => { item.type = 1; item.value = `${item.menuId}_${item.id}`; item.parentId = item.menuId }) | |||
const tableData = [...menuListVoList.concat(permissionList)] | |||
data.menuSource = toTreeData(sourecData, 'value', 'parentId', 'children') | |||
data.tableData = toTreeData(tableData, 'value', 'parentId', 'children') | |||
} | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
fetchList() | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = type === 'create' ? { parentId: row.id } : row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
// 删除方法 | |||
function deleteData({ type, id }) { | |||
if (type === 0) { | |||
deleteMpMenu(id).then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}) | |||
.catch((e) => { | |||
console.log(e) | |||
}) | |||
} else { | |||
deletePermission(id) | |||
.then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}) | |||
.catch((e) => { | |||
console.log(e) | |||
}) | |||
} | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
menuSource: [], | |||
tableData: [], | |||
modalType: 'create', | |||
modalShow: false, | |||
refetch: false, | |||
handleSearch, | |||
fetchList, | |||
columns: [ | |||
{ | |||
title: '编号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center', | |||
width: 100 | |||
}, | |||
{ | |||
title: '菜单标题', | |||
key: 'name', | |||
align: 'center', | |||
width: 150 | |||
}, | |||
{ | |||
title: '菜单类型', | |||
key: 'type', | |||
align: 'center', | |||
width: 100, | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.type, | |||
filters: MENU_TYPE, | |||
tags: { | |||
bordered: true | |||
} | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '路由地址', | |||
key: 'path', | |||
align: 'center', | |||
minWidth: 200 | |||
}, | |||
{ | |||
title: '组件路径', | |||
key: 'component', | |||
align: 'center', | |||
minWidth: 300 | |||
}, | |||
{ | |||
title: '权限标识', | |||
key: 'code', | |||
align: 'center', | |||
minWidth: 200 | |||
}, | |||
{ | |||
title: '接口地址', | |||
key: 'apiUrl', | |||
align: 'center', | |||
minWidth: 200 | |||
}, | |||
{ | |||
title: '创建时间', | |||
key: 'createTime', | |||
align: 'center', | |||
minWidth: 200 | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'create') | |||
}, | |||
auth: 'system:mpmenu:add' | |||
}, | |||
{ | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'system:mpmenu:edit' | |||
}, | |||
{ | |||
type: 'popconfirm', | |||
auth: 'system:mpmenu:delete', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, { type: row.type, id: [row.id] }) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -0,0 +1,204 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-tabs type="line" animated> | |||
<n-tab-pane display-directive="show" name="pc" tab="管理端菜单"> | |||
<n-tree | |||
v-if="tabsTreeVisible" | |||
ref="adminTreeRef" | |||
block-line | |||
cascade | |||
checkable | |||
default-expand-all | |||
virtual-scroll | |||
label-field="name" | |||
:data="adminData" | |||
:default-checked-keys="adminKeys" | |||
style="height: 400px" | |||
/> | |||
</n-tab-pane> | |||
<n-tab-pane display-directive="show" name="mp" tab="小程序菜单"> | |||
<n-tree | |||
v-if="tabsTreeVisible" | |||
ref="miniTreeRef" | |||
block-line | |||
cascade | |||
checkable | |||
default-expand-all | |||
virtual-scroll | |||
label-field="name" | |||
:data="miniData" | |||
:default-checked-keys="miniKeys" | |||
style="height: 400px" | |||
/> | |||
</n-tab-pane> | |||
</n-tabs> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import Modal from '@/components/Modal/index.vue' | |||
import { getRoleMenu, saveRoleMenu } from '@/api/system/role/index.js' | |||
import { toTreeData } from '@/utils/handleData.js' | |||
import { defineComponent, ref, reactive, computed, toRefs, onBeforeMount, nextTick } from 'vue' | |||
export default defineComponent({ | |||
name: 'UserModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => {} | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { 'relate': '分配权限' } | |||
const adminTreeRef = ref() | |||
const miniTreeRef = ref() | |||
const data = reactive({ | |||
tabsTreeVisible: false, | |||
adminData: [], | |||
adminKeys: [], | |||
miniData: [], | |||
miniKeys: [] | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
title: MODAL_TYPE[props.type], | |||
width: 600, | |||
show: props.visible, | |||
trapFocus: false, | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
function handleConfirm() { | |||
const adminCheckedData = adminTreeRef.value.getCheckedData() | |||
const adminIndeterminateData = adminTreeRef.value.getIndeterminateData() | |||
const miniCheckedData = miniTreeRef.value.getCheckedData() | |||
const miniIndeterminateData = miniTreeRef.value.getIndeterminateData() | |||
const adminKeys = adminCheckedData.keys.concat(adminIndeterminateData.keys) | |||
const miniKeys = miniCheckedData.keys.concat(miniIndeterminateData.keys) | |||
const params = { roleId: props.data.id, ids: adminKeys.concat(miniKeys) } | |||
saveRoleMenu(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
const querySelectedKeys = (data, list) => { | |||
data.forEach((item) => { | |||
if (item.children && item.children.length) { | |||
querySelectedKeys(item.children, list) | |||
} else { | |||
if (item.checked) { | |||
list.push(item.key) | |||
} | |||
} | |||
}) | |||
} | |||
onBeforeMount(async() => { | |||
const res = await getRoleMenu({ roleId: props.data.id }) | |||
if (res.code === 0) { | |||
const { opMenusList, permissionsList } = res.data | |||
const adminMenus = opMenusList.filter((item) => { | |||
item.title = item.name | |||
item.key = item.identificationId | |||
return item.clientId === 'tuoheng-pilot-admin' | |||
}) | |||
const adminPermissions = permissionsList.filter((item) => { | |||
item.title = item.name | |||
item.parentId = item.menuId | |||
item.key = item.identificationId | |||
return item.clientId === 'tuoheng-pilot-admin' | |||
}) | |||
const mpMenus = opMenusList.filter((item) => { | |||
item.title = item.name | |||
item.key = item.identificationId | |||
return item.clientId === 'tuoheng-waterway-mp' | |||
}) | |||
const mpPermissions = permissionsList.filter((item) => { | |||
item.title = item.name | |||
item.parentId = item.menuId | |||
item.key = item.identificationId | |||
return item.clientId === 'tuoheng-waterway-mp' | |||
}) | |||
const adminData = adminMenus.concat(adminPermissions) | |||
const adminTreeData = toTreeData(adminData, 'id', 'parentId', 'children') | |||
data.adminKeys = [] | |||
querySelectedKeys(adminTreeData, data.adminKeys) | |||
const miniData = mpMenus.concat(mpPermissions) | |||
const miniTreeData = toTreeData(miniData, 'id', 'parentId', 'children') | |||
data.miniKeys = [] | |||
querySelectedKeys(miniTreeData, data.miniKeys) | |||
nextTick(() => { | |||
data.tabsTreeVisible = true | |||
data.adminData = adminTreeData | |||
data.miniData = miniTreeData | |||
}) | |||
} | |||
}) | |||
return { | |||
...toRefs(data), | |||
adminTreeRef, | |||
miniTreeRef, | |||
getModalOptions, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-input-number{ | |||
width: 100%; | |||
} | |||
.flex__body{ | |||
display: flex; | |||
width: 100%; | |||
flex-wrap: wrap; | |||
.n-form-item{ | |||
width: 100%; | |||
} | |||
// .flex__item{ | |||
// width: 50%; | |||
// } | |||
} | |||
.n-date-picker{ | |||
width: 100%; | |||
} | |||
</style> |
@@ -8,35 +8,39 @@ | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="roleForm" | |||
:rules="roleRules" | |||
:label-width="80" | |||
:model="userForm" | |||
:rules="userRules" | |||
:label-width="100" | |||
label-placement="left" | |||
require-mark-placement="left" | |||
:disabled="disabled" | |||
> | |||
<template v-for="(item,index) in getFormOptions" :key="index"> | |||
<n-form-item :label="item.label" :path="item.key"> | |||
<n-input v-if="item.type === 'input'" v-model:value="roleForm[item.key]" v-bind="item.props" /> | |||
<n-radio-group v-if="item.type === 'radio'" v-model:value="roleForm[item.key]" :name="item.key"> | |||
<n-space> | |||
<n-radio v-for="(cItem,cIndex) in item.options" :key="`${item.key}_${cIndex}`" :value="cItem.value"> {{ cItem.label }}</n-radio> | |||
</n-space> | |||
</n-radio-group> | |||
</n-form-item> | |||
</template> | |||
<div class="flex__body"> | |||
<template v-for="(item,index) in getFormOptions" :key="index"> | |||
<n-form-item :label="item.label" :path="item.key" :class="{ 'flex__item': !['oss','editor'].includes(item.type) }"> | |||
<n-input v-if="item.type === 'input'" v-model:value="userForm[item.key]" v-bind="item.props" /> | |||
<n-radio-group v-if="item.type === 'radio'" v-model:value="userForm[item.key]" :name="item.key"> | |||
<n-space> | |||
<n-radio v-for="(cItem,cIndex) in item.options" :key="`${item.key}_${cIndex}`" :value="cItem.value"> {{ cItem.label }}</n-radio> | |||
</n-space> | |||
</n-radio-group> | |||
</n-form-item> | |||
</template> | |||
</div> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import form from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import { form } from '../tools/form.js' | |||
import Modal from '@/components/Modal/index.vue' | |||
import { addRole, editRole } from '@/api/system/role/index' | |||
import { roleCreate, roleUpdate } from '@/api/system/role/index.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
export default defineComponent({ | |||
name: 'RoleModal', | |||
name: 'UserModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
@@ -58,27 +62,28 @@ export default defineComponent({ | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建角色', | |||
'preview': '角色详情', | |||
'update': '编辑角色' | |||
'create': '新增用户', | |||
'preview': '用户详情', | |||
'update': '编辑用户' | |||
} | |||
const { roleForm, roleRules } = form | |||
const { userForm, userRules } = form | |||
const formRef = ref() | |||
const data = reactive({ | |||
roleForm: { | |||
...roleForm, | |||
userForm: { | |||
...userForm, | |||
...props.data | |||
}, | |||
roleRules: { | |||
...roleRules | |||
userRules: { | |||
...userRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
title: MODAL_TYPE[props.type], | |||
width: 600, | |||
show: props.visible, | |||
trapFocus: false, | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
@@ -91,30 +96,29 @@ export default defineComponent({ | |||
}) | |||
function handleConfirm() { | |||
formRef.value?.validate((errors) => { | |||
formRef.value.validate((errors) => { | |||
if (!errors) { | |||
const params = { ...data.roleForm } | |||
if (params.id) { | |||
/* 编辑 */ | |||
editRole(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
const params = { ...data.userForm } | |||
if (props.type === 'preview') { | |||
emit('reload') | |||
handleClose() | |||
} else if (params.id) { | |||
roleUpdate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} else { | |||
/* 新增 */ | |||
addRole(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
roleCreate(params).then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
@@ -135,5 +139,25 @@ export default defineComponent({ | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-input-number{ | |||
width: 100%; | |||
} | |||
.flex__body{ | |||
display: flex; | |||
width: 100%; | |||
flex-wrap: wrap; | |||
.n-form-item{ | |||
width: 100%; | |||
} | |||
// .flex__item{ | |||
// width: 50%; | |||
// } | |||
} | |||
.n-date-picker{ | |||
width: 100%; | |||
} | |||
</style> |
@@ -5,32 +5,21 @@ | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:row-key="(row) => row.id" | |||
:request="loadDataTable" | |||
:row-key="(row) => row.id" | |||
:scroll-x="1200" | |||
size="large" | |||
scroll-x="1200" | |||
@update:checked-row-keys="handleCheck" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
<n-popconfirm | |||
negative-text="取消" | |||
positive-text="确认" | |||
@positive-click="deleteComplex" | |||
> | |||
<template #trigger> | |||
<n-button type="primary">删除</n-button> | |||
</template> | |||
确定删除选中的数据吗? | |||
</n-popconfirm> | |||
<n-button v-if="permissionStore.includes('system:role:create')" type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
{{ permissionStore.getToken }} | |||
<!-- 新增、编辑弹窗 --> | |||
<RoleModal v-if="modalShow" v-model:visible="modalShow" :data="rowData" :type="modalType" @reload="handleSearch" /> | |||
<!-- 分配权限 --> | |||
<ConfigModal v-if="configModalShow" v-model:visible="configModalShow" :data="rowData" @reload="handleSearch" /> | |||
<AuthModal v-if="relateShow" v-model:visible="relateShow" :data="rowData" :type="modalType" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
@@ -39,19 +28,20 @@ import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import RoleModal from './components/RoleModal.vue' | |||
import ConfigModal from './components/ConfigModal.vue' | |||
import { getRoleList } from '@/api/system/role/index' | |||
import { ref, unref, toRefs, reactive, onUnmounted } from 'vue' | |||
import AuthModal from './components/AuthModal.vue' | |||
import { getRoleList } from '@/api/system/role/index.js' | |||
import { unref, toRefs, reactive, onUnmounted } from 'vue' | |||
import { usePermissionStore } from '@/store/modules/permission' | |||
export default { | |||
name: 'MenuPage', | |||
components: { headSearch, dataTable, RoleModal, ConfigModal }, | |||
name: 'UserManage', | |||
components: { dataTable, headSearch, RoleModal, AuthModal }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table), | |||
search | |||
}) | |||
const permissionStore = usePermissionStore().accessPermissionCodes // 按钮权限 | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
@@ -60,39 +50,23 @@ export default { | |||
return await getRoleList(_params) | |||
} | |||
// 新增用户 | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
// 选择表格数据 | |||
const selectedIds = ref([]) | |||
function handleCheck(rowKeys) { | |||
selectedIds.value = rowKeys | |||
} | |||
// 批量删除 | |||
function deleteComplex() { | |||
if (selectedIds.value.length) { | |||
data.deleteData(selectedIds.value) | |||
} else { | |||
$message.warning('请至少选中一条数据') | |||
} | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
data.modalShow = false | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal, | |||
handleCheck, | |||
selectedIds, | |||
deleteComplex | |||
permissionStore | |||
} | |||
} | |||
} |
@@ -1,24 +1,21 @@ | |||
import { reactive } from 'vue' | |||
import { USER_STATUS } from '@/utils/dictionary.js' | |||
import { ref, reactive } from 'vue' | |||
import { ROLE_STATUS } from '@/utils/dictionary.js' | |||
const data = reactive({ | |||
roleForm: { | |||
code: '', | |||
name: '', | |||
status: 1, | |||
note: '' | |||
export const form = reactive({ | |||
userForm: { | |||
roleName: null, | |||
remark: null, | |||
roleCode: null, | |||
status: 1 | |||
}, | |||
roleRules: { | |||
code: [{ required: true, message: '请输入角色编号', trigger: 'blur' }], | |||
name: [{ required: true, message: '请输入角色名称', type: 'string', trigger: 'blur' }], | |||
status: [{ required: true, message: '请选择状态', type: 'number', trigger: 'blur' }] | |||
userRules: { | |||
roleName: [{ required: true, message: '请输入角色名称', trigger: 'blur' }], | |||
roleCode: [{ required: true, message: '请输入角色编码', trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
{ type: 'input', key: 'code', label: '角色编号', props: { maxlength: '20', placeholder: '请输入角色编号', clearable: true }}, | |||
{ type: 'input', key: 'name', label: '角色名称', props: { maxlength: '20', placeholder: '请输入角色名称', clearable: true }}, | |||
{ type: 'radio', key: 'status', label: '状态', options: USER_STATUS }, | |||
{ type: 'input', key: 'note', label: '备注', props: { type: 'textarea', autosize: { maxlength: '200', minRows: 3, maxRows: 3 }}} | |||
{ type: 'input', key: 'roleName', label: '角色名称', props: { maxlength: '20', placeholder: '请输入角色名称', clearable: true }}, | |||
{ type: 'input', key: 'roleCode', label: '角色编码', props: { maxlength: '20', placeholder: '请输入角色名称', clearable: true }}, | |||
{ type: 'radio', key: 'status', label: '角色状态', options: ROLE_STATUS }, | |||
{ type: 'input', key: 'remark', label: '备注', props: { type: 'textarea', maxlength: '140', autosize: { minRows: 3, maxRows: 3 }, placeholder: '请输入备注', clearable: true }} | |||
] | |||
}) | |||
export default data |
@@ -3,7 +3,7 @@ import { reactive } from 'vue' | |||
const data = reactive([ | |||
{ | |||
label: '角色名称', | |||
key: 'name', | |||
key: 'roleName', | |||
props: { | |||
placeholder: '请输入角色名称' | |||
} | |||
@@ -11,3 +11,4 @@ const data = reactive([ | |||
]) | |||
export default data | |||
@@ -1,7 +1,7 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableSwitch from '@/components/DataTable/tools/Switch.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { deleteRole, setRoleStatus } from '@/api/system/role/index' | |||
import TableSwitch from '@/components/DataTable/tools/Switch.vue' | |||
import { setRoleStatus, roleDelete } from '@/api/system/role/index.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
@@ -12,28 +12,6 @@ function handleSearch(params) { | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
/** | |||
* @description: 打开分配权限窗口 | |||
* @param {*} row 单行数据 | |||
* @return {*} | |||
*/ | |||
function configure(row) { | |||
data.rowData = row | |||
data.configModalShow = true | |||
} | |||
/** | |||
* @description: 改变状态 | |||
* @param {*} row 选中数据 | |||
@@ -51,9 +29,8 @@ function handleStatusChange(row) { | |||
}) | |||
} | |||
// 删除接口 | |||
function deleteData(data) { | |||
deleteRole(data) | |||
function deleteData(id) { | |||
roleDelete(id) | |||
.then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
@@ -63,6 +40,21 @@ function deleteData(data) { | |||
console.log(e) | |||
}) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row || {} | |||
data.modalType = type | |||
if (type === 'relate') { | |||
data.relateShow = true | |||
} else { | |||
data.modalShow = true | |||
} | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
@@ -70,32 +62,30 @@ const data = reactive({ | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
configModalShow: false, | |||
relateShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ type: 'selection' }, | |||
{ | |||
title: '角色编号', | |||
key: 'code', | |||
align: 'center' | |||
title: '编号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center', | |||
width: 50 | |||
}, | |||
{ | |||
title: '角色名称', | |||
key: 'name', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '创建时间', | |||
key: 'createTime', | |||
key: 'roleName', | |||
align: 'center', | |||
Minwidth: 160 | |||
width: 200 | |||
}, | |||
{ | |||
title: '更新时间', | |||
key: 'updateTime', | |||
title: '角色编码', | |||
key: 'roleCode', | |||
align: 'center', | |||
Minwidth: 160 | |||
width: 200 | |||
}, | |||
{ | |||
title: '状态', | |||
@@ -107,45 +97,63 @@ const data = reactive({ | |||
data: { id: row.id, status: row.status }, | |||
rowKey: 'status', | |||
checkedValue: 1, | |||
uncheckedValue: 2, | |||
uncheckedValue: 0, | |||
onChange: handleStatusChange.bind(row) | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '描述', | |||
key: 'remark', | |||
align: 'center', | |||
ellipsis: { | |||
tooltip: true | |||
}, | |||
width: 400 | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
width: 200, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '修改', | |||
label: '详情', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'preview') | |||
}, | |||
auth: 'system:role:preview' | |||
}, | |||
{ | |||
label: '编辑', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
auth: 'system:role:edit' | |||
}, | |||
{ | |||
label: '分配权限', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
ghost: true, | |||
text: true, | |||
onClick: configure.bind(null, row) | |||
onClick: getRowData.bind(null, row, 'relate') | |||
}, | |||
auth: 'basic_list' | |||
auth: 'system:role:dispatch' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
auth: 'system:role:delete', | |||
tip: '确定删除这个角色吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
@@ -154,6 +162,7 @@ const data = reactive({ | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) |
@@ -1,168 +0,0 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="userForm" | |||
:rules="userRules" | |||
:label-width="80" | |||
label-placement="left" | |||
require-mark-placement="left" | |||
:disabled="disabled" | |||
> | |||
<template v-for="(item,index) in getFormOptions" :key="index"> | |||
<n-form-item :label="item.label" :path="item.key"> | |||
<UploadOss v-if="item.type === 'oss'" :ref="el=>{ossRefs[item.refIndex] = el}" :default-list="userForm[item.file]" @upload-status="handleUploadStatus" /> | |||
<n-input v-if="item.type === 'input'" v-model:value="userForm[item.key]" v-bind="item.props" /> | |||
<n-select v-if="item.type === 'select'" v-model:value="userForm[item.key]" v-bind="item.props" /> | |||
<n-radio-group v-if="item.type === 'radio'" v-model:value="userForm[item.key]" :name="item.key"> | |||
<n-space> | |||
<n-radio v-for="(cItem,cIndex) in item.options" :key="`${item.key}_${cIndex}`" :value="cItem.value"> {{ cItem.label }}</n-radio> | |||
</n-space> | |||
</n-radio-group> | |||
</n-form-item> | |||
</template> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form, getDeptOptions, getRoleOptions } from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import Modal from '@/components/Modal/index.vue' | |||
import UploadOss from '@/components/UploadOss/index.vue' | |||
import { addUser, editUser } from '@/api/system/user/index' | |||
export default defineComponent({ | |||
name: 'UserModal', | |||
components: { Modal, UploadOss }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => null | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建用户', | |||
'preview': '用户详情', | |||
'update': '编辑用户' | |||
} | |||
getDeptOptions() | |||
getRoleOptions() | |||
const { userForm, userRules } = form | |||
const formRef = ref() | |||
const ossRefs = ref([]) | |||
const data = reactive({ | |||
userForm: { | |||
...userForm, | |||
...props.data, | |||
roleIds: props?.data?.roles.map((item) => { return item.id }) || [] | |||
}, | |||
userRules: { | |||
...userRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
show: props.visible, | |||
title: MODAL_TYPE[props.type], | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
return { | |||
...form.formItem | |||
} | |||
}) | |||
function handleUploadStatus(status) { | |||
data.userForm.imageStatus = status | |||
} | |||
function handleConfirm() { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const uploads = ossRefs.value.map((item, index) => { | |||
return item.startUpload() | |||
}) | |||
Promise.all(uploads) | |||
.then(response => { | |||
const isError = response.map((item) => { | |||
return item.includes('error') | |||
}) | |||
if (!isError.includes(true)) { | |||
const imageStr = response.join() | |||
const params = { | |||
...data.userForm, | |||
// avatar: imageStr | |||
avatar: '/imagedir/2uim2hpl94u_1655380018696.png' | |||
} | |||
if (params.id) { | |||
/* 编辑 */ | |||
editUser(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
addUser(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('文件上传失败,请稍后重试') | |||
} | |||
}) | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
ossRefs, | |||
getModalOptions, | |||
getFormOptions, | |||
handleUploadStatus, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> |
@@ -1,105 +0,0 @@ | |||
<template> | |||
<div> | |||
<n-card> | |||
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:row-key="(row) => row.id" | |||
:request="loadDataTable" | |||
size="large" | |||
@update:checked-row-keys="handleCheck" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
<n-popconfirm | |||
negative-text="取消" | |||
positive-text="确认" | |||
@positive-click="deleteComplex" | |||
> | |||
<template #trigger> | |||
<n-button type="primary"> 删除 </n-button> | |||
</template> | |||
确认要删除选中数据吗? | |||
</n-popconfirm> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<UserModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :data="rowData" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import { search, fetchRolesOption } from './tools/search.js' | |||
import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import UserModal from './components/UserModal.vue' | |||
import { getUserList } from '@/api/system/user/index.js' | |||
import { unref, ref, toRefs, reactive, onUnmounted } from 'vue' | |||
export default { | |||
name: 'MenuPage', | |||
components: { dataTable, UserModal, headSearch }, | |||
setup() { | |||
fetchRolesOption() | |||
const data = reactive({ | |||
...toRefs(table), | |||
...toRefs(search) | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await getUserList(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
// 选择表格数据 | |||
const selectedIds = ref([]) | |||
function handleCheck(rowKeys) { | |||
selectedIds.value = rowKeys | |||
} | |||
// 批量删除 | |||
function deleteComplex() { | |||
if (selectedIds.value.length) { | |||
data.deleteData(selectedIds.value) | |||
} else { | |||
$message.warning('请至少选中一条数据') | |||
} | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal, | |||
selectedIds, | |||
deleteComplex, | |||
handleCheck | |||
} | |||
}, | |||
methods: { | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -1,52 +0,0 @@ | |||
import { ref, reactive } from 'vue' | |||
import { USER_STATUS } from '@/utils/dictionary.js' | |||
import { getDeptAll } from '@/api/system/dept/index' | |||
import { getRoleAll } from '@/api/system/role/index' | |||
import { dataToSelect } from '@/utils/handleData.js' | |||
const departmentOptions = ref() | |||
const rolesOptions = ref() | |||
export const form = reactive({ | |||
userForm: { | |||
avatar: '', | |||
code: '', | |||
deptId: null, | |||
username: '', | |||
realname: '', | |||
password: '', | |||
roleIds: [], | |||
status: 1, | |||
note: '' | |||
}, | |||
userRules: { | |||
imageStatus: [{ required: true, message: '请选择头像', type: 'string', trigger: 'blur' }], | |||
code: [{ required: true, message: '请输入编号', trigger: 'blur' }], | |||
realname: [{ required: true, message: '请输入用户姓名', type: 'string', trigger: 'blur' }], | |||
deptId: [{ required: true, message: '请选择部门', type: 'number', trigger: 'blur' }], | |||
status: [{ required: true, message: '请选择状态', type: 'number', trigger: 'blur' }], | |||
roleIds: [{ required: true, message: '请选择角色', type: 'array', trigger: 'blur' }], | |||
username: [{ required: true, message: '请输入用户账号', type: 'string', trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
// { type: 'oss', key: 'imageStatus', label: '头像', file: 'avatar', refIndex: 0 }, | |||
{ type: 'oss', key: 'imageStatus', label: '头像', file: 'avatar' }, | |||
{ type: 'input', key: 'code', label: '用户编号', props: { maxlength: '20', placeholder: '请输入用户编号', clearable: true }}, | |||
{ type: 'input', key: 'username', label: '用户账号', props: { maxlength: '20', placeholder: '请输入用户账号', clearable: true }}, | |||
{ type: 'input', key: 'password', label: '登录密码', props: { type: 'password', maxlength: '20', placeholder: '请输入登录密码', clearable: true }}, | |||
{ type: 'input', key: 'realname', label: '用户姓名', props: { maxlength: '20', placeholder: '请输入用户姓名', clearable: true }}, | |||
{ type: 'select', key: 'deptId', label: '所属部门', props: { options: departmentOptions, placeholder: '请选择所属部门' }}, | |||
{ type: 'select', key: 'roleIds', label: '角色', props: { multiple: true, options: rolesOptions, placeholder: '请选择角色' }}, | |||
{ type: 'radio', key: 'status', label: '状态', options: USER_STATUS }, | |||
{ type: 'input', key: 'note', label: '备注', props: { type: 'textarea', autosize: { maxlength: '200', minRows: 3, maxRows: 3 }}} | |||
] | |||
}) | |||
export const getDeptOptions = async function() { | |||
const res = await getDeptAll() | |||
departmentOptions.value = dataToSelect(res.data, { label: 'name', value: 'id' }) | |||
} | |||
// 获取角色列表 | |||
export const getRoleOptions = async function() { | |||
const res = await getRoleAll() | |||
rolesOptions.value = dataToSelect(res.data, { label: 'name', value: 'id' }) | |||
} |
@@ -1,36 +0,0 @@ | |||
import { getRoleAll } from '@/api/system/role/index' | |||
import { ref, reactive } from 'vue' | |||
const rolesOptions = ref([]) | |||
export const search = reactive({ | |||
search: [ | |||
{ | |||
label: '用户账号', | |||
key: 'username', | |||
props: { | |||
placeholder: '请输入用户账号' | |||
} | |||
}, | |||
{ | |||
label: '用户姓名', | |||
key: 'realname', | |||
props: { | |||
placeholder: '请输入用户姓名' | |||
} | |||
}, | |||
{ | |||
label: '用户角色', | |||
type: 'select', | |||
key: 'roleId', | |||
props: { | |||
placeholder: '请选择用户角色', | |||
options: rolesOptions | |||
} | |||
} | |||
] | |||
}) | |||
export const fetchRolesOption = async function() { | |||
const res = await getRoleAll() | |||
rolesOptions.value = res.data.map((item) => { return { key: item.id, label: item.name } }) | |||
} |
@@ -1,210 +0,0 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableImage from '@/components/DataTable/tools/Image.vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import TableSwitch from '@/components/DataTable/tools/Switch.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { resetPassword, deleteUser, setUserStatus } from '@/api/system/user/index.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
// 设置状态 | |||
function setStatus(row) { | |||
setUserStatus({ id: row.data.id, status: row.value }).then(res => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
$message.success(res.msg) | |||
} else { | |||
$message.error(res.msg) | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
/** | |||
* @description: 重置密码 | |||
* @param {Number} id 选中数据id | |||
* @return {*} | |||
*/ | |||
function handlePasswordReset(id) { | |||
resetPassword({ id }) | |||
.then(res => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
/** | |||
* @description: 删除用户接口 | |||
* @param {Array} ids 用户id集合 | |||
* @return {*} | |||
*/ | |||
function deleteData(ids) { | |||
deleteUser(ids).then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ | |||
type: 'selection' | |||
}, | |||
{ | |||
title: '用户编号', | |||
key: 'code', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '头像', | |||
key: 'avatar', | |||
align: 'center', | |||
render(row) { | |||
return h(TableImage, { | |||
images: { | |||
width: 36, | |||
height: 36, | |||
src: row.avatar | |||
} | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '用户账号', | |||
key: 'username', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '用户姓名', | |||
key: 'realname', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '角色', | |||
key: 'roleList', | |||
align: 'center', | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.roles, | |||
rowKey: 'name' | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '部门', | |||
key: 'deptName', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '创建时间', | |||
key: 'createTime', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '更新时间', | |||
key: 'updateTime', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '状态', | |||
key: 'status', | |||
align: 'center', | |||
width: 100, | |||
render(row) { | |||
return h(TableSwitch, { | |||
data: { id: row.id, status: row.status }, | |||
rowKey: 'status', | |||
checkedValue: 1, | |||
uncheckedValue: 2, | |||
onChange: setStatus.bind(row) | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '修改', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '重置密码', | |||
type: 'popconfirm', | |||
tip: '确定要重置为123456吗?', | |||
props: { | |||
onPositiveClick: handlePasswordReset.bind(null, row.id) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -13,8 +13,8 @@ | |||
size="large" | |||
@update:checked-row-keys="handleCheck" | |||
> | |||
<template v-if="roleType === 1" #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
<template #tableTitle> | |||
<n-button v-if="permissionStore.includes('task:all:create')" type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
@@ -42,6 +42,7 @@ import UserModal from './components/UserModal.vue' | |||
import { getTaskList } from '@/api/task/index' | |||
import { unref, ref, toRefs, reactive, onUnmounted } from 'vue' | |||
import { useUserStore } from '@/store/modules/user' | |||
import { usePermissionStore } from '@/store/modules/permission' | |||
export default { | |||
name: 'TaskManage', | |||
@@ -52,6 +53,7 @@ export default { | |||
...toRefs(table), | |||
...toRefs(search) | |||
}) | |||
const permissionStore = usePermissionStore().accessPermissionCodes // 按钮权限 | |||
const status = ['任务待分配', '任务已分配', '飞手已接单', '任务飞行中', '任务已完成'] | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
@@ -99,6 +101,7 @@ export default { | |||
handleModal, | |||
selectedIds, | |||
// deleteComplex, | |||
permissionStore, | |||
handleCheck, | |||
roleType | |||
} |
@@ -132,27 +132,23 @@ const data = reactive({ | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: editHandle.bind(null, row, 'update'), | |||
style: { | |||
display: row.status > 10 || row.inspectionType !== 1 || roleType.value !== 1 ? 'none' : '' | |||
} | |||
onClick: editHandle.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
show: row.status > 10 || row.inspectionType !== 1, | |||
auth: 'task:all:edit' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, row.id) | |||
}, | |||
auth: 'task:all:delete', | |||
show: row.status > 15 || row.inspectionType !== 1, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary', | |||
style: { | |||
display: row.status > 15 || row.inspectionType !== 1 || roleType.value !== 1 ? 'none' : '' | |||
} | |||
type: 'primary' | |||
} | |||
} | |||
], |
@@ -27,7 +27,7 @@ | |||
</template> | |||
<script> | |||
import { form } from '../tools/form.js' | |||
import { form, getRoleOptions } from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import { userCreate, userUpdate } from '@/api/system/user.js' | |||
import Modal from '@/components/Modal/index.vue' | |||
@@ -58,6 +58,7 @@ export default defineComponent({ | |||
'preview': '人员详情', | |||
'update': '编辑人员信息' | |||
} | |||
getRoleOptions() | |||
const { userForm, userRules } = form | |||
const formRef = ref() | |||
const data = reactive({ |
@@ -10,7 +10,7 @@ | |||
size="large" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
<n-button v-if="permissionStore.includes('user:menber:create')" type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
@@ -21,21 +21,24 @@ | |||
<script> | |||
import search from './tools/search.js' | |||
import table from './tools/table.js' | |||
import { tableData, getRoleOptions } from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import UserModal from './components/UserModal.vue' | |||
import { unref, toRefs, reactive, onUnmounted } from 'vue' | |||
import { userPage } from '@/api/system/user.js' | |||
import { usePermissionStore } from '@/store/modules/permission' | |||
export default { | |||
name: 'UserManage', | |||
components: { dataTable, UserModal, headSearch }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table), | |||
...toRefs(tableData), | |||
search | |||
}) | |||
const permissionStore = usePermissionStore().accessPermissionCodes // 按钮权限 | |||
getRoleOptions() | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
@@ -59,7 +62,8 @@ export default { | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal | |||
handleModal, | |||
permissionStore | |||
} | |||
} | |||
} |
@@ -1,12 +1,13 @@ | |||
import { reactive } from 'vue' | |||
import { ROLE_TYPE } from '@/utils/dictionary.js' | |||
import { reactive, ref } from 'vue' | |||
import { isPhone } from '@/utils/is.js' | |||
import { getRoleAll } from '@/api/system/role' | |||
const roleOptions = ref([]) | |||
export const form = reactive({ | |||
userForm: { | |||
realname: null, | |||
mobile: null, | |||
type: null, | |||
roleId: null, | |||
username: null, | |||
password: null | |||
}, | |||
@@ -16,15 +17,25 @@ export const form = reactive({ | |||
// { required: true, message: '请输入联系电话', type: 'string', trigger: 'blur' }, | |||
{ required: true, message: '请输入正确的联系电话', pattern: /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/, trigger: 'blur' } | |||
], | |||
type: [{ required: true, type: 'number', message: '请选择身份', trigger: 'blur' }], | |||
roleId: [{ required: true, type: 'number', message: '请选择身份', trigger: 'blur' }], | |||
username: [{ required: true, message: '账号为英文和数字', pattern: /^[a-zA-Z0-9]+$/, trigger: 'blur' }], | |||
password: [{ required: true, message: '密码为6到20位的大小写英文和数字组成的', pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$)([^\u4e00-\u9fa5\s]){6,20}$/, trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
{ type: 'input', key: 'realname', label: '姓名', props: { maxlength: '5', placeholder: '请输入姓名', clearable: true }}, | |||
{ type: 'input', key: 'mobile', label: '联系电话', props: { maxlength: '20', placeholder: '请输入联系电话', clearable: true }}, | |||
{ type: 'select', key: 'type', label: '身份选择', props: { options: ROLE_TYPE, clearable: true }}, | |||
{ type: 'select', key: 'roleId', label: '身份选择', props: { options: roleOptions, clearable: true }}, | |||
{ type: 'input', key: 'username', label: '帐号', props: { maxlength: '20', placeholder: '请输入帐号', clearable: true }}, | |||
{ type: 'input', key: 'password', label: '初始密码', props: { maxlength: '20', placeholder: '请输入初始密码', clearable: true }} | |||
] | |||
}) | |||
export const getRoleOptions = async function() { | |||
const res = await getRoleAll() | |||
if (res.code === 0) { | |||
roleOptions.value = res.data.map(it => ({ | |||
...it, | |||
label: it.roleName, | |||
value: it.id | |||
})) | |||
} | |||
} |
@@ -2,9 +2,20 @@ import { h, ref, reactive } from 'vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import TableSwitch from '@/components/DataTable/tools/Switch.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { ROLE_TYPE } from '@/utils/dictionary.js' | |||
import { userUpdate } from '@/api/system/user.js' | |||
import { getRoleAll } from '@/api/system/role' | |||
const roleOptions = ref([]) | |||
export const getRoleOptions = async function() { | |||
const res = await getRoleAll() | |||
if (res.code === 0) { | |||
roleOptions.value = res.data.map(it => ({ | |||
...it, | |||
label: it.roleName, | |||
value: it.id | |||
})) | |||
} | |||
} | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
@@ -21,9 +32,9 @@ function handleSearch(params) { | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
tableData.rowData = row | |||
tableData.modalType = type | |||
tableData.modalShow = true | |||
} | |||
// 设置状态 | |||
@@ -37,7 +48,7 @@ function changeStatus(row) { | |||
}) | |||
} | |||
const data = reactive({ | |||
export const tableData = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
@@ -55,14 +66,19 @@ const data = reactive({ | |||
align: 'center', | |||
width: 100 | |||
}, | |||
{ | |||
title: '账号', | |||
key: 'username', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '角色', | |||
key: 'type', | |||
key: 'roleId', | |||
align: 'center', | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.type, | |||
filters: ROLE_TYPE | |||
data: row.roleId, | |||
filters: roleOptions.value | |||
}) | |||
} | |||
}, | |||
@@ -119,7 +135,7 @@ const data = reactive({ | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
auth: 'user:menber:edit' | |||
} | |||
], | |||
align: 'center' | |||
@@ -129,4 +145,3 @@ const data = reactive({ | |||
] | |||
}) | |||
export default data |