diff --git a/.gitignore b/.gitignore index a547bf36d8d11a4f89c59c144f24795749086dd1..4587401d84ec04ebadd7f015aa823c60e08f636c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ dist-ssr *.local # Editor directories and files -.vscode/* +# .vscode/* !.vscode/extensions.json .idea .DS_Store diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..d6c413a8cce6a453536eb843f080c839657ec2b4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..05d2234985740046624bcc3887d611fc4439d55d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,28 @@ +//团队格式化规范 airbnb-base + eslint + prettier + volar +{ + "editor.formatOnSave": true, + "eslint.validate": ["javascript", "html", "vue"], + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[vue]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "javascript.format.insertSpaceBeforeFunctionParenthesis": false, //true:普通函数名后面加空格 + "javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": false //true:匿名函数后面加空格 +} diff --git a/package.json b/package.json index 5f55ede297d15c263c535f74865fd279f9f5eda9..de335bfc3379caf6140c8f239288daec726d2a5d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "@element-plus/icons": "^0.0.11", "@element-plus/icons-vue": "^2.0.6", "axios": "^0.27.2", + "echarts": "5.3.2", "element-plus": "^2.1.11", + "js-md5": "^0.7.3", "pinia": "^2.0.13", "pinia-plugin-persist": "^1.0.0", "quick-vue3-ui": "1.0.5", @@ -27,6 +29,7 @@ "vue-router": "4" }, "devDependencies": { + "@types/js-md5": "^0.4.3", "@types/node": "^17.0.30", "@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/parser": "^5.22.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68587b74d2a554491feb0626f776a949c59b25a1..72ba0baeded511c70531b0921c67d94a4bbb7a66 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,11 +3,13 @@ lockfileVersion: 5.3 specifiers: '@element-plus/icons': ^0.0.11 '@element-plus/icons-vue': ^2.0.6 + '@types/js-md5': ^0.4.3 '@types/node': ^17.0.30 '@typescript-eslint/eslint-plugin': ^5.22.0 '@typescript-eslint/parser': ^5.22.0 '@vitejs/plugin-vue': ^2.3.1 axios: ^0.27.2 + echarts: 5.3.2 element-plus: ^2.1.11 eslint: ^8.14.0 eslint-config-airbnb-base: ^15.0.0 @@ -16,6 +18,7 @@ specifiers: eslint-plugin-prettier: ^4.0.0 eslint-plugin-vue: ^8.7.1 husky: ^7.0.4 + js-md5: ^0.7.3 pinia: ^2.0.13 pinia-plugin-persist: ^1.0.0 prettier: 2.6.2 @@ -35,7 +38,9 @@ dependencies: '@element-plus/icons': 0.0.11 '@element-plus/icons-vue': 2.0.6_vue@3.2.36 axios: registry.npmmirror.com/axios/0.27.2 + echarts: 5.3.2 element-plus: registry.npmmirror.com/element-plus/2.2.2_vue@3.2.36 + js-md5: 0.7.3 pinia: registry.npmmirror.com/pinia/2.0.14_typescript@4.7.2+vue@3.2.36 pinia-plugin-persist: registry.npmmirror.com/pinia-plugin-persist/1.0.0_pinia@2.0.14+vue@3.2.36 quick-vue3-ui: 1.0.5 @@ -44,6 +49,7 @@ dependencies: vue-router: registry.npmmirror.com/vue-router/4.0.15_vue@3.2.36 devDependencies: + '@types/js-md5': 0.4.3 '@types/node': registry.npmmirror.com/@types/node/17.0.35 '@typescript-eslint/eslint-plugin': registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.26.0_3e687f93547efbf7d61b629ca4d69a5c '@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.26.0_eslint@8.16.0+typescript@4.7.2 @@ -162,6 +168,10 @@ packages: resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} dev: false + /@types/js-md5/0.4.3: + resolution: {integrity: sha512-BIga/WEqTi35ccnGysOuO4RmwVnpajv9oDB/sDQSY2b7/Ac7RyYR30bv7otZwByMvOJV9Vqq6/O1DFAnOzE4Pg==} + dev: true + /@vue/compiler-core/3.2.36: resolution: {integrity: sha512-bbyZM5hvBicv0PW3KUfVi+x3ylHnfKG7DOn5wM+f2OztTzTjLEyBb/5yrarIYpmnGitVGbjZqDbODyW4iK8hqw==} dependencies: @@ -404,6 +414,13 @@ packages: engines: {node: '>=12'} dev: true + /echarts/5.3.2: + resolution: {integrity: sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==} + dependencies: + tslib: 2.3.0 + zrender: 5.3.1 + dev: false + /ejs/3.1.8: resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} engines: {node: '>=0.10.0'} @@ -716,6 +733,10 @@ packages: minimatch: 3.1.2 dev: true + /js-md5/0.7.3: + resolution: {integrity: sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==} + dev: false + /jsonfile/6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: @@ -903,6 +924,10 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true + /tslib/2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + dev: false + /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} dev: true @@ -981,6 +1006,12 @@ packages: resolution: {integrity: sha512-h9atBP/bsZohWpHnr+2sic8Iecb60GxftXsWNLLLSqewgIsGzByd2gcIID4nXcG+3tNe4GQG3dLcff3kXupdRA==} dev: true + /zrender/5.3.1: + resolution: {integrity: sha512-7olqIjy0gWfznKr6vgfnGBk7y4UtdMvdwFmK92vVQsQeDPyzkHW1OlrLEKg6GHz1W5ePf0FeN1q2vkl/HFqhXw==} + dependencies: + tslib: 2.3.0 + dev: false + registry.npmmirror.com/@algolia/autocomplete-core/1.6.3: resolution: {integrity: sha512-dqQqRt01fX3YuVFrkceHsoCnzX0bLhrrg8itJI1NM68KjrPYQPYsE+kY8EZTCM4y8VDnhqJErR73xe/ZsV+qAA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@algolia/autocomplete-core/-/autocomplete-core-1.6.3.tgz} name: '@algolia/autocomplete-core' diff --git a/src/api/dictionary.ts b/src/api/dictionary.ts new file mode 100644 index 0000000000000000000000000000000000000000..efdd2d3dbbc8279cc9be96bf9ebcc2514237a1c1 --- /dev/null +++ b/src/api/dictionary.ts @@ -0,0 +1,43 @@ +import { Dictionary } from '../types/dictionary' +import request, { QuickResponseData } from '../utils/request' + +const Api = { + list: '/api/dictionary/getList', + add: '/api/dictionary/add', + update: '/api/dictionary/update', + remove: '/api/dictionary/remove', +} +export const getDictionaryList = ( + dicTypeId: string +): Promise>> => { + return request>>({ + url: Api.list, + method: 'GET', + params: { + dicTypeId, + }, + }) +} +export const addDictionary = (data: Dictionary) => { + return request({ + url: Api.add, + method: 'POST', + data, + }) +} +export const updateDictionary = (data: Dictionary) => { + return request({ + url: Api.update, + method: 'POST', + data, + }) +} +export const removeDictionary = (id: string) => { + return request({ + url: Api.remove, + method: 'POST', + params: { + id, + }, + }) +} diff --git a/src/api/dictionaryType.ts b/src/api/dictionaryType.ts new file mode 100644 index 0000000000000000000000000000000000000000..388e1baaa69a242f9ba433423f979237b52b0c30 --- /dev/null +++ b/src/api/dictionaryType.ts @@ -0,0 +1,41 @@ +import { DictionaryType } from '../types/dictionaryType' +import request, { QuickResponseData } from '../utils/request' + +const Api = { + list: '/api/dictionaryType/getList', + add: '/api/dictionaryType/add', + update: '/api/dictionaryType/update', + remove: '/api/dictionaryType/remove', +} +export const getDictionaryTypeList = (): Promise< + QuickResponseData> +> => { + return request>>({ + url: Api.list, + method: 'GET', + }) +} + +export const addDictionaryType = (data: DictionaryType) => { + return request({ + url: Api.add, + method: 'POST', + data, + }) +} +export const updateDictionaryType = (data: DictionaryType) => { + return request({ + url: Api.update, + method: 'POST', + data, + }) +} +export const removeDictionaryType = (id: string) => { + return request({ + url: Api.remove, + method: 'POST', + params: { + id, + }, + }) +} diff --git a/src/api/login.ts b/src/api/login.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc7803376ddacaf38d421f637a8ee0497082590d --- /dev/null +++ b/src/api/login.ts @@ -0,0 +1,39 @@ +import { Login, User } from '../types/user' +import request, { QuickResponseData } from '../utils/request' + +const Api = { + login: '/api/login', + loginOut: '/api/loginOut', + getUserInfo: '/api/getUserInfo', + changePassword: '/api/changePassword', +} +export const userLogin = (data: Login): Promise> => { + return request>({ + url: Api.login, + method: 'POST', + data, + }) +} +export const userLoginOut = (data: Login) => { + return request({ + url: Api.loginOut, + method: 'POST', + data, + }) +} +export const getUserInfo = (userName: string) => { + return request({ + url: Api.getUserInfo, + method: 'GET', + params: { + userName, + }, + }) +} +export const changePassword = (data: Login) => { + return request({ + url: Api.changePassword, + method: 'POST', + data, + }) +} diff --git a/src/api/user.ts b/src/api/user.ts index 545c22952a98243a98eac108d5eea10e7dcb3e66..842193f6ba83be8faa8bf397fb15126b6c02d98a 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -1,17 +1,19 @@ import { User } from '../types/user' -import request from '../utils/request' +import request, { QuickResponseData } from '../utils/request' const Api = { pageList: '/api/user/getPageList', list: '/api/user/getList', + info: '/api/user/getInfo', add: '/api/user/add', update: '/api/user/update', remove: '/api/user/remove', } -export const getUserPageList = (params: object) => { - return request({ +export const getUserPageList = ( + params: object +): Promise>> => { + return request>>({ url: Api.pageList, - // url:'https://console-mock.apipost.cn/app/mock/project/1cee3669-4ecb-431e-a7b7-67c5298e06ab/api/user/getUserList', method: 'GET', params, }) @@ -19,10 +21,20 @@ export const getUserPageList = (params: object) => { export const getUserList = () => { return request({ url: Api.list, - // url:'https://console-mock.apipost.cn/app/mock/project/1cee3669-4ecb-431e-a7b7-67c5298e06ab/api/user/getUserList', method: 'GET', }) } +export const getUserInfo = ( + userName: string +): Promise> => { + return request>({ + url: Api.info, + method: 'GET', + params: { + userName, + }, + }) +} export const addUser = (data: User) => { return request({ url: Api.add, @@ -42,7 +54,7 @@ export const deleteUser = (userId: string) => { url: Api.remove, method: 'POST', params: { - user_id: userId, + userId, }, }) } diff --git a/src/assets/audios/order.mp3 b/src/assets/audios/order.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f67c4e9569c9471f4412fd246e1d42c35f3b42f6 Binary files /dev/null and b/src/assets/audios/order.mp3 differ diff --git a/src/components/QuickCrud/index.vue b/src/components/QuickCrud/index.vue index 92f16b309a55cd868d233ae77766c05a2d3534a0..99296b9ebe5378a2a3c0e1d33a06706fe26bd163 100644 --- a/src/components/QuickCrud/index.vue +++ b/src/components/QuickCrud/index.vue @@ -7,14 +7,16 @@ import { toRefs, Ref, computed, + onActivated, + nextTick, } from 'vue' -import { ElMessage } from 'element-plus' +import { ElMessage, ElTree, FormInstance } from 'element-plus' import { User } from '../../types/user' import { Column } from '../../types/table' -import { Page } from '../../types/page' -import { FormItem, FormTitle } from '../../types/form' +import { FormItem } from '../../types/form' import QuickTable from '../QuickTable/index.vue' import QuickForm from '../QuickForm/index.vue' +import { Tree } from '../../types/tree' const props = defineProps({ tableData: { @@ -65,6 +67,46 @@ const props = defineProps({ return {} }, }, + showSearch: { + type: Boolean, + default: false, + }, + showPage: { + type: Boolean, + default: false, + }, + showTree: { + type: Boolean, + default: false, + }, + treeData: { + type: Array, + default: () => { + return [] + }, + }, + defaultTreeProps: { + type: Object, + default: () => { + return { + id: 'id', + label: 'label', + children: 'children', + } + }, + }, + treeSpan: { + type: Number, + default: 4, + }, + autoFefresh: { + type: Boolean, + default: false, + }, + interval: { + type: Number, + default: 1000 * 30, + }, }) const { @@ -76,6 +118,14 @@ const { formItems, formTitle, page, + showSearch, + showPage, + showTree, + treeData, + defaultTreeProps, + treeSpan, + // autoReFefresh, + // autoRefreshTime, } = toRefs(props) as { tableData: Ref tableColumns: Ref @@ -83,30 +133,65 @@ const { searchFormItems: Ref formModel: Ref formItems: Ref - formTitle: Ref - page: Ref + formTitle: Ref + page: Ref + showSearch: Ref + showPage: Ref + showTree: Ref + treeData: Ref + defaultTreeProps: Ref + treeSpan: Ref + // autoReFefresh: Ref + // autoRefreshTime: Ref } const emit = defineEmits([ + 'onTreeClick', 'onLoad', + 'onTreeLoad', 'onAdd', 'onEdit', 'onDelete', 'onSearchFormSubmit', - 'onSearchFormReset', + 'onSearchFormClear', 'onFormSubmit', 'onFormCancel', 'onSizeChange', 'onCurrentChange', ]) +const quickFormRef = ref>() +const selectTree = ref({}) +const treeRef = ref>() const selectDataList = ref>([]) const dialogFormVisible = ref(false) const dialogFormType = ref('') +const autoReFefresh = ref(true) +const timeCount = ref() +const autoRefreshTime = ref(1000 * 60) +/** + * 函数 + */ const load = () => { const { current, size } = page.value const params = { ...searchFormModel.value, current, size } emit('onLoad', params) } +const handleTreeNodeClick = (data: Tree) => { + selectTree.value = data + emit('onTreeClick', data, () => { + load() + }) +} +const treeLoad = () => { + emit('onTreeLoad', (id: string) => { + nextTick(() => { + treeRef.value?.setCurrentKey(id) + const node = treeRef.value?.getCurrentNode() as Tree + handleTreeNodeClick(node) + }) + load() + }) +} const handleSelectionChange = (val: User[]) => { selectDataList.value = val } @@ -114,15 +199,30 @@ const handleSearch = () => { emit('onSearchFormSubmit', searchFormModel.value) load() } -const handleReset = () => { - emit('onSearchFormReset', searchFormModel.value) +const handleClear = () => { + emit('onSearchFormClear', searchFormModel.value) } const handleAdd = () => { dialogFormType.value = 'add' + if (showTree.value && !selectTree.value.id) { + ElMessage({ + type: 'warning', + message: '请选择节点', + }) + return + } dialogFormVisible.value = true + emit('onAdd') } const handleEdit = () => { dialogFormType.value = 'edit' + if (showTree.value && !selectTree.value.id) { + ElMessage({ + type: 'warning', + message: '请选择节点', + }) + return + } if (selectDataList.value.length !== 1) { ElMessage({ type: 'warning', @@ -152,7 +252,12 @@ const handleCancel = () => { emit('onFormCancel', formModel.value) } const handleOk = () => { - emit('onFormSubmit', formModel.value, () => { + if (quickFormRef.value) { + quickFormRef.value.handleSubmit() + } +} +const handleSubmit = (formRef: FormInstance | undefined) => { + emit('onFormSubmit', formRef, formModel.value, () => { dialogFormVisible.value = false load() }) @@ -180,68 +285,104 @@ const dialogTitle = computed(() => { return '标题' }) onMounted(() => { - load() + // TODO:onMounted会执行两次,待解决 + console.log('onMounted-crud') + if (showTree.value) { + treeLoad() + } else { + load() + } + if (autoReFefresh.value) { + timeCount.value = setInterval(() => { + load() + }, autoRefreshTime.value) + } +}) +onActivated(() => { + clearInterval(timeCount.value) }) diff --git a/src/components/QuickTable/index.vue b/src/components/QuickTable/index.vue index f9fa869e502f32ad2781ed341b393fc72fd9042f..4b2ba7dd2c6eb07b7e5dd81ef3bc38767c656d54 100644 --- a/src/components/QuickTable/index.vue +++ b/src/components/QuickTable/index.vue @@ -33,7 +33,7 @@ const handleSelectionChange = (val: any) => { export default component } +// declare module 'echarts' { +// const echarts: any +// export default echarts +// } diff --git a/src/layout/components/AiniSidebar/index.vue b/src/layout/components/AiniSidebar/index.vue index 1fd1f8efae0a0190d63c61ad8fb25237a456aa11..8c8f006b46073b0327b15a09252370c7dad27a9b 100644 --- a/src/layout/components/AiniSidebar/index.vue +++ b/src/layout/components/AiniSidebar/index.vue @@ -31,31 +31,20 @@ const handleClose = (key: string, keyPath: string[]) => { default-active="2" :collapse="isCollapse" text-color="#fff" + :router="true" @open="handleOpen" @close="handleClose" > - + - - - 用户列表 - + 用户管理 + + 字典分类 + 字典管理 - - - 角色管理 - - - - 权限管理 - - - - 菜单管理 - diff --git a/src/layout/components/AiniTop/index.vue b/src/layout/components/AiniTop/index.vue index ce1d45084c7c16b8301b69d560be9b442915aaf3..090219927864cf4ec218c5eb68b01ee8eade02c8 100644 --- a/src/layout/components/AiniTop/index.vue +++ b/src/layout/components/AiniTop/index.vue @@ -1,4 +1,5 @@