mirror of
https://github.com/vbenjs/gf-vben-admin.git
synced 2025-01-23 03:40:19 +08:00
合并
This commit is contained in:
parent
44cda700e9
commit
5f62d64757
@ -1,5 +1,5 @@
|
||||
# Whether to open mock
|
||||
VITE_USE_MOCK = true
|
||||
VITE_USE_MOCK = false
|
||||
|
||||
# public path
|
||||
VITE_PUBLIC_PATH = /
|
||||
@ -13,7 +13,7 @@ VITE_PROXY = [["/basic-api","http://localhost:3000"],["/upload","http://localhos
|
||||
VITE_DROP_CONSOLE = false
|
||||
|
||||
# Basic interface address SPA
|
||||
VITE_GLOB_API_URL=/basic-api
|
||||
VITE_GLOB_API_URL=http://localhost:10088/api
|
||||
|
||||
# File upload address, optional
|
||||
VITE_GLOB_UPLOAD_URL=/upload
|
||||
|
@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# shellcheck source=./_/husky.sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit "$1"
|
@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
command_exists () {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Workaround for Windows 10, Git Bash and Yarn
|
||||
if command_exists winpty && test -t 1; then
|
||||
exec < /dev/tty
|
||||
fi
|
@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
|
||||
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --write--parser json'],
|
||||
'package.json': ['prettier --write'],
|
||||
'*.vue': ['eslint --fix', 'prettier --write', 'stylelint --fix'],
|
||||
'*.{scss,less,styl,html}': ['stylelint --fix', 'prettier --write'],
|
||||
'*.md': ['prettier --write'],
|
||||
};
|
@ -1,10 +0,0 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
. "$(dirname "$0")/common.sh"
|
||||
|
||||
[ -n "$CI" ] && exit 0
|
||||
|
||||
# Format and submit code according to lintstagedrc.js configuration
|
||||
npm run lint:lint-staged
|
||||
|
||||
npm run lint:pretty
|
@ -30,7 +30,6 @@
|
||||
"test:gzip": "http-server dist --cors --gzip -c-1",
|
||||
"test:br": "http-server dist --cors --brotli -c-1",
|
||||
"reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
|
||||
"prepare": "husky install",
|
||||
"gen:icon": "esno ./build/generate/icon/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
|
3
src/api/Curd.ts
Normal file
3
src/api/Curd.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
export const Curd = (params) => defHttp.post({ url: '/curd', params });
|
@ -15,7 +15,7 @@ import { defHttp } from '/@/utils/http/axios';
|
||||
enum Api {
|
||||
AccountList = '/system/getAccountList',
|
||||
IsAccountExist = '/system/accountExist',
|
||||
DeptList = '/system/getDeptList',
|
||||
DeptList = 'https://open.ys7.com/api/lapp/device/list',
|
||||
setRoleStatus = '/system/setRoleStatus',
|
||||
MenuList = '/system/getMenuList',
|
||||
RolePageList = '/system/getRoleListByPage',
|
||||
@ -26,7 +26,7 @@ export const getAccountList = (params: AccountParams) =>
|
||||
defHttp.get<AccountListGetResultModel>({ url: Api.AccountList, params });
|
||||
|
||||
export const getDeptList = (params?: DeptListItem) =>
|
||||
defHttp.get<DeptListGetResultModel>({ url: Api.DeptList, params });
|
||||
defHttp.post<DeptListGetResultModel>({ url: Api.DeptList, params });
|
||||
|
||||
export const getMenuList = (params?: MenuParams) =>
|
||||
defHttp.get<MenuListGetResultModel>({ url: Api.MenuList, params });
|
||||
|
@ -2,7 +2,7 @@ import { defHttp } from '/@/utils/http/axios';
|
||||
import { getMenuListResultModel } from './model/menuModel';
|
||||
|
||||
enum Api {
|
||||
GetMenuList = '/getMenuList',
|
||||
GetMenuList = '/router/list',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import {
|
||||
LoginParams,
|
||||
GetUserInfoByUserIdParams,
|
||||
GetUserInfoByUserIdModel,
|
||||
} from './model/userModel';
|
||||
import { LoginParams, GetUserInfoModel } from './model/userModel';
|
||||
|
||||
import { ErrorMessageMode } from '/#/axios';
|
||||
|
||||
@ -25,6 +21,7 @@ export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal')
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
apiUrl: 'http://localhost:10088',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
joinParentPath(list);
|
||||
return cloneDeep(list);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ const setting: ProjectConfig = {
|
||||
settingButtonPosition: SettingButtonPositionEnum.AUTO,
|
||||
|
||||
// Permission mode
|
||||
permissionMode: PermissionModeEnum.ROUTE_MAPPING,
|
||||
permissionMode: PermissionModeEnum.BACK,
|
||||
|
||||
// Permission-related cache is stored in sessionStorage or localStorage
|
||||
permissionCacheType: CacheTypeEnum.LOCAL,
|
||||
|
@ -184,7 +184,7 @@ export const usePermissionStore = defineStore({
|
||||
// this function may only need to be executed once, and the actual project can be put at the right time by itself
|
||||
let routeList: AppRouteRecordRaw[] = [];
|
||||
try {
|
||||
this.changePermissionCode();
|
||||
// this.changePermissionCode();
|
||||
routeList = (await getMenuList()) as AppRouteRecordRaw[];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
@ -83,13 +83,13 @@ export class VAxios {
|
||||
const {
|
||||
headers: { ignoreCancelToken },
|
||||
} = config;
|
||||
|
||||
const ignoreCancel =
|
||||
ignoreCancelToken !== undefined
|
||||
? ignoreCancelToken
|
||||
: this.options.requestOptions?.ignoreCancelToken;
|
||||
|
||||
!ignoreCancel && axiosCanceler.addPending(config);
|
||||
|
||||
if (requestInterceptors && isFunction(requestInterceptors)) {
|
||||
config = requestInterceptors(config, this.options);
|
||||
}
|
||||
|
@ -52,8 +52,18 @@ const transform: AxiosTransform = {
|
||||
|
||||
// 这里逻辑可以根据项目进行修改
|
||||
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
|
||||
if (!hasSuccess) {
|
||||
if (hasSuccess) {
|
||||
if (message) {
|
||||
notification.success({ message });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
checkStatus(code, message, options.errorMessageMode);
|
||||
// if (options.errorMessageMode === 'modal') {
|
||||
// createErrorModal({ title: t('sys.api.errorTip'), content: message });
|
||||
// } else if (options.errorMessageMode === 'message') {
|
||||
// createMessage.error(message);
|
||||
// }
|
||||
// errorMessageMode=‘modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
||||
if (options.errorMessageMode === 'modal') {
|
||||
createErrorModal({ title: t('sys.api.errorTip'), content: message });
|
||||
@ -63,38 +73,8 @@ const transform: AxiosTransform = {
|
||||
}
|
||||
Promise.reject(new Error(message));
|
||||
return message;
|
||||
}
|
||||
|
||||
// 接口请求成功,直接返回结果
|
||||
if (code === ResultEnum.SUCCESS) {
|
||||
if (message) {
|
||||
notification.success({ message });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// 接口请求错误,统一提示错误信息
|
||||
if (code === ResultEnum.ERROR) {
|
||||
if (message) {
|
||||
createMessage.error(data.message);
|
||||
Promise.reject(new Error(message));
|
||||
} else {
|
||||
const msg = t('sys.api.errorMessage');
|
||||
createMessage.error(msg);
|
||||
Promise.reject(new Error(msg));
|
||||
}
|
||||
throw new Error( t('sys.api.apiRequestFailed'));
|
||||
}
|
||||
// 登录超时
|
||||
if (code === ResultEnum.TIMEOUT) {
|
||||
const timeoutMsg = t('sys.api.timeoutMessage');
|
||||
createErrorModal({
|
||||
title: t('sys.api.operationFailed'),
|
||||
content: timeoutMsg,
|
||||
});
|
||||
Promise.reject(new Error(timeoutMsg));
|
||||
return message;
|
||||
}
|
||||
return message;
|
||||
|
||||
},
|
||||
|
||||
// 请求之前处理config
|
||||
@ -174,6 +154,7 @@ const transform: AxiosTransform = {
|
||||
responseInterceptorsCatch: (error: any) => {
|
||||
const { t } = useI18n();
|
||||
const errorLogStore = useErrorLogStoreWithOut();
|
||||
|
||||
errorLogStore.addAjaxErrorInfo(error);
|
||||
const { response, code, message, config } = error || {};
|
||||
const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none';
|
||||
@ -212,8 +193,8 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
||||
{
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
|
||||
// authentication schemes,e.g: Bearer
|
||||
// authenticationScheme: 'Bearer',
|
||||
authenticationScheme: 'Bearer',
|
||||
// authenticationScheme: '',
|
||||
timeout: 10 * 1000,
|
||||
// 基础接口地址
|
||||
// baseURL: globSetting.apiUrl,
|
||||
|
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
|
||||
{{ formRef }}
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
@ -16,10 +17,11 @@
|
||||
emits: ['success', 'register'],
|
||||
setup(_, { emit }) {
|
||||
const isUpdate = ref(true);
|
||||
|
||||
const formRef = ref();
|
||||
const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: formSchema,
|
||||
model: formRef,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
@ -55,7 +57,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
return { registerModal, registerForm, getTitle, handleSubmit };
|
||||
return { registerModal, registerForm, getTitle, handleSubmit, formRef };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
60
src/views/dept/DeptModal.vue
Normal file
60
src/views/dept/DeptModal.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema } from './dept.data';
|
||||
|
||||
import { getDeptList } from '/@/api/demo/system';
|
||||
export default defineComponent({
|
||||
name: 'DeptModal',
|
||||
components: { BasicModal, BasicForm },
|
||||
emits: ['success', 'register'],
|
||||
setup(_, { emit }) {
|
||||
const isUpdate = ref(true);
|
||||
const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
|
||||
if (unref(isUpdate)) {
|
||||
setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
// const treeData = await getDeptList();
|
||||
// updateSchema({
|
||||
// field: 'parentDept',
|
||||
// componentProps: { treeData },
|
||||
// });
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增部门' : '编辑部门'));
|
||||
|
||||
async function handleSubmit() {
|
||||
// try {
|
||||
// const values = await validate();
|
||||
// setModalProps({ confirmLoading: true });
|
||||
// // TODO custom api
|
||||
// console.log(values);
|
||||
// closeModal();
|
||||
// emit('success');
|
||||
// } finally {
|
||||
// setModalProps({ confirmLoading: false });
|
||||
// }
|
||||
}
|
||||
|
||||
return { registerModal, registerForm, getTitle, handleSubmit };
|
||||
},
|
||||
});
|
||||
</script>
|
108
src/views/dept/dept.data.ts
Normal file
108
src/views/dept/dept.data.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import { h } from 'vue';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '部门名称',
|
||||
dataIndex: 'deptName',
|
||||
width: 160,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'orderNo',
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 80,
|
||||
customRender: ({ record }) => {
|
||||
const status = record.status;
|
||||
const enable = ~~status === 0;
|
||||
const color = enable ? 'green' : 'red';
|
||||
const text = enable ? '启用' : '停用';
|
||||
return h(Tag, { color: color }, () => text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'deptName',
|
||||
label: '部门名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '启用', value: '0' },
|
||||
{ label: '停用', value: '1' },
|
||||
],
|
||||
},
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'deptName',
|
||||
label: '部门名称',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'parentDept',
|
||||
label: '上级部门',
|
||||
component: 'TreeSelect',
|
||||
|
||||
componentProps: {
|
||||
replaceFields: {
|
||||
title: 'deptName',
|
||||
key: 'id',
|
||||
value: 'id',
|
||||
},
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'orderNo',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioButtonGroup',
|
||||
defaultValue: '0',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '启用', value: '0' },
|
||||
{ label: '停用', value: '1' },
|
||||
],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
field: 'remark',
|
||||
component: 'InputTextArea',
|
||||
},
|
||||
];
|
100
src/views/dept/index.vue
Normal file
100
src/views/dept/index.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleCreate"> 新增部门 </a-button>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
icon: 'clarity:note-edit-line',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<DeptModal @register="registerModal" @success="handleSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { getDeptList } from '/@/api/demo/system';
|
||||
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import DeptModal from './DeptModal.vue';
|
||||
|
||||
import { columns, searchFormSchema } from './dept.data';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DeptManagement',
|
||||
components: { BasicTable, DeptModal, TableAction },
|
||||
setup() {
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerTable, { reload }] = useTable({
|
||||
title: '部门列表',
|
||||
api: getDeptList,
|
||||
columns,
|
||||
formConfig: {
|
||||
labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
pagination: false,
|
||||
striped: false,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
canResize: false,
|
||||
actionColumn: {
|
||||
width: 80,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
function handleCreate() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(record: Recordable) {
|
||||
console.log(record);
|
||||
}
|
||||
|
||||
function handleSuccess() {
|
||||
reload();
|
||||
}
|
||||
|
||||
return {
|
||||
registerTable,
|
||||
registerModal,
|
||||
handleCreate,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleSuccess,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
70
src/views/menu/MenuDrawer.vue
Normal file
70
src/views/menu/MenuDrawer.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<BasicDrawer
|
||||
v-bind="$attrs"
|
||||
@register="registerDrawer"
|
||||
showFooter
|
||||
:title="getTitle"
|
||||
width="50%"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, unref } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema } from './menu.data';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
|
||||
import { getMenuList } from '/@/api/demo/system';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MenuDrawer',
|
||||
components: { BasicDrawer, BasicForm },
|
||||
emits: ['success', 'register'],
|
||||
setup(_, { emit }) {
|
||||
const isUpdate = ref(true);
|
||||
|
||||
const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: { lg: 12, md: 24 },
|
||||
});
|
||||
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
resetFields();
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
|
||||
if (unref(isUpdate)) {
|
||||
setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
const treeData = await getMenuList();
|
||||
updateSchema({
|
||||
field: 'parentMenu',
|
||||
componentProps: { treeData },
|
||||
});
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增菜单' : '编辑菜单'));
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setDrawerProps({ confirmLoading: true });
|
||||
// TODO custom api
|
||||
console.log(values);
|
||||
closeDrawer();
|
||||
emit('success');
|
||||
} finally {
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
return { registerDrawer, registerForm, getTitle, handleSubmit };
|
||||
},
|
||||
});
|
||||
</script>
|
104
src/views/menu/index.vue
Normal file
104
src/views/menu/index.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleCreate"> 新增菜单 </a-button>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
icon: 'clarity:note-edit-line',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<MenuDrawer @register="registerDrawer" @success="handleSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import MenuDrawer from './MenuDrawer.vue';
|
||||
import { columns, searchFormSchema } from './menu.data';
|
||||
import { Curd } from '/@/api/Curd';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MenuManagement',
|
||||
components: { BasicTable, MenuDrawer, TableAction },
|
||||
setup() {
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
const [registerTable, { reload }] = useTable({
|
||||
title: '菜单列表',
|
||||
api: Curd,
|
||||
beforeFetch: (v) => {
|
||||
v.i = 'router';
|
||||
v.a = 'list';
|
||||
return v;
|
||||
},
|
||||
columns,
|
||||
formConfig: {
|
||||
labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
pagination: false,
|
||||
striped: false,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
canResize: false,
|
||||
actionColumn: {
|
||||
width: 80,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
function handleCreate() {
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(record: Recordable) {
|
||||
console.log(record);
|
||||
}
|
||||
|
||||
function handleSuccess() {
|
||||
reload();
|
||||
}
|
||||
|
||||
return {
|
||||
registerTable,
|
||||
registerDrawer,
|
||||
handleCreate,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleSuccess,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
202
src/views/menu/menu.data.ts
Normal file
202
src/views/menu/menu.data.ts
Normal file
@ -0,0 +1,202 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import { h } from 'vue';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '图标',
|
||||
dataIndex: 'icon',
|
||||
width: 50,
|
||||
customRender: ({ record }) => {
|
||||
return h(Icon, { icon: record.meta.icon });
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '权限标识',
|
||||
dataIndex: 'permission',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '组件',
|
||||
dataIndex: 'component',
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'orderNo',
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 80,
|
||||
customRender: ({ record }) => {
|
||||
const status = record.status;
|
||||
const enable = ~~status === 1;
|
||||
const color = enable ? 'green' : 'red';
|
||||
const text = enable ? '启用' : '停用';
|
||||
return h(Tag, { color: color }, () => text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'create_at',
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
|
||||
const isDir = (type: string) => type === '0';
|
||||
const isMenu = (type: string) => type === '1';
|
||||
const isButton = (type: string) => type === '2';
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'menuName',
|
||||
label: '菜单名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '启用', value: '0' },
|
||||
{ label: '停用', value: '1' },
|
||||
],
|
||||
},
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'type',
|
||||
label: '菜单类型',
|
||||
component: 'RadioButtonGroup',
|
||||
defaultValue: '0',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '目录', value: '0' },
|
||||
{ label: '菜单', value: '1' },
|
||||
{ label: '按钮', value: '2' },
|
||||
],
|
||||
},
|
||||
colProps: { lg: 24, md: 24 },
|
||||
},
|
||||
{
|
||||
field: 'menuName',
|
||||
label: '菜单名称',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
|
||||
{
|
||||
field: 'parentMenu',
|
||||
label: '上级菜单',
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
replaceFields: {
|
||||
title: 'menuName',
|
||||
key: 'id',
|
||||
value: 'id',
|
||||
},
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
field: 'orderNo',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'icon',
|
||||
label: '图标',
|
||||
component: 'IconPicker',
|
||||
required: true,
|
||||
show: ({ values }) => !isButton(values.type),
|
||||
},
|
||||
|
||||
{
|
||||
field: 'routePath',
|
||||
label: '路由地址',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
show: ({ values }) => !isButton(values.type),
|
||||
},
|
||||
{
|
||||
field: 'component',
|
||||
label: '组件路径',
|
||||
component: 'Input',
|
||||
show: ({ values }) => isMenu(values.type),
|
||||
},
|
||||
{
|
||||
field: 'permission',
|
||||
label: '权限标识',
|
||||
component: 'Input',
|
||||
show: ({ values }) => !isDir(values.type),
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioButtonGroup',
|
||||
defaultValue: '0',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '启用', value: '0' },
|
||||
{ label: '禁用', value: '1' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'isExt',
|
||||
label: '是否外链',
|
||||
component: 'RadioButtonGroup',
|
||||
defaultValue: '0',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '否', value: '0' },
|
||||
{ label: '是', value: '1' },
|
||||
],
|
||||
},
|
||||
show: ({ values }) => !isButton(values.type),
|
||||
},
|
||||
|
||||
{
|
||||
field: 'keepalive',
|
||||
label: '是否缓存',
|
||||
component: 'RadioButtonGroup',
|
||||
defaultValue: '0',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '否', value: '0' },
|
||||
{ label: '是', value: '1' },
|
||||
],
|
||||
},
|
||||
show: ({ values }) => isMenu(values.type),
|
||||
},
|
||||
|
||||
{
|
||||
field: 'show',
|
||||
label: '是否显示',
|
||||
component: 'RadioButtonGroup',
|
||||
defaultValue: '0',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '是', value: '0' },
|
||||
{ label: '否', value: '1' },
|
||||
],
|
||||
},
|
||||
show: ({ values }) => !isButton(values.type),
|
||||
},
|
||||
];
|
84
src/views/role/RoleDrawer.vue
Normal file
84
src/views/role/RoleDrawer.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<BasicDrawer
|
||||
v-bind="$attrs"
|
||||
@register="registerDrawer"
|
||||
showFooter
|
||||
:title="getTitle"
|
||||
width="500px"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<BasicForm @register="registerForm">
|
||||
<template #menu="{ model, field }">
|
||||
<BasicTree
|
||||
v-model:value="model[field]"
|
||||
:treeData="treeData"
|
||||
:replaceFields="{ title: 'menuName', key: 'id' }"
|
||||
checkable
|
||||
toolbar
|
||||
title="菜单分配"
|
||||
/>
|
||||
</template>
|
||||
</BasicForm>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, unref } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema } from './role.data';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { BasicTree, TreeItem } from '/@/components/Tree';
|
||||
|
||||
import { getMenuList } from '/@/api/demo/system';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RoleDrawer',
|
||||
components: { BasicDrawer, BasicForm, BasicTree },
|
||||
emits: ['success', 'register'],
|
||||
setup(_, { emit }) {
|
||||
const isUpdate = ref(true);
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
labelWidth: 90,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
resetFields();
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
|
||||
if (unref(isUpdate)) {
|
||||
setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
treeData.value = (await getMenuList()) as any as TreeItem[];
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增角色' : '编辑角色'));
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setDrawerProps({ confirmLoading: true });
|
||||
// TODO custom api
|
||||
console.log(values);
|
||||
closeDrawer();
|
||||
emit('success');
|
||||
} finally {
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
registerDrawer,
|
||||
registerForm,
|
||||
getTitle,
|
||||
handleSubmit,
|
||||
treeData,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
101
src/views/role/index.vue
Normal file
101
src/views/role/index.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleCreate"> 新增角色 </a-button>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
icon: 'clarity:note-edit-line',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
icon: 'ant-design:delete-outlined',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<RoleDrawer @register="registerDrawer" @success="handleSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import RoleDrawer from './RoleDrawer.vue';
|
||||
|
||||
import { columns, searchFormSchema } from './role.data';
|
||||
import { Curd } from '/@/api/Curd';
|
||||
export default defineComponent({
|
||||
name: 'RoleManagement',
|
||||
components: { BasicTable, RoleDrawer, TableAction },
|
||||
setup() {
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
const [registerTable, { reload }] = useTable({
|
||||
title: '角色列表',
|
||||
api: Curd,
|
||||
beforeFetch: (v) => {
|
||||
v.i = 'role';
|
||||
v.a = 'list';
|
||||
return v;
|
||||
},
|
||||
columns,
|
||||
formConfig: {
|
||||
labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
actionColumn: {
|
||||
width: 80,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
function handleCreate() {
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(record: Recordable) {
|
||||
console.log(record);
|
||||
}
|
||||
|
||||
function handleSuccess() {
|
||||
reload();
|
||||
}
|
||||
|
||||
return {
|
||||
registerTable,
|
||||
registerDrawer,
|
||||
handleCreate,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleSuccess,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
69
src/views/role/role.data.ts
Normal file
69
src/views/role/role.data.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '角色名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'roleNme',
|
||||
label: '角色名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '启用', value: '0' },
|
||||
{ label: '停用', value: '1' },
|
||||
],
|
||||
},
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'roleName',
|
||||
label: '角色名称',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'roleValue',
|
||||
label: '角色值',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioButtonGroup',
|
||||
defaultValue: '0',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '启用', value: '0' },
|
||||
{ label: '停用', value: '1' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
field: 'remark',
|
||||
component: 'InputTextArea',
|
||||
},
|
||||
{
|
||||
label: ' ',
|
||||
field: 'menu',
|
||||
slot: 'menu',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
@ -1,8 +1,20 @@
|
||||
<template>
|
||||
<LoginFormTitle v-show="getShow" class="enter-x" />
|
||||
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef" v-show="getShow">
|
||||
<Form
|
||||
class="p-4 enter-x"
|
||||
:model="formData"
|
||||
:rules="getFormRules"
|
||||
ref="formRef"
|
||||
v-show="getShow"
|
||||
@keypress.enter="handleLogin"
|
||||
>
|
||||
<FormItem name="account" class="enter-x">
|
||||
<Input size="large" v-model:value="formData.account" :placeholder="t('sys.login.userName')" />
|
||||
<Input
|
||||
size="large"
|
||||
v-model:value="formData.account"
|
||||
:placeholder="t('sys.login.userName')"
|
||||
class="fix-auto-fill"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem name="password" class="enter-x">
|
||||
<InputPassword
|
||||
@ -107,7 +119,7 @@
|
||||
const rememberMe = ref(false);
|
||||
|
||||
const formData = reactive({
|
||||
account: 'vben',
|
||||
account: 'admin',
|
||||
password: '123456',
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user