diff --git a/.env.base b/.env.base index 799b9e1..e499588 100644 --- a/.env.base +++ b/.env.base @@ -2,10 +2,10 @@ NODE_ENV=development # 接口前缀 -VITE_API_BASE_PATH=base +VITE_API_BASE_PATH= # 打包路径 VITE_BASE_PATH=/ # 标题 -VITE_APP_TITLE=ElementAdmin +VITE_APP_TITLE=ElementAdmin \ No newline at end of file diff --git a/.env.dev b/.env.dev index b5b794e..afc3cee 100644 --- a/.env.dev +++ b/.env.dev @@ -2,7 +2,7 @@ NODE_ENV=production # 接口前缀 -VITE_API_BASE_PATH=dev +VITE_API_BASE_PATH= # 打包路径 VITE_BASE_PATH=/dist-dev/ diff --git a/.env.gitee b/.env.gitee index 412f1c4..9ac2a52 100644 --- a/.env.gitee +++ b/.env.gitee @@ -2,7 +2,7 @@ NODE_ENV=production # 接口前缀 -VITE_API_BASE_PATH=pro +VITE_API_BASE_PATH= # 打包路径 VITE_BASE_PATH=/vue-element-plus-admin/ diff --git a/.env.pro b/.env.pro index d21f25c..432fc46 100644 --- a/.env.pro +++ b/.env.pro @@ -2,7 +2,7 @@ NODE_ENV=production # 接口前缀 -VITE_API_BASE_PATH=pro +VITE_API_BASE_PATH= # 打包路径 VITE_BASE_PATH=/ diff --git a/.env.test b/.env.test index 00d8f8a..b6eb7d9 100644 --- a/.env.test +++ b/.env.test @@ -2,7 +2,7 @@ NODE_ENV=production # 接口前缀 -VITE_API_BASE_PATH=test +VITE_API_BASE_PATH= # 打包路径 VITE_BASE_PATH=/dist-test/ diff --git a/.eslintrc.js b/.eslintrc.js index 4cad823..8068163 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -65,6 +65,7 @@ module.exports = defineConfig({ } ], 'vue/multi-word-component-names': 'off', - 'vue/no-v-html': 'off' + 'vue/no-v-html': 'off', + 'vue/require-toggle-inside-transition': 'off' } }) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8bafd70..f0e4b08 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,11 @@ { "typescript.tsdk": "node_modules/typescript/lib", - "prettier.enable": false, + "prettier.enable": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "[vue]": { - "editor.defaultFormatter": "rvest.vs-code-prettier-eslint" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "i18n-ally.localesPaths": ["src/locales"], "i18n-ally.keystyle": "nested", diff --git a/mock/department/index.ts b/mock/department/index.ts index 67ac648..598f926 100644 --- a/mock/department/index.ts +++ b/mock/department/index.ts @@ -124,8 +124,6 @@ export default [ email: '@EMAIL', // 创建时间 createTime: '@datetime', - // 角色 - role: '@first', // 用户id id: toAnyString() }) diff --git a/mock/menu/index.ts b/mock/menu/index.ts index 488b82c..bf04570 100644 --- a/mock/menu/index.ts +++ b/mock/menu/index.ts @@ -24,6 +24,8 @@ export default [ name: 'Dashboard', status: Mock.Random.integer(0, 1), id: 1, + type: 0, + parentId: undefined, title: '首页', meta: { title: '首页', @@ -37,10 +39,23 @@ export default [ name: 'Analysis', status: Mock.Random.integer(0, 1), id: 2, + type: 1, + parentId: 1, title: '分析页', + permissionList: [ + { + label: '新增', + value: 'add' + }, + { + label: '编辑', + value: 'edit' + } + ], meta: { title: '分析页', - noCache: true + noCache: true, + permission: ['add', 'edit'] } }, { @@ -49,7 +64,23 @@ export default [ name: 'Workplace', status: Mock.Random.integer(0, 1), id: 3, + type: 1, + parentId: 1, title: '工作台', + permissionList: [ + { + label: '新增', + value: 'add' + }, + { + label: '编辑', + value: 'edit' + }, + { + label: '删除', + value: 'delete' + } + ], meta: { title: '工作台', noCache: true @@ -60,7 +91,6 @@ export default [ { path: '/external-link', component: '#', - title: '文档', meta: { title: '文档', icon: 'clarity:document-solid' @@ -68,12 +98,17 @@ export default [ name: 'ExternalLink', status: Mock.Random.integer(0, 1), id: 4, + type: 0, + parentId: undefined, + title: '文档', children: [ { path: 'https://element-plus-admin-doc.cn/', name: 'DocumentLink', status: Mock.Random.integer(0, 1), id: 5, + type: 1, + parentId: 4, title: '文档', meta: { title: '文档' @@ -88,6 +123,8 @@ export default [ name: 'Level', status: Mock.Random.integer(0, 1), id: 6, + type: 0, + parentId: undefined, title: '菜单', meta: { title: '菜单', @@ -100,8 +137,10 @@ export default [ component: '##', status: Mock.Random.integer(0, 1), id: 7, - redirect: '/level/menu1/menu1-1/menu1-1-1', + type: 0, + parentId: 6, title: '菜单1', + redirect: '/level/menu1/menu1-1/menu1-1-1', meta: { title: '菜单1' }, @@ -112,8 +151,10 @@ export default [ component: '##', status: Mock.Random.integer(0, 1), id: 8, - redirect: '/level/menu1/menu1-1/menu1-1-1', + type: 0, + parentId: 7, title: '菜单1-1', + redirect: '/level/menu1/menu1-1/menu1-1-1', meta: { title: '菜单1-1', alwaysShow: true @@ -125,7 +166,8 @@ export default [ component: 'views/Level/Menu111', status: Mock.Random.integer(0, 1), id: 9, - permission: ['edit', 'add', 'delete'], + type: 1, + parentId: 8, title: '菜单1-1-1', meta: { title: '菜单1-1-1' @@ -139,7 +181,8 @@ export default [ component: 'views/Level/Menu12', status: Mock.Random.integer(0, 1), id: 10, - permission: ['edit', 'add', 'delete'], + type: 1, + parentId: 7, title: '菜单1-2', meta: { title: '菜单1-2' @@ -153,7 +196,8 @@ export default [ component: 'views/Level/Menu2', status: Mock.Random.integer(0, 1), id: 11, - permission: ['edit', 'add', 'delete'], + type: 1, + parentId: 6, title: '菜单2', meta: { title: '菜单2' @@ -168,6 +212,8 @@ export default [ name: 'Example', status: Mock.Random.integer(0, 1), id: 12, + type: 0, + parentId: undefined, title: '综合示例', meta: { title: '综合示例', @@ -181,11 +227,29 @@ export default [ name: 'ExampleDialog', status: Mock.Random.integer(0, 1), id: 13, + type: 1, + parentId: 12, title: '综合示例-弹窗', - permission: ['edit', 'add', 'delete'], + permissionList: [ + { + label: '新增', + value: 'add' + }, + { + label: '编辑', + value: 'edit' + }, + { + label: '删除', + value: 'delete' + }, + { + label: '查看', + value: 'view' + } + ], meta: { - title: '综合示例-弹窗', - permission: ['edit', 'add'] + title: '综合示例-弹窗' } }, { @@ -194,11 +258,29 @@ export default [ name: 'ExamplePage', status: Mock.Random.integer(0, 1), id: 14, - permission: ['edit', 'add', 'delete'], + type: 1, + parentId: 12, title: '综合示例-页面', + permissionList: [ + { + label: '新增', + value: 'edit' + }, + { + label: '编辑', + value: 'edit' + }, + { + label: '删除', + value: 'delete' + }, + { + label: '查看', + value: 'view' + } + ], meta: { - title: '综合示例-页面', - permission: ['edit', 'add'] + title: '综合示例-页面' } }, { @@ -207,7 +289,8 @@ export default [ name: 'ExampleAdd', status: Mock.Random.integer(0, 1), id: 15, - permission: ['edit', 'add', 'delete'], + type: 1, + parentId: 12, title: '综合示例-新增', meta: { title: '综合示例-新增', @@ -215,8 +298,7 @@ export default [ noCache: true, hidden: true, showMainRoute: true, - activeMenu: '/example/example-page', - permission: ['delete', 'add'] + activeMenu: '/example/example-page' } }, { @@ -225,7 +307,8 @@ export default [ name: 'ExampleEdit', status: Mock.Random.integer(0, 1), id: 16, - permission: ['edit', 'add', 'delete'], + type: 1, + parentId: 12, title: '综合示例-编辑', meta: { title: '综合示例-编辑', @@ -233,8 +316,7 @@ export default [ noCache: true, hidden: true, showMainRoute: true, - activeMenu: '/example/example-page', - permission: ['delete', 'add'] + activeMenu: '/example/example-page' } }, { @@ -243,7 +325,8 @@ export default [ name: 'ExampleDetail', status: Mock.Random.integer(0, 1), id: 17, - permission: ['edit', 'add', 'delete'], + type: 1, + parentId: 12, title: '综合示例-详情', meta: { title: '综合示例-详情', @@ -251,8 +334,7 @@ export default [ noCache: true, hidden: true, showMainRoute: true, - activeMenu: '/example/example-page', - permission: ['delete', 'edit'] + activeMenu: '/example/example-page' } } ] diff --git a/package.json b/package.json index cc09f03..956313f 100644 --- a/package.json +++ b/package.json @@ -34,12 +34,12 @@ "@wangeditor/editor-for-vue": "^5.1.10", "@zxcvbn-ts/core": "^3.0.4", "animate.css": "^4.1.1", - "axios": "^1.5.1", + "axios": "^1.6.0", "dayjs": "^1.11.10", "driver.js": "^1.3.0", "echarts": "^5.4.3", "echarts-wordcloud": "^2.1.0", - "element-plus": "^2.4.0", + "element-plus": "^2.4.2", "lodash-es": "^4.17.21", "mitt": "^3.0.1", "mockjs": "^1.1.0", @@ -49,62 +49,62 @@ "qrcode": "^1.5.3", "qs": "^6.11.2", "url": "^0.11.3", - "vue": "3.3.4", - "vue-i18n": "9.5.0", + "vue": "3.3.8", + "vue-i18n": "9.6.5", "vue-json-pretty": "^2.2.4", "vue-router": "^4.2.5", "vue-types": "^5.1.1" }, "devDependencies": { - "@commitlint/cli": "^17.7.2", - "@commitlint/config-conventional": "^17.7.0", - "@iconify/json": "^2.2.128", - "@intlify/unplugin-vue-i18n": "^1.4.0", + "@commitlint/cli": "^18.2.0", + "@commitlint/config-conventional": "^18.1.0", + "@iconify/json": "^2.2.138", + "@intlify/unplugin-vue-i18n": "^1.5.0", "@purge-icons/generated": "^0.9.0", - "@types/fs-extra": "^11.0.2", - "@types/inquirer": "^9.0.4", - "@types/lodash-es": "^4.17.9", - "@types/node": "^20.8.6", - "@types/nprogress": "^0.2.1", - "@types/qrcode": "^1.5.2", - "@types/qs": "^6.9.8", - "@types/sortablejs": "^1.15.3", - "@typescript-eslint/eslint-plugin": "^6.7.5", - "@typescript-eslint/parser": "^6.7.5", - "@unocss/transformer-variant-group": "^0.56.5", + "@types/fs-extra": "^11.0.4", + "@types/inquirer": "^9.0.7", + "@types/lodash-es": "^4.17.11", + "@types/node": "^20.9.0", + "@types/nprogress": "^0.2.3", + "@types/qrcode": "^1.5.5", + "@types/qs": "^6.9.10", + "@types/sortablejs": "^1.15.5", + "@typescript-eslint/eslint-plugin": "^6.10.0", + "@typescript-eslint/parser": "^6.10.0", + "@unocss/transformer-variant-group": "^0.57.2", "@vitejs/plugin-legacy": "^4.1.1", "@vitejs/plugin-vue": "^4.4.0", "@vitejs/plugin-vue-jsx": "^3.0.2", "autoprefixer": "^10.4.16", "chalk": "^5.3.0", "consola": "^3.2.3", - "eslint": "^8.51.0", + "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-define-config": "^1.24.1", "eslint-plugin-prettier": "^5.0.1", - "eslint-plugin-vue": "^9.17.0", + "eslint-plugin-vue": "^9.18.1", "esno": "^0.17.0", "fs-extra": "^11.1.1", "husky": "^8.0.3", - "inquirer": "^9.2.11", + "inquirer": "^9.2.12", "less": "^4.2.0", - "lint-staged": "^14.0.1", + "lint-staged": "^15.0.2", "plop": "^4.0.0", "postcss": "^8.4.31", "postcss-html": "^1.5.0", "postcss-less": "^6.0.0", "prettier": "^3.0.3", "rimraf": "^5.0.5", - "rollup": "^4.0.2", - "stylelint": "^15.10.3", + "rollup": "^4.3.0", + "stylelint": "^15.11.0", "stylelint-config-html": "^1.1.0", "stylelint-config-recommended": "^13.0.0", "stylelint-config-standard": "^34.0.0", "stylelint-order": "^6.0.3", - "terser": "^5.21.0", + "terser": "^5.24.0", "typescript": "5.2.2", - "unocss": "^0.56.5", - "vite": "4.4.11", + "unocss": "^0.57.2", + "vite": "4.5.0", "vite-plugin-ejs": "^1.6.4", "vite-plugin-eslint": "^1.8.1", "vite-plugin-mock": "2.9.6", @@ -112,7 +112,7 @@ "vite-plugin-purge-icons": "^0.9.2", "vite-plugin-style-import": "2.0.0", "vite-plugin-svg-icons": "^2.0.1", - "vue-tsc": "^1.8.19" + "vue-tsc": "^1.8.22" }, "engines": { "node": ">= 16.13.0" diff --git a/src/components/Waterfall/src/Waterfall.vue b/src/components/Waterfall/src/Waterfall.vue index ae648c1..32a0aa7 100644 --- a/src/components/Waterfall/src/Waterfall.vue +++ b/src/components/Waterfall/src/Waterfall.vue @@ -20,10 +20,13 @@ const prop = defineProps({ src: 'src', height: 'height' }), + cols: propTypes.number.def(undefined), loadingText: propTypes.string.def('加载中...'), loading: propTypes.bool.def(false), end: propTypes.bool.def(false), - endText: propTypes.string.def('没有更多了') + endText: propTypes.string.def('没有更多了'), + autoCenter: propTypes.bool.def(true), + layout: propTypes.oneOf(['javascript', 'flex']).def('flex') }) const wrapEl = ref() @@ -37,7 +40,7 @@ const wrapWidth = ref(0) const loadMore = ref() // 首先确定列数 = 页面宽度 / 图片宽度 -const cols = ref(0) +const innerCols = ref(0) const filterData = ref([]) @@ -49,11 +52,11 @@ const filterWaterfall = async () => { const container = unref(wrapEl) as HTMLElement if (!container) return - cols.value = Math.floor(container.clientWidth / (width + gap)) + innerCols.value = prop.cols ?? Math.floor(container.clientWidth / (width + gap)) const length = data.length for (let i = 0; i < length; i++) { - if (i < unref(cols)) { + if (i < unref(innerCols)) { heights.value[i] = data[i][props.height as string] filterData.value.push({ ...data[i], @@ -66,7 +69,7 @@ const filterWaterfall = async () => { let minHeight = heights.value[0] let index = 0 // 找出最小高度 - for (let j = 1; j < unref(cols); j++) { + for (let j = 1; j < unref(innerCols); j++) { if (unref(heights)[j] < minHeight) { minHeight = unref(heights)[j] index = j @@ -83,14 +86,42 @@ const filterWaterfall = async () => { } } wrapHeight.value = Math.max(...unref(heights)) - wrapWidth.value = unref(cols) * (width + gap) - gap + wrapWidth.value = unref(innerCols) * (width + gap) - gap +} + +const flexWaterfall = async () => { + const { width, gap } = prop + const data = prop.data as any[] + await nextTick() + + const container = unref(wrapEl) as HTMLElement + if (!container) return + innerCols.value = prop.cols ?? Math.floor(container.clientWidth / (width + gap)) + + const length = data.length + // 根据列数,创建数组 + const arr = new Array(unref(innerCols)).fill([]) + // 循环data,依次插入到arr中 + for (let i = 0; i < length; i++) { + const index = i % unref(innerCols) + arr[index] = [...arr[index], data[i]] + } + filterData.value = arr +} + +const initLayout = () => { + const { layout } = prop + if (layout === 'javascript') { + filterWaterfall() + } else if (layout === 'flex') { + flexWaterfall() + } } watch( - () => prop.data, - async () => { - await nextTick() - filterWaterfall() + () => [prop.data, prop.cols], + () => { + initLayout() }, { immediate: true @@ -99,7 +130,7 @@ watch( onMounted(() => { if (unref(prop.reset)) { - useEventListener(window, 'resize', debounce(filterWaterfall, 300)) + useEventListener(window, 'resize', debounce(initLayout, 300)) } useIntersectionObserver( unref(loadMore), @@ -117,41 +148,87 @@ onMounted(() => { diff --git a/src/config/axios/config.ts b/src/config/axios/config.ts index fd75447..d0c1f0d 100644 --- a/src/config/axios/config.ts +++ b/src/config/axios/config.ts @@ -8,23 +8,6 @@ import { ElMessage } from 'element-plus' import qs from 'qs' const config: AxiosConfig = { - /** - * api请求基础路径 - */ - baseUrl: { - // 开发环境接口前缀 - base: '', - - // 打包开发环境接口前缀 - dev: '', - - // 打包生产环境接口前缀 - pro: '', - - // 打包测试环境接口前缀 - test: '' - }, - /** * 接口成功返回状态码 */ diff --git a/src/config/axios/service.ts b/src/config/axios/service.ts index 01f31e3..b68fa70 100644 --- a/src/config/axios/service.ts +++ b/src/config/axios/service.ts @@ -4,8 +4,8 @@ import config, { defaultRequestInterceptors, defaultResponseInterceptors } from import { AxiosInstance, InternalAxiosRequestConfig, RequestConfig, AxiosResponse } from './types' import { ElMessage } from 'element-plus' -const { interceptors, baseUrl } = config -export const PATH_URL = baseUrl[import.meta.env.VITE_API_BASE_PATH] +const { interceptors } = config +export const PATH_URL = import.meta.env.VITE_API_BASE_PATH const { requestInterceptors, responseInterceptors } = interceptors diff --git a/src/config/axios/types/index.ts b/src/config/axios/types/index.ts index b900c17..471ce4e 100644 --- a/src/config/axios/types/index.ts +++ b/src/config/axios/types/index.ts @@ -16,12 +16,6 @@ interface RequestInterceptors { responseInterceptorsCatch?: (err: any) => any } interface AxiosConfig { - baseUrl: { - base: string - dev: string - pro: string - test: string - } code: number defaultHeaders: AxiosHeaders timeout: number diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 98068ec..cab0dd8 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -521,7 +521,7 @@ export default { menu: { menuName: '菜单名称', icon: '图标', - permission: '权限标识', + permission: '按钮权限', component: '组件', path: '路径', status: '状态', diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts index 3247708..8e7858c 100644 --- a/src/store/modules/app.ts +++ b/src/store/modules/app.ts @@ -63,7 +63,7 @@ export const useAppStore = defineStore('app', { layout: getStorage('layout') || 'classic', // layout布局 isDark: getStorage('isDark'), // 是否是暗黑模式 - currentSize: getStorage('default') || 'default', // 组件尺寸 + currentSize: getStorage('currentSize') || 'default', // 组件尺寸 theme: getStorage('theme') || { // 主题色 elColorPrimary: '#409eff', diff --git a/src/styles/index.less b/src/styles/index.less index cebb2c7..6825d70 100644 --- a/src/styles/index.less +++ b/src/styles/index.less @@ -4,4 +4,4 @@ // 解决抽屉弹出时,body宽度变化的问题 .el-popup-parent--hidden { width: 100% !important; -} \ No newline at end of file +} diff --git a/src/views/Authorization/Department/Department.vue b/src/views/Authorization/Department/Department.vue index 6d52381..4d15138 100644 --- a/src/views/Authorization/Department/Department.vue +++ b/src/views/Authorization/Department/Department.vue @@ -166,11 +166,7 @@ const crudSchemas = reactive([ hidden: true }, form: { - component: 'DatePicker', - componentProps: { - type: 'datetime', - valueFormat: 'YYYY-MM-DD HH:mm:ss' - } + hidden: true } }, { diff --git a/src/views/Authorization/Department/components/Write.vue b/src/views/Authorization/Department/components/Write.vue index 512e651..2c3c527 100644 --- a/src/views/Authorization/Department/components/Write.vue +++ b/src/views/Authorization/Department/components/Write.vue @@ -20,9 +20,7 @@ const props = defineProps({ const rules = reactive({ id: [required()], - status: [required()], - createTime: [required()], - remark: [required()] + status: [required()] }) const { formRegister, formMethods } = useForm() diff --git a/src/views/Authorization/Menu/Menu.vue b/src/views/Authorization/Menu/Menu.vue index ee5bd22..3f5a976 100644 --- a/src/views/Authorization/Menu/Menu.vue +++ b/src/views/Authorization/Menu/Menu.vue @@ -35,7 +35,13 @@ const tableColumns = reactive([ }, { field: 'meta.title', - label: t('menu.menuName') + label: t('menu.menuName'), + slots: { + default: (data: any) => { + const title = data.row.meta.title + return <>{title} + } + } }, { field: 'meta.icon', diff --git a/src/views/Authorization/Menu/components/AddButtonPermission.vue b/src/views/Authorization/Menu/components/AddButtonPermission.vue new file mode 100644 index 0000000..31ad3c9 --- /dev/null +++ b/src/views/Authorization/Menu/components/AddButtonPermission.vue @@ -0,0 +1,67 @@ + + + diff --git a/src/views/Authorization/Menu/components/Detail.vue b/src/views/Authorization/Menu/components/Detail.vue index 2b75627..54b2cad 100644 --- a/src/views/Authorization/Menu/components/Detail.vue +++ b/src/views/Authorization/Menu/components/Detail.vue @@ -75,6 +75,24 @@ const detailSchema = ref([ field: 'meta.activeMenu', label: '高亮菜单' }, + { + field: 'permissionList', + label: '按钮权限', + span: 24, + slots: { + default: (data: any) => ( + <> + {data?.permissionList?.map((v) => { + return ( + + {v.label} + + ) + })} + + ) + } + }, { field: 'menuState', label: '菜单状态', diff --git a/src/views/Authorization/Menu/components/Write.vue b/src/views/Authorization/Menu/components/Write.vue index fc00cb4..d23c0a7 100644 --- a/src/views/Authorization/Menu/components/Write.vue +++ b/src/views/Authorization/Menu/components/Write.vue @@ -1,11 +1,12 @@ diff --git a/src/views/Authorization/Role/Role.vue b/src/views/Authorization/Role/Role.vue index 42a7923..9c4e68b 100644 --- a/src/views/Authorization/Role/Role.vue +++ b/src/views/Authorization/Role/Role.vue @@ -9,6 +9,7 @@ import { Search } from '@/components/Search' import { FormSchema } from '@/components/Form' import { ContentWrap } from '@/components/ContentWrap' import Write from './components/Write.vue' +import Detail from './components/Detail.vue' import { Dialog } from '@/components/Dialog' const { t } = useI18n() @@ -36,10 +37,6 @@ const tableColumns = reactive([ field: 'roleName', label: t('role.roleName') }, - { - field: 'role', - label: t('role.role') - }, { field: 'status', label: t('menu.status'), @@ -75,6 +72,9 @@ const tableColumns = reactive([ action(row, 'edit')}> {t('exampleDemo.edit')} + action(row, 'detail')}> + {t('exampleDemo.detail')} + {t('exampleDemo.del')} ) @@ -155,6 +155,7 @@ const save = async () => { +