From 5f62d647575850c0f01ac15d3dd6784e5360244e Mon Sep 17 00:00:00 2001 From: JinMao Date: Wed, 25 Aug 2021 10:04:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 4 +- .husky/commit-msg | 6 - .husky/common.sh | 9 - .husky/lintstagedrc.js | 8 - .husky/pre-commit | 10 - package.json | 1 - src/api/Curd.ts | 3 + src/api/demo/system.ts | 4 +- src/api/sys/menu.ts | 2 +- src/api/sys/user.ts | 7 +- src/router/helper/menuHelper.ts | 1 + src/settings/projectSetting.ts | 2 +- src/store/modules/permission.ts | 2 +- src/utils/http/axios/Axios.ts | 2 +- src/utils/http/axios/index.ts | 47 +- src/views/demo/system/dept/DeptModal.vue | 6 +- src/views/dept/DeptModal.vue | 60 + src/views/dept/dept.data.ts | 108 + src/views/dept/index.vue | 100 + src/views/menu/MenuDrawer.vue | 70 + src/views/menu/index.vue | 104 + src/views/menu/menu.data.ts | 202 + src/views/role/RoleDrawer.vue | 84 + src/views/role/index.vue | 101 + src/views/role/role.data.ts | 69 + src/views/sys/login/LoginForm.vue | 18 +- yarn.lock | 8702 ++++++++++------------ 27 files changed, 5053 insertions(+), 4679 deletions(-) delete mode 100755 .husky/commit-msg delete mode 100644 .husky/common.sh delete mode 100644 .husky/lintstagedrc.js delete mode 100755 .husky/pre-commit create mode 100644 src/api/Curd.ts create mode 100644 src/views/dept/DeptModal.vue create mode 100644 src/views/dept/dept.data.ts create mode 100644 src/views/dept/index.vue create mode 100644 src/views/menu/MenuDrawer.vue create mode 100644 src/views/menu/index.vue create mode 100644 src/views/menu/menu.data.ts create mode 100644 src/views/role/RoleDrawer.vue create mode 100644 src/views/role/index.vue create mode 100644 src/views/role/role.data.ts diff --git a/.env.development b/.env.development index 92361233..8b4f9865 100644 --- a/.env.development +++ b/.env.development @@ -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 diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100755 index 567ff71f..00000000 --- a/.husky/commit-msg +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -# shellcheck source=./_/husky.sh -. "$(dirname "$0")/_/husky.sh" - -npx --no-install commitlint --edit "$1" diff --git a/.husky/common.sh b/.husky/common.sh deleted file mode 100644 index 9d5129bd..00000000 --- a/.husky/common.sh +++ /dev/null @@ -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 diff --git a/.husky/lintstagedrc.js b/.husky/lintstagedrc.js deleted file mode 100644 index 08d8c9e8..00000000 --- a/.husky/lintstagedrc.js +++ /dev/null @@ -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'], -}; diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index c7d15f24..00000000 --- a/.husky/pre-commit +++ /dev/null @@ -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 diff --git a/package.json b/package.json index 12fa4e67..3779efed 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/api/Curd.ts b/src/api/Curd.ts new file mode 100644 index 00000000..b56c24a6 --- /dev/null +++ b/src/api/Curd.ts @@ -0,0 +1,3 @@ +import { defHttp } from '/@/utils/http/axios'; + +export const Curd = (params) => defHttp.post({ url: '/curd', params }); diff --git a/src/api/demo/system.ts b/src/api/demo/system.ts index f5c296b5..1456470a 100644 --- a/src/api/demo/system.ts +++ b/src/api/demo/system.ts @@ -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({ url: Api.AccountList, params }); export const getDeptList = (params?: DeptListItem) => - defHttp.get({ url: Api.DeptList, params }); + defHttp.post({ url: Api.DeptList, params }); export const getMenuList = (params?: MenuParams) => defHttp.get({ url: Api.MenuList, params }); diff --git a/src/api/sys/menu.ts b/src/api/sys/menu.ts index de9fa563..057c65b3 100644 --- a/src/api/sys/menu.ts +++ b/src/api/sys/menu.ts @@ -2,7 +2,7 @@ import { defHttp } from '/@/utils/http/axios'; import { getMenuListResultModel } from './model/menuModel'; enum Api { - GetMenuList = '/getMenuList', + GetMenuList = '/router/list', } /** diff --git a/src/api/sys/user.ts b/src/api/sys/user.ts index 237b8ba5..537e5c8a 100644 --- a/src/api/sys/user.ts +++ b/src/api/sys/user.ts @@ -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', }, ); } diff --git a/src/router/helper/menuHelper.ts b/src/router/helper/menuHelper.ts index a1307124..2b16f5ea 100644 --- a/src/router/helper/menuHelper.ts +++ b/src/router/helper/menuHelper.ts @@ -66,6 +66,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi }; }, }); + joinParentPath(list); return cloneDeep(list); } diff --git a/src/settings/projectSetting.ts b/src/settings/projectSetting.ts index 0d4415b8..f726fce9 100644 --- a/src/settings/projectSetting.ts +++ b/src/settings/projectSetting.ts @@ -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, diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts index aa9d0f4f..e9ce3ad9 100644 --- a/src/store/modules/permission.ts +++ b/src/store/modules/permission.ts @@ -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); diff --git a/src/utils/http/axios/Axios.ts b/src/utils/http/axios/Axios.ts index f2d57be1..3471bdaf 100644 --- a/src/utils/http/axios/Axios.ts +++ b/src/utils/http/axios/Axios.ts @@ -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); } diff --git a/src/utils/http/axios/index.ts b/src/utils/http/axios/index.ts index d1277ee3..8a04d4fa 100644 --- a/src/utils/http/axios/index.ts +++ b/src/utils/http/axios/index.ts @@ -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) { { // 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, diff --git a/src/views/demo/system/dept/DeptModal.vue b/src/views/demo/system/dept/DeptModal.vue index 005148de..00c2aed5 100644 --- a/src/views/demo/system/dept/DeptModal.vue +++ b/src/views/demo/system/dept/DeptModal.vue @@ -1,5 +1,6 @@ @@ -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 }; }, }); diff --git a/src/views/dept/DeptModal.vue b/src/views/dept/DeptModal.vue new file mode 100644 index 00000000..69923517 --- /dev/null +++ b/src/views/dept/DeptModal.vue @@ -0,0 +1,60 @@ + + diff --git a/src/views/dept/dept.data.ts b/src/views/dept/dept.data.ts new file mode 100644 index 00000000..6bdd1cae --- /dev/null +++ b/src/views/dept/dept.data.ts @@ -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', + }, +]; diff --git a/src/views/dept/index.vue b/src/views/dept/index.vue new file mode 100644 index 00000000..9b7d1ca2 --- /dev/null +++ b/src/views/dept/index.vue @@ -0,0 +1,100 @@ + + diff --git a/src/views/menu/MenuDrawer.vue b/src/views/menu/MenuDrawer.vue new file mode 100644 index 00000000..04025102 --- /dev/null +++ b/src/views/menu/MenuDrawer.vue @@ -0,0 +1,70 @@ + + diff --git a/src/views/menu/index.vue b/src/views/menu/index.vue new file mode 100644 index 00000000..651b1533 --- /dev/null +++ b/src/views/menu/index.vue @@ -0,0 +1,104 @@ + + diff --git a/src/views/menu/menu.data.ts b/src/views/menu/menu.data.ts new file mode 100644 index 00000000..2ed1a631 --- /dev/null +++ b/src/views/menu/menu.data.ts @@ -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), + }, +]; diff --git a/src/views/role/RoleDrawer.vue b/src/views/role/RoleDrawer.vue new file mode 100644 index 00000000..b41019d0 --- /dev/null +++ b/src/views/role/RoleDrawer.vue @@ -0,0 +1,84 @@ + + diff --git a/src/views/role/index.vue b/src/views/role/index.vue new file mode 100644 index 00000000..5917ff0c --- /dev/null +++ b/src/views/role/index.vue @@ -0,0 +1,101 @@ + + diff --git a/src/views/role/role.data.ts b/src/views/role/role.data.ts new file mode 100644 index 00000000..440d4d5e --- /dev/null +++ b/src/views/role/role.data.ts @@ -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', + }, +]; diff --git a/src/views/sys/login/LoginForm.vue b/src/views/sys/login/LoginForm.vue index d74e0fac..2d51f0f0 100644 --- a/src/views/sys/login/LoginForm.vue +++ b/src/views/sys/login/LoginForm.vue @@ -1,8 +1,20 @@