├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── docker-image.yml ├── .gitignore ├── .postcssrc.js ├── .stylintrc ├── .vscode ├── extensions.json └── settings.json ├── Dockerfile ├── LICENSE ├── README.md ├── README_CN.md ├── babel.config.js ├── build.sh ├── docker ├── README.md └── default.conf ├── img ├── crudapiweixin.jpeg ├── customer.png ├── relation.png ├── table.png └── weixinqun.png ├── jsconfig.json ├── package.json ├── public ├── crudapi-mini.png ├── crudapi.png ├── crudapiweixin.jpeg ├── favicon.ico ├── icons │ ├── favicon-128x128.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon-96x96.png ├── price.png └── weixinqun.png ├── quasar.conf.js ├── run.sh └── src ├── App.vue ├── api ├── file.js ├── index.js ├── metadataRelation.js ├── metadataSequence.js ├── metadataTable.js ├── table.js └── user.js ├── assets └── quasar-logo-full.svg ├── boot ├── .gitkeep ├── axios.js ├── cfile.js ├── cindexlist.js ├── cpage.js ├── crawDisplayer.js ├── csqleditor.js ├── ctableedit.js ├── ctablelist.js ├── ctablelistedit.js ├── ctablelistread.js ├── ctablenew.js ├── cvaluedialog.js └── i18n.js ├── components ├── CDownload │ ├── CDownloadDialog.vue │ └── index.js ├── CFile │ ├── CFile.vue │ └── index.js ├── CIndexList │ ├── CIndexList.vue │ └── index.js ├── CPage │ ├── CPage.vue │ └── index.js ├── CRawDisplayer │ ├── CRawDisplayer.vue │ └── index.js ├── CSqlEditor │ ├── CSqlEditor.vue │ └── index.js ├── CTableEdit │ ├── CTableEdit.vue │ └── index.js ├── CTableList │ ├── CTableList.vue │ └── index.js ├── CTableListEdit │ ├── CTableListEdit.vue │ └── index.js ├── CTableListRead │ ├── CTableListRead.vue │ ├── CTableListReadDialog.vue │ └── index.js ├── CTableNew │ ├── CTableNew.vue │ └── index.js ├── CValueDialog │ ├── CValueDialog.vue │ └── index.js └── EssentialLink.vue ├── css ├── app.styl └── quasar.variables.styl ├── i18n ├── en-us │ └── index.js └── index.js ├── index.template.html ├── layouts └── MainLayout.vue ├── models └── file-md5.js ├── pages ├── About.vue ├── Error403.vue ├── Error404.vue ├── Index.vue ├── Login.vue ├── Setting.vue ├── business │ └── table │ │ ├── edit.vue │ │ ├── import.vue │ │ ├── list.vue │ │ ├── multiImport.vue │ │ └── new.vue ├── form-builder │ └── index.vue ├── metadata │ ├── relation │ │ ├── edit.vue │ │ ├── graph.vue │ │ ├── list.vue │ │ └── new.vue │ ├── sequence │ │ ├── edit.vue │ │ ├── list.vue │ │ └── new.vue │ ├── sql │ │ └── new.vue │ └── table │ │ ├── edit.vue │ │ ├── import.vue │ │ ├── indexList.vue │ │ ├── list.vue │ │ ├── new.vue │ │ └── reverse.vue └── tablePermission │ └── index.vue ├── plugins └── js-spark-md5.js ├── router ├── index.js └── routes.js ├── service ├── authService.js ├── fileService.js ├── index.js ├── metadataRelationService.js ├── metadataSequenceService.js ├── metadataTableService.js ├── permissionService.js ├── settingService.js ├── tableService.js └── userService.js ├── store ├── config │ ├── actions.js │ ├── getters.js │ ├── index.js │ ├── mutations.js │ └── state.js ├── index.js ├── module-example │ ├── actions.js │ ├── getters.js │ ├── index.js │ ├── mutations.js │ └── state.js ├── store-flag.d.ts └── user │ ├── actions.js │ ├── getters.js │ ├── index.js │ ├── mutations.js │ ├── plugin.js │ └── state.js └── utils ├── date.js └── index.js /.dockerignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .git 3 | .gitignore 4 | node_modules 5 | dist 6 | .quasar 7 | .vscode 8 | .dockerignore 9 | package-lock.json 10 | Dockerfile 11 | *.md 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /src-bex/www 3 | /src-capacitor 4 | /src-cordova 5 | /.quasar 6 | /node_modules 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy 3 | // This option interrupts the configuration hierarchy at this file 4 | // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos) 5 | root: true, 6 | 7 | parserOptions: { 8 | parser: 'babel-eslint', 9 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 10 | sourceType: 'module' // Allows for the use of imports 11 | }, 12 | 13 | env: { 14 | browser: true 15 | }, 16 | 17 | // Rules order is important, please avoid shuffling them 18 | extends: [ 19 | // Base ESLint recommended rules 20 | // 'eslint:recommended', 21 | 22 | 23 | // Uncomment any of the lines below to choose desired strictness, 24 | // but leave only one uncommented! 25 | // See https://eslint.vuejs.org/rules/#available-rules 26 | 'plugin:vue/essential', // Priority A: Essential (Error Prevention) 27 | // 'plugin:vue/strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) 28 | // 'plugin:vue/recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead) 29 | 30 | // https://github.com/prettier/eslint-config-prettier#installation 31 | // usage with Prettier, provided by 'eslint-config-prettier'. 32 | 'prettier', 33 | 34 | 'prettier/vue' 35 | ], 36 | 37 | plugins: [ 38 | // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-file 39 | // required to lint *.vue files 40 | 'vue', 41 | 42 | // https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674 43 | // Prettier has not been included as plugin to avoid performance impact 44 | // add it as an extension for your IDE 45 | ], 46 | 47 | globals: { 48 | ga: true, // Google Analytics 49 | cordova: true, 50 | __statics: true, 51 | process: true, 52 | Capacitor: true, 53 | chrome: true 54 | }, 55 | 56 | // add your custom rules here 57 | rules: { 58 | 'prefer-promise-reject-errors': 'off', 59 | 60 | 61 | // allow debugger during development only 62 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: self-hosted 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Build the Docker image 18 | run: docker build . --file Dockerfile --tag crudapi/crudapi-admin-web:$(date +%s) 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .thumbs.db 3 | node_modules 4 | 5 | # Quasar core related directories 6 | .quasar 7 | /dist 8 | 9 | # Cordova related directories and files 10 | /src-cordova/node_modules 11 | /src-cordova/platforms 12 | /src-cordova/plugins 13 | /src-cordova/www 14 | 15 | # Capacitor related directories and files 16 | /src-capacitor/www 17 | /src-capacitor/node_modules 18 | 19 | # BEX related directories and files 20 | /src-bex/www 21 | /src-bex/js/core 22 | 23 | # Log files 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # Editor directories and files 29 | .idea 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | 35 | 36 | package-lock.json 37 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: [ 5 | // to edit target browsers: use "browserslist" field in package.json 6 | require('autoprefixer') 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.stylintrc: -------------------------------------------------------------------------------- 1 | { 2 | "blocks": "never", 3 | "brackets": "never", 4 | "colons": "never", 5 | "colors": "always", 6 | "commaSpace": "always", 7 | "commentSpace": "always", 8 | "cssLiteral": "never", 9 | "depthLimit": false, 10 | "duplicates": true, 11 | "efficient": "always", 12 | "extendPref": false, 13 | "globalDupe": true, 14 | "indentPref": 2, 15 | "leadingZero": "never", 16 | "maxErrors": false, 17 | "maxWarnings": false, 18 | "mixed": false, 19 | "namingConvention": false, 20 | "namingConventionStrict": false, 21 | "none": "never", 22 | "noImportant": false, 23 | "parenSpace": "never", 24 | "placeholder": false, 25 | "prefixVarsWithDollar": "always", 26 | "quotePref": "single", 27 | "semicolons": "never", 28 | "sortOrder": false, 29 | "stackedProperties": "never", 30 | "trailingWhitespace": "never", 31 | "universal": "never", 32 | "valid": true, 33 | "zeroUnits": "never", 34 | "zIndexNormalize": false 35 | } 36 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "octref.vetur" 6 | ], 7 | "unwantedRecommendations": [ 8 | "hookyqr.beautify", 9 | "dbaeumer.jshint", 10 | "ms-vscode.vscode-typescript-tslint-plugin" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "vetur.validation.template": false, 3 | "vetur.format.enable": false, 4 | "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"], 5 | 6 | "vetur.experimental.templateInterpolationService": true 7 | } 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #FROM node:14-alpine as builder 2 | FROM registry.cn-qingdao.aliyuncs.com/dh-mirror/node:14-alpine as builder 3 | 4 | RUN apk add jq 5 | 6 | COPY package.json /crudapi-admin-web/package.json 7 | 8 | WORKDIR /crudapi-admin-web 9 | RUN npm install 10 | 11 | COPY . /crudapi-admin-web/ 12 | 13 | WORKDIR /crudapi-admin-web 14 | 15 | RUN npm run build && \ 16 | version=`cat package.json | jq .version | sed 's/\"//g'` && \ 17 | echo $version && \ 18 | mkdir -p /crudapi/dist/crudapi-admin-web/$version && \ 19 | cd ./dist/spa && \ 20 | tar -zcvf crudapi-admin-web-$version.tar.gz crudapi && \ 21 | cp ./crudapi-admin-web-$version.tar.gz /crudapi/dist/crudapi-admin-web/$version && \ 22 | rm -rf crudapi-admin-web-$version.tar.gz 23 | 24 | #FROM nginx:latest 25 | FROM registry.cn-qingdao.aliyuncs.com/dh-mirror/nginx:latest 26 | 27 | WORKDIR /crudapi-admin-web 28 | 29 | COPY --from=builder /crudapi-admin-web/dist/spa . 30 | COPY --from=builder /crudapi/dist /crudapi/dist/ 31 | 32 | COPY ./docker/default.conf /etc/nginx/conf.d/default.conf 33 | 34 | EXPOSE 80 35 | 36 | COPY run.sh run.sh 37 | 38 | CMD ["/crudapi-admin-web/run.sh"] 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-present crudapi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crudapi Admin Web (crudapi-admin-web) 2 | 3 | ## Language 4 | [中文](README_CN.md) 5 | 6 | ## GIT URL 7 | Name | Type | License | GitHub| Gitee 8 | --- | --- | --- | --- | --- 9 | crudapi-admin-web | Vue Qusar Code | Open Source | [crudapi-admin-web](https://github.com/crudapi/crudapi-admin-web) | [crudapi-admin-web](https://gitee.com/crudapi/crudapi-admin-web) 10 | crudapi-example| Java SDK | Free Forever | [crudapi-example](https://github.com/crudapi/crudapi-example) | [crudapi-example](https://gitee.com/crudapi/crudapi-example) 11 | 12 | ## Install the dependencies 13 | ```bash 14 | npm install 15 | ``` 16 | 17 | ## Start the app in development mode (hot-code reloading, error reporting, etc.) 18 | ```bash 19 | npm run dev 20 | ``` 21 | 22 | ## Lint the files 23 | ```bash 24 | npm run lint 25 | ``` 26 | 27 | ## Build the app for production 28 | ```bash 29 | npm run build 30 | ``` 31 | 32 | ## Config 33 | update file quasar.conf.js, set devServer->proxy->target 34 | quasar.conf.js 35 | ```javascript 36 | devServer: { 37 | https: false, 38 | port: 8080, 39 | open: true, 40 | proxy: { 41 | "/api/*": { 42 | target: "http://127.0.0.1:8888", 43 | changeOrigin: true 44 | } 45 | } 46 | } 47 | ``` 48 | 49 | ## Docker 50 | ```bash 51 | docker build -t crudapi-admin-web:latest . 52 | docker rm -f crudapi-admin-web 53 | docker run -d -p 80:80 --name crudapi-admin-web crudapi-admin-web:latest 54 | ``` 55 | Visit [ http://127.0.0.1/crudapi ](http://127.0.0.1/crudapi) 56 | 57 | ## Documentation 58 | 59 | [https://help.crudapi.cn](https://help.crudapi.cn) 60 | 61 | 1. [ 基于Vue和Quasar的前端SPA项目实战之环境搭建(一)](https://help.crudapi.cn/crudapi-admin-web/helloworld.html) 62 | 2. [ 基于Vue和Quasar的前端SPA项目实战之用户登录(二)](https://help.crudapi.cn/crudapi-admin-web/login.html) 63 | 3. [ 基于Vue和Quasar的前端SPA项目实战之布局菜单(三)](https://help.crudapi.cn/crudapi-admin-web/layout.html) 64 | 4. [ 基于Vue和Quasar的前端SPA项目实战之序列号(四)](https://help.crudapi.cn/crudapi-admin-web/sequence.html) 65 | 5. [ 基于Vue和Quasar的前端SPA项目实战之动态表单(五)](https://help.crudapi.cn/crudapi-admin-web/metadatatable.html) 66 | 6. [ 基于Vue和Quasar的前端SPA项目实战之表关系(六)](https://help.crudapi.cn/crudapi-admin-web/metadatarelation.html) 67 | 7. [ 基于Vue和Quasar的前端SPA项目实战之业务数据(七)](https://help.crudapi.cn/crudapi-admin-web/business.html) 68 | 8. [ 基于Vue和Quasar的前端SPA项目实战之docker部署(八)](https://help.crudapi.cn/crudapi-admin-web/docker.html) 69 | 9. [ 基于Vue和Quasar的前端SPA项目实战之数据导入(九)](https://help.crudapi.cn/crudapi-admin-web/import.html) 70 | 10. [ 基于Vue和Quasar的前端SPA项目实战之文件上传(九)](https://help.crudapi.cn/crudapi-admin-web/fileupload.html) 71 | 11. [ 基于Vue和Quasar的前端SPA项目实战之联合索引(十一)](https://help.crudapi.cn/crudapi-admin-web/unionindex.html) 72 | 12. [ 基于Vue和Quasar的前端SPA项目实战之数据库逆向(十二)](https://help.crudapi.cn/crudapi-admin-web/dbfirst.html) 73 | 13. [ 基于Vue和Quasar的前端SPA项目实战之数据导出(十三)](https://help.crudapi.cn/crudapi-admin-web/export.html) 74 | 14. [ 基于Vue和Quasar的前端SPA项目实战之模块管理(十四)](https://help.crudapi.cn/crudapi-admin-web/module.html) 75 | 15. [ 基于Vue和Quasar的前端SPA项目实战之元数据导出导入(十五)](https://help.crudapi.cn/crudapi-admin-web/metadataexportimport.html) 76 | 16. [ 基于Vue和Quasar的前端SPA项目实战之拖拽表单定制(十六)](https://help.crudapi.cn/crudapi-admin-web/formbuilder.html) 77 | 78 | Ongoing updates... 79 | 80 | ## Demo 81 | Demo url:[https://demo.crudapi.cn/crudapi/](https://demo.crudapi.cn/crudapi/) 82 | 83 | ![table](./img/table.png) 84 | Metadata table 85 | 86 | ![table](./img/relation.png) 87 | Table relation 88 | 89 | ![customer](./img/customer.png) 90 | Bussiness Data 91 | 92 | ## Java SDK development 93 | ### GitHub repo 94 | [https://github.com/crudapi/crudapi-example](https://github.com/crudapi/crudapi-example) 95 | 96 | ### Gitee repo 97 | [https://gitee.com/crudapi/crudapi-example](https://gitee.com/crudapi/crudapi-example) 98 | 99 | ## Contact 100 | #### Email 101 | admin@crudapi.cn 102 | 103 | #### QQ 104 | 1440737304 105 | 106 | #### QQQun 107 | 632034576 108 | 109 | #### Weixin 110 | undefinedneqnull 111 | 112 |
113 | 114 |
115 | 116 | #### WeixinQun 117 |
118 | 119 |
120 | 121 | If you have any questions, please contact us! 122 | 123 | ## License 124 | 125 | Copyright (c) 2021-present crudapi 126 | 127 | [MIT License](http://en.wikipedia.org/wiki/MIT_License) 128 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # crudapi后台管理WEB (crudapi-admin-web) 2 | 3 | ## 语言 4 | [English](README.md) 5 | 6 | ## GIT地址 7 | 名称 | 类型 | 授权 | GitHub仓库 | Gitee仓库 8 | --- | --- | --- | --- | --- 9 | crudapi-admin-web | Vue Qusar源码 | 开源 | [crudapi-admin-web](https://github.com/crudapi/crudapi-admin-web) | [crudapi-admin-web](https://gitee.com/crudapi/crudapi-admin-web) 10 | crudapi-example| Java集成SDK | 永久免费 | [crudapi-example](https://github.com/crudapi/crudapi-example) | [crudapi-example](https://gitee.com/crudapi/crudapi-example) 11 | 12 | ## 安装npm依赖包 13 | ```bash 14 | npm install 15 | ``` 16 | 17 | ## 调试模式运行(代码热加载,错误提示等) 18 | ```bash 19 | npm run dev 20 | ``` 21 | 22 | ## 代码静态检查 23 | ```bash 24 | npm run lint 25 | ``` 26 | 27 | ## 编译发布 28 | ```bash 29 | npm run build 30 | ``` 31 | 32 | ## 修改配置 33 | 修改quasar.conf.js文件中devServer->proxy->target 34 | 35 | ```javascript 36 | devServer: { 37 | https: false, 38 | port: 8080, 39 | open: true, 40 | proxy: { 41 | "/api/*": { 42 | target: "http://127.0.0.1:8888", 43 | changeOrigin: true 44 | } 45 | } 46 | } 47 | ``` 48 | 49 | ## Docker部署 50 | ```bash 51 | docker build -t crudapi-admin-web:latest . 52 | docker rm -f crudapi-admin-web 53 | docker run -d -p 80:80 --name crudapi-admin-web crudapi-admin-web:latest 54 | ``` 55 | 访问[ http://127.0.0.1/crudapi ](http://127.0.0.1/crudapi) 56 | 57 | 58 | ## 文档 59 | 60 | [https://help.crudapi.cn](https://help.crudapi.cn) 61 | 62 | 1. [ 基于Vue和Quasar的前端SPA项目实战之环境搭建(一)](https://help.crudapi.cn/crudapi-admin-web/helloworld.html) 63 | 2. [ 基于Vue和Quasar的前端SPA项目实战之用户登录(二)](https://help.crudapi.cn/crudapi-admin-web/login.html) 64 | 3. [ 基于Vue和Quasar的前端SPA项目实战之布局菜单(三)](https://help.crudapi.cn/crudapi-admin-web/layout.html) 65 | 4. [ 基于Vue和Quasar的前端SPA项目实战之序列号(四)](https://help.crudapi.cn/crudapi-admin-web/sequence.html) 66 | 5. [ 基于Vue和Quasar的前端SPA项目实战之动态表单(五)](https://help.crudapi.cn/crudapi-admin-web/metadatatable.html) 67 | 6. [ 基于Vue和Quasar的前端SPA项目实战之表关系(六)](https://help.crudapi.cn/crudapi-admin-web/metadatarelation.html) 68 | 7. [ 基于Vue和Quasar的前端SPA项目实战之业务数据(七)](https://help.crudapi.cn/crudapi-admin-web/business.html) 69 | 8. [ 基于Vue和Quasar的前端SPA项目实战之docker部署(八)](https://help.crudapi.cn/crudapi-admin-web/docker.html) 70 | 9. [ 基于Vue和Quasar的前端SPA项目实战之数据导入(九)](https://help.crudapi.cn/crudapi-admin-web/import.html) 71 | 10. [ 基于Vue和Quasar的前端SPA项目实战之文件上传(九)](https://help.crudapi.cn/crudapi-admin-web/fileupload.html) 72 | 11. [ 基于Vue和Quasar的前端SPA项目实战之联合索引(十一)](https://help.crudapi.cn/crudapi-admin-web/unionindex.html) 73 | 12. [ 基于Vue和Quasar的前端SPA项目实战之数据库逆向(十二)](https://help.crudapi.cn/crudapi-admin-web/dbfirst.html) 74 | 13. [ 基于Vue和Quasar的前端SPA项目实战之数据导出(十三)](https://help.crudapi.cn/crudapi-admin-web/export.html) 75 | 14. [ 基于Vue和Quasar的前端SPA项目实战之模块管理(十四)](https://help.crudapi.cn/crudapi-admin-web/module.html) 76 | 15. [ 基于Vue和Quasar的前端SPA项目实战之元数据导出导入(十五)](https://help.crudapi.cn/crudapi-admin-web/metadataexportimport.html) 77 | 16. [ 基于Vue和Quasar的前端SPA项目实战之拖拽表单定制(十六)](https://help.crudapi.cn/crudapi-admin-web/formbuilder.html) 78 | 79 | 持续更新中。。。 80 | 81 | ## 演示 82 | 演示地址:[https://demo.crudapi.cn/crudapi/](https://demo.crudapi.cn/crudapi/) 83 | 84 | ![table](./img/table.png) 85 | 表单对应不同的对象 86 | 87 | ![relation](./img/relation.png) 88 | 表关系图显示不同对象之间的关系 89 | 90 | ![customer](./img/customer.png) 91 | 业务数据操作 92 | 93 | ## Java SDK 二次开发 94 | ### GitHub仓库 95 | [https://github.com/crudapi/crudapi-example](https://github.com/crudapi/crudapi-example) 96 | 97 | ### Gitee仓库 98 | [https://gitee.com/crudapi/crudapi-example](https://gitee.com/crudapi/crudapi-example) 99 | 100 | 101 | ## 联系方式 102 | #### 邮箱 103 | admin@crudapi.cn 104 | 105 | #### QQ 106 | 1440737304 107 | 108 | #### QQ群 109 | 632034576 110 | 111 | #### 微信 112 | undefinedneqnull 113 | 114 |
115 | 116 |
117 | 118 | #### 微信群 119 |
120 | 121 |
122 | 123 | 如有任何问题,欢迎咨询和交流! 124 | 125 | ## 授权 126 | 127 | Copyright (c) 2021-present crudapi 128 | 129 | [MIT License](https://baike.baidu.com/item/MIT许可证) 130 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | presets: [ 4 | '@quasar/babel-preset-app' 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export QUASAR_PUBLIC_PATH=mapapi 3 | npm run build 4 | 5 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | ## docker 2 | ```bash 3 | docker build -t crudapi-admin-web:latest . 4 | docker rm -f crudapi-admin-web 5 | docker run -d -p 80:80 --name crudapi-admin-web crudapi-admin-web:latest 6 | ``` 7 | -------------------------------------------------------------------------------- /docker/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | charset 'utf-8'; 6 | #access_log /var/log/nginx/host.access.log main; 7 | 8 | #error_page 404 /404.html; 9 | 10 | # redirect server error pages to the static page /50x.html 11 | # 12 | error_page 500 502 503 504 /50x.html; 13 | location = /50x.html { 14 | root /usr/share/nginx/html; 15 | } 16 | 17 | location ~ /api { 18 | proxy_pass http://demo.crudapi.cn; 19 | } 20 | 21 | location / { 22 | root /crudapi-admin-web; 23 | index index.html index.htm; 24 | try_files $uri $uri/ /crudapi/index.html; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /img/crudapiweixin.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/img/crudapiweixin.jpeg -------------------------------------------------------------------------------- /img/customer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/img/customer.png -------------------------------------------------------------------------------- /img/relation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/img/relation.png -------------------------------------------------------------------------------- /img/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/img/table.png -------------------------------------------------------------------------------- /img/weixinqun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/img/weixinqun.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "src/*": [ 6 | "src/*" 7 | ], 8 | "app/*": [ 9 | "*" 10 | ], 11 | "components/*": [ 12 | "src/components/*" 13 | ], 14 | "layouts/*": [ 15 | "src/layouts/*" 16 | ], 17 | "pages/*": [ 18 | "src/pages/*" 19 | ], 20 | "assets/*": [ 21 | "src/assets/*" 22 | ], 23 | "boot/*": [ 24 | "src/boot/*" 25 | ], 26 | "vue$": [ 27 | "node_modules/vue/dist/vue.esm.js" 28 | ] 29 | } 30 | }, 31 | "exclude": [ 32 | "dist", 33 | ".quasar", 34 | "node_modules" 35 | ] 36 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crudapi-admin-web", 3 | "version": "1.6.2", 4 | "description": "Crudapi Admin Web", 5 | "productName": "crudapi增删改查接口后台管理系统", 6 | "author": "admin@crudapi.cn", 7 | "private": false, 8 | "license": "MIT", 9 | "scripts": { 10 | "dev": "quasar dev", 11 | "build": "quasar build", 12 | "lint": "eslint --ext .js,.vue ./", 13 | "test": "echo \"No test specified\" && exit 0" 14 | }, 15 | "dependencies": { 16 | "@antv/g6": "^3.3.6", 17 | "@quasar/extras": "^1.0.0", 18 | "axios": "^0.18.1", 19 | "codemirror": "^5.65.6", 20 | "core-js": "^3.6.5", 21 | "form-data": "^4.0.0", 22 | "quasar": "^1.0.0", 23 | "sql-formatter": "^7.0.4", 24 | "vue-i18n": "^8.0.0", 25 | "vuedraggable": "^2.24.3" 26 | }, 27 | "devDependencies": { 28 | "@quasar/app": "^2.0.0", 29 | "babel-eslint": "^10.0.1", 30 | "eslint": "^6.8.0", 31 | "eslint-config-prettier": "^6.9.0", 32 | "eslint-loader": "^3.0.3", 33 | "eslint-plugin-vue": "^6.1.2" 34 | }, 35 | "browserslist": [ 36 | "ie >= 11", 37 | "last 10 Chrome versions", 38 | "last 10 Firefox versions", 39 | "last 4 Edge versions", 40 | "last 7 Safari versions", 41 | "last 8 Android versions", 42 | "last 8 ChromeAndroid versions", 43 | "last 8 FirefoxAndroid versions", 44 | "last 10 iOS versions", 45 | "last 5 Opera versions" 46 | ], 47 | "engines": { 48 | "node": ">= 10.18.1", 49 | "npm": ">= 6.13.4", 50 | "yarn": ">= 1.21.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /public/crudapi-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/crudapi-mini.png -------------------------------------------------------------------------------- /public/crudapi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/crudapi.png -------------------------------------------------------------------------------- /public/crudapiweixin.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/crudapiweixin.jpeg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/favicon.ico -------------------------------------------------------------------------------- /public/icons/favicon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/icons/favicon-128x128.png -------------------------------------------------------------------------------- /public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/icons/favicon-96x96.png -------------------------------------------------------------------------------- /public/price.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/price.png -------------------------------------------------------------------------------- /public/weixinqun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/public/weixinqun.png -------------------------------------------------------------------------------- /quasar.conf.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file runs in a Node context (it's NOT transpiled by Babel), so use only 3 | * the ES6 features that are supported by your Node version. https://node.green/ 4 | */ 5 | 6 | // Configuration for your app 7 | // https://quasar.dev/quasar-cli/quasar-conf-js 8 | /* eslint-env node */ 9 | 10 | module.exports = function ( ctx ) { 11 | const publicPath = process.env.QUASAR_PUBLIC_PATH || "crudapi"; 12 | 13 | return { 14 | // https://quasar.dev/quasar-cli/supporting-ts 15 | supportTS: false, 16 | 17 | // https://quasar.dev/quasar-cli/prefetch-feature 18 | // preFetch: true, 19 | 20 | // app boot file (/src/boot) 21 | // --> boot files are part of "main.js" 22 | // https://quasar.dev/quasar-cli/boot-files 23 | boot: [ 24 | 25 | 'i18n', 26 | 'axios', 27 | 'cpage', 28 | 'cfile', 29 | 'csqleditor', 30 | 'ctablenew', 31 | 'ctablelist', 32 | 'ctableedit', 33 | 'ctablelistedit', 34 | 'cindexlist', 35 | 'ctablelistread', 36 | 'crawDisplayer', 37 | 'cvaluedialog' 38 | ], 39 | 40 | // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-css 41 | css: [ 42 | 'app.styl' 43 | ], 44 | 45 | // https://github.com/quasarframework/quasar/tree/dev/extras 46 | extras: [ 47 | // 'ionicons-v4', 48 | // 'mdi-v5', 49 | // 'fontawesome-v5', 50 | // 'eva-icons', 51 | // 'themify', 52 | // 'line-awesome', 53 | // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! 54 | 55 | 'roboto-font', // optional, you are not bound to it 56 | 'material-icons', // optional, you are not bound to it 57 | 'material-icons-outlined' 58 | ], 59 | 60 | // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build 61 | build: { 62 | vueRouterMode: 'history', // available values: 'hash', 'history' 63 | 64 | // transpile: false, 65 | 66 | // Add dependencies for transpiling with Babel (Array of string/regex) 67 | // (from node_modules, which are by default not transpiled). 68 | // Applies only if "transpile" is set to true. 69 | // transpileDependencies: [], 70 | 71 | // rtl: false, // https://quasar.dev/options/rtl-support 72 | // preloadChunks: true, 73 | // showProgress: false, 74 | // gzip: true, 75 | // analyze: true, 76 | 77 | // Options below are automatically set depending on the env, set them if you want to override 78 | // extractCSS: false, 79 | 80 | // https://quasar.dev/quasar-cli/handling-webpack 81 | extendWebpack (cfg) { 82 | cfg.module.rules.push({ 83 | enforce: 'pre', 84 | test: /\.(js|vue)$/, 85 | loader: 'eslint-loader', 86 | exclude: /node_modules/ 87 | }) 88 | }, 89 | publicPath: `${publicPath}`, 90 | distDir: `dist/${ctx.modeName}/${publicPath}` 91 | }, 92 | 93 | // Full list of options: https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-devServer 94 | devServer: { 95 | https: false, 96 | port: 8080, 97 | open: true, // opens browser window automatically 98 | proxy: { 99 | //crudapi demo env 100 | "/api/*": { 101 | target: "https://demo.crudapi.cn", 102 | changeOrigin: true 103 | }, 104 | "/download/*": { 105 | target: "https://demo.crudapi.cn", 106 | changeOrigin: true 107 | } 108 | //local env 109 | // "/api/*": { 110 | // target: "http://127.0.0.1:8888", 111 | // changeOrigin: true 112 | // }, 113 | // "/download/*": { 114 | // target: "http://127.0.0.1:8888", 115 | // changeOrigin: true 116 | // } 117 | } 118 | }, 119 | 120 | // https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework 121 | framework: { 122 | iconSet: 'material-icons', // Quasar icon set 123 | lang: 'zh-hans', // Quasar language pack 124 | config: {}, 125 | 126 | // Possible values for "importStrategy": 127 | // * 'auto' - (DEFAULT) Auto-import needed Quasar components & directives 128 | // * 'all' - Manually specify what to import 129 | importStrategy: 'auto', 130 | 131 | // For special cases outside of where "auto" importStrategy can have an impact 132 | // (like functional components as one of the examples), 133 | // you can manually specify Quasar components/directives to be available everywhere: 134 | // 135 | // components: [], 136 | // directives: [], 137 | 138 | // Quasar plugins 139 | plugins: [ 140 | 'LocalStorage', 141 | 'Notify', 142 | 'Loading', 143 | 'Dialog', 144 | 'SessionStorage' 145 | ] 146 | }, 147 | 148 | // animations: 'all', // --- includes all animations 149 | // https://quasar.dev/options/animations 150 | animations: [], 151 | 152 | // https://quasar.dev/quasar-cli/developing-ssr/configuring-ssr 153 | ssr: { 154 | pwa: false 155 | }, 156 | 157 | // https://quasar.dev/quasar-cli/developing-pwa/configuring-pwa 158 | pwa: { 159 | workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest' 160 | workboxOptions: {}, // only for GenerateSW 161 | manifest: { 162 | name: `Crudapi Admin Web App`, 163 | short_name: `Crudapi Admin Web App`, 164 | description: `Crudapi Admin Web`, 165 | display: 'standalone', 166 | orientation: 'portrait', 167 | background_color: '#ffffff', 168 | theme_color: '#027be3', 169 | icons: [ 170 | { 171 | src: 'icons/icon-128x128.png', 172 | sizes: '128x128', 173 | type: 'image/png' 174 | }, 175 | { 176 | src: 'icons/icon-192x192.png', 177 | sizes: '192x192', 178 | type: 'image/png' 179 | }, 180 | { 181 | src: 'icons/icon-256x256.png', 182 | sizes: '256x256', 183 | type: 'image/png' 184 | }, 185 | { 186 | src: 'icons/icon-384x384.png', 187 | sizes: '384x384', 188 | type: 'image/png' 189 | }, 190 | { 191 | src: 'icons/icon-512x512.png', 192 | sizes: '512x512', 193 | type: 'image/png' 194 | } 195 | ] 196 | } 197 | }, 198 | 199 | // Full list of options: https://quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova 200 | cordova: { 201 | // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing 202 | }, 203 | 204 | // Full list of options: https://quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor 205 | capacitor: { 206 | hideSplashscreen: true 207 | }, 208 | 209 | // Full list of options: https://quasar.dev/quasar-cli/developing-electron-apps/configuring-electron 210 | electron: { 211 | bundler: 'packager', // 'packager' or 'builder' 212 | 213 | packager: { 214 | // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options 215 | 216 | // OS X / Mac App Store 217 | // appBundleId: '', 218 | // appCategoryType: '', 219 | // osxSign: '', 220 | // protocol: 'myapp://path', 221 | 222 | // Windows only 223 | // win32metadata: { ... } 224 | }, 225 | 226 | builder: { 227 | // https://www.electron.build/configuration/configuration 228 | 229 | appId: 'crudapi-admin-web' 230 | }, 231 | 232 | // More info: https://quasar.dev/quasar-cli/developing-electron-apps/node-integration 233 | nodeIntegration: true, 234 | 235 | extendWebpack (/* cfg */) { 236 | // do something with Electron main process Webpack cfg 237 | // chainWebpack also available besides this extendWebpack 238 | } 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "cp crudpi-admin-web" 3 | 4 | cp -R /crudapi/dist/* /crudapi/npm 5 | ls -al /crudapi/npm 6 | nginx -g "daemon off;" 7 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 11 | -------------------------------------------------------------------------------- /src/api/file.js: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from "boot/axios"; 2 | 3 | const HEADERS = { 4 | "Content-Type": "multipart/form-data" 5 | }; 6 | 7 | const file = { 8 | upload: async function(data, progressCallback) { 9 | console.log("file->upload") 10 | return axiosInstance.post(`/api/file` , data, 11 | { 12 | headers: HEADERS, 13 | onUploadProgress: (progressEvent) => { 14 | if (progressCallback) { 15 | progressCallback(progressEvent) 16 | } 17 | } 18 | }); 19 | }, 20 | bigUpload: async function(data, progressCallback) { 21 | console.log("file->bigUpload") 22 | return axiosInstance.post(`/api/file/big` , data, 23 | { 24 | headers: HEADERS, 25 | onUploadProgress: (progressEvent) => { 26 | if (progressCallback) { 27 | progressCallback(progressEvent) 28 | } 29 | } 30 | }); 31 | } 32 | }; 33 | 34 | export { file }; 35 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | export { user } from "./user"; 2 | export { metadataTable } from "./metadataTable"; 3 | export { metadataSequence } from "./metadataSequence"; 4 | export { metadataRelation } from "./metadataRelation"; 5 | export { file } from "./file"; 6 | export { table } from "./table"; 7 | -------------------------------------------------------------------------------- /src/api/metadataRelation.js: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from "boot/axios"; 2 | 3 | const metadataRelation = { 4 | create: function(dataSource, data) { 5 | return axiosInstance.post("/api/metadata/tablerelations", 6 | data, 7 | { 8 | dataSource: dataSource 9 | } 10 | ); 11 | }, 12 | update: function(dataSource, id, data) { 13 | return axiosInstance.patch("/api/metadata/tablerelations/" + id, 14 | data, 15 | { 16 | dataSource: dataSource 17 | } 18 | ); 19 | }, 20 | list: function(dataSource, page, rowsPerPage, search, query) { 21 | if (!page) { 22 | page = 1 23 | } 24 | 25 | if (!rowsPerPage) { 26 | rowsPerPage = 999 27 | } 28 | 29 | return axiosInstance.get("/api/metadata/tablerelations", 30 | { 31 | params: { 32 | offset: (page - 1) * rowsPerPage, 33 | limit: rowsPerPage, 34 | search: search, 35 | ...query 36 | }, 37 | dataSource: dataSource 38 | } 39 | ); 40 | }, 41 | count: function(dataSource, search, query) { 42 | return axiosInstance.get("/api/metadata/tablerelations/count", 43 | { 44 | params: { 45 | search: search, 46 | ...query 47 | }, 48 | dataSource: dataSource 49 | } 50 | ); 51 | }, 52 | get: function(dataSource, id) { 53 | return axiosInstance.get("/api/metadata/tablerelations/" + id, 54 | { 55 | params: { 56 | } 57 | } 58 | ); 59 | }, 60 | getFromTableById: function(dataSource, id) { 61 | return axiosInstance.get("/api/metadata/tablerelations/fromTable/" + id, 62 | { 63 | params: { 64 | }, 65 | dataSource: dataSource 66 | } 67 | ); 68 | }, 69 | getFromTableByName: function(dataSource, name) { 70 | return axiosInstance.get("/api/metadata/tablerelations/fromTable/name/" + name, 71 | { 72 | params: { 73 | }, 74 | dataSource: dataSource 75 | } 76 | ); 77 | }, 78 | delete: function(dataSource, id) { 79 | return axiosInstance.delete("/api/metadata/tablerelations/" + id, 80 | { 81 | dataSource: dataSource 82 | }); 83 | }, 84 | batchDelete: function(dataSource, ids) { 85 | return axiosInstance.delete("/api/metadata/tablerelations", 86 | { 87 | data: ids, 88 | dataSource: dataSource 89 | } 90 | ); 91 | } 92 | }; 93 | 94 | export { metadataRelation }; 95 | -------------------------------------------------------------------------------- /src/api/metadataSequence.js: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from "boot/axios"; 2 | 3 | const metadataSequence = { 4 | create: function(dataSource, data) { 5 | return axiosInstance.post("/api/metadata/sequences", 6 | data, 7 | { 8 | dataSource: dataSource 9 | } 10 | ); 11 | }, 12 | update: function(dataSource, id, data) { 13 | return axiosInstance.patch("/api/metadata/sequences/" + id, 14 | data, 15 | { 16 | dataSource: dataSource 17 | } 18 | ); 19 | }, 20 | list: function(dataSource, page, rowsPerPage, search, query) { 21 | if (!page) { 22 | page = 1 23 | } 24 | 25 | if (!rowsPerPage) { 26 | rowsPerPage = 10 27 | } 28 | 29 | return axiosInstance.get("/api/metadata/sequences", 30 | { 31 | params: { 32 | offset: (page - 1) * rowsPerPage, 33 | limit: rowsPerPage, 34 | search: search, 35 | ...query 36 | }, 37 | dataSource: dataSource 38 | } 39 | ); 40 | }, 41 | count: function(dataSource, search, query) { 42 | return axiosInstance.get("/api/metadata/sequences/count", 43 | { 44 | params: { 45 | search: search, 46 | ...query 47 | }, 48 | dataSource: dataSource 49 | } 50 | ); 51 | }, 52 | get: function(dataSource, id) { 53 | return axiosInstance.get("/api/metadata/sequences/" + id, 54 | { 55 | params: { 56 | }, 57 | dataSource: dataSource 58 | } 59 | ); 60 | }, 61 | delete: function(dataSource, id) { 62 | return axiosInstance.delete("/api/metadata/sequences/" + id, 63 | { 64 | dataSource: dataSource 65 | }); 66 | }, 67 | batchDelete: function(dataSource, ids) { 68 | return axiosInstance.delete("/api/metadata/sequences", 69 | { 70 | data: ids, 71 | dataSource: dataSource 72 | } 73 | ); 74 | } 75 | }; 76 | 77 | export { metadataSequence }; 78 | -------------------------------------------------------------------------------- /src/api/metadataTable.js: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from "boot/axios"; 2 | 3 | const HEADERS = { 4 | "Content-Type": "multipart/form-data" 5 | }; 6 | 7 | 8 | const metadataTable = { 9 | create: function(dataSource, data) { 10 | return axiosInstance.post("/api/metadata/tables", 11 | data, 12 | { 13 | dataSource: dataSource 14 | } 15 | ); 16 | }, 17 | update: function(dataSource, id, data) { 18 | return axiosInstance.patch("/api/metadata/tables/" + id, 19 | data, 20 | { 21 | dataSource: dataSource 22 | } 23 | ); 24 | }, 25 | list: function(dataSource, page, rowsPerPage, search, query) { 26 | if (!page) { 27 | page = 1 28 | } 29 | 30 | if (!rowsPerPage) { 31 | rowsPerPage = 10 32 | } 33 | 34 | return axiosInstance.get("/api/metadata/tables", 35 | { 36 | params: { 37 | offset: (page - 1) * rowsPerPage, 38 | limit: rowsPerPage, 39 | search: search, 40 | ...query 41 | }, 42 | dataSource: dataSource 43 | //permission: ["ROLE_META_TABLE_R"] 44 | } 45 | ); 46 | }, 47 | count: function(dataSource, search, query) { 48 | return axiosInstance.get("/api/metadata/tables/count", 49 | { 50 | params: { 51 | search: search, 52 | ...query 53 | }, 54 | dataSource: dataSource 55 | } 56 | ); 57 | }, 58 | get: function(dataSource, id) { 59 | return axiosInstance.get("/api/metadata/tables/" + id, 60 | { 61 | params: { 62 | }, 63 | dataSource: dataSource 64 | } 65 | ); 66 | }, 67 | getByName: function(dataSource, name) { 68 | return axiosInstance.get("/api/metadata/tables/name/" + name, 69 | { 70 | params: { 71 | }, 72 | dataSource: dataSource 73 | } 74 | ); 75 | }, 76 | getMetadata: function(dataSource, name) { 77 | return axiosInstance.get("/api/metadata/tables/metadatas/" + name, 78 | { 79 | params: { 80 | }, 81 | dataSource: dataSource 82 | } 83 | ); 84 | }, 85 | getMetadatas: function(dataSource) { 86 | return axiosInstance.get("/api/metadata/tables/metadatas", 87 | { 88 | params: { 89 | }, 90 | dataSource: dataSource 91 | } 92 | ); 93 | }, 94 | reverse: function(dataSource, tableName) { 95 | return axiosInstance.post("/api/metadata/tables/metadatas/reverse/" + tableName, 96 | {}, 97 | { 98 | dataSource: dataSource 99 | }); 100 | }, 101 | batchReverse: function(dataSource, tableNames) { 102 | return axiosInstance.post("/api/metadata/tables/metadatas/reverse", tableNames, 103 | { 104 | dataSource: dataSource 105 | }); 106 | }, 107 | repairMeataData: function(dataSource, name, columnNameLsit) { 108 | return axiosInstance.patch("/api/metadata/tables/metadata/" + name, 109 | columnNameLsit, 110 | { 111 | dataSource: dataSource 112 | } 113 | ); 114 | }, 115 | delete: function(dataSource, id, isDropPhysicalTable) { 116 | return axiosInstance.delete("/api/metadata/tables/" + id, { 117 | params: { 118 | isDropPhysicalTable: isDropPhysicalTable 119 | }, 120 | dataSource: dataSource 121 | }); 122 | }, 123 | batchDelete: function(dataSource, ids, isDropPhysicalTable) { 124 | return axiosInstance.delete("/api/metadata/tables", 125 | { 126 | data: ids, 127 | params: { 128 | isDropPhysicalTable: isDropPhysicalTable 129 | }, 130 | dataSource: dataSource 131 | } 132 | ); 133 | }, 134 | importFile: async function(dataSource, data, progressCallback) { 135 | return axiosInstance.post("/api/metadata/tables/file/import", data, 136 | { 137 | headers: HEADERS, 138 | onUploadProgress: (progressEvent) => { 139 | if (progressCallback) { 140 | progressCallback(progressEvent) 141 | } 142 | }, 143 | dataSource: dataSource 144 | }); 145 | }, 146 | import: async function(dataSource, data) { 147 | return axiosInstance.post("/api/metadata/tables/import", data, 148 | { 149 | dataSource: dataSource 150 | }); 151 | }, 152 | export: function(dataSource, ids) { 153 | return axiosInstance.post("/api/metadata/tables/export", 154 | ids, 155 | { 156 | dataSource: dataSource 157 | } 158 | ); 159 | }, 160 | listDataSource: function() { 161 | return axiosInstance.get("/api/metadata/dataSources", 162 | { 163 | params: { 164 | } 165 | } 166 | ); 167 | } 168 | }; 169 | 170 | export { metadataTable }; 171 | -------------------------------------------------------------------------------- /src/api/table.js: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from "boot/axios"; 2 | 3 | 4 | const HEADERS = { 5 | "Content-Type": "multipart/form-data" 6 | }; 7 | 8 | 9 | const table = { 10 | create: function(dataSource, tableName, data) { 11 | return axiosInstance.post("/api/business/" + tableName, 12 | data, 13 | { 14 | dataSource: dataSource 15 | } 16 | ); 17 | }, 18 | update: function(dataSource, tableName, id, data) { 19 | return axiosInstance.patch("/api/business/" + tableName + "/" + id, 20 | data, 21 | { 22 | dataSource: dataSource 23 | } 24 | ); 25 | }, 26 | list: function(dataSource, tableName, page, rowsPerPage, search, query, filter, select) { 27 | if (!page) { 28 | page = 1 29 | } 30 | 31 | if (!rowsPerPage) { 32 | rowsPerPage = 10 33 | } 34 | 35 | let filterStrEncode; 36 | if (filter) { 37 | let filterStr = JSON.stringify(filter); 38 | filterStrEncode = encodeURIComponent(filterStr); 39 | } 40 | 41 | return axiosInstance.get("/api/business/" + tableName, 42 | { 43 | params: { 44 | offset: (page - 1) * rowsPerPage, 45 | limit: rowsPerPage, 46 | search: search, 47 | ...query, 48 | filter: filterStrEncode, 49 | select: select 50 | }, 51 | dataSource: dataSource 52 | } 53 | ); 54 | }, 55 | listAllByIds: function(dataSource, tableName, ids) { 56 | let idStr = ids.join(","); 57 | return axiosInstance.get("/api/business/" + tableName + "/all", 58 | { 59 | params: { 60 | ids: idStr 61 | }, 62 | dataSource: dataSource 63 | } 64 | ); 65 | }, 66 | count: function(dataSource, tableName, search, query, filter) { 67 | let filterStrEncode; 68 | if (filter) { 69 | let filterStr = JSON.stringify(filter); 70 | filterStrEncode = encodeURIComponent(filterStr); 71 | } 72 | 73 | return axiosInstance.get("/api/business/" + tableName + "/count", 74 | { 75 | params: { 76 | search: search, 77 | ...query, 78 | filter: filterStrEncode 79 | }, 80 | dataSource: dataSource 81 | } 82 | ); 83 | }, 84 | get: function(dataSource, tableName, id) { 85 | return axiosInstance.get("/api/business/" + tableName + "/" + id, 86 | { 87 | params: { 88 | }, 89 | dataSource: dataSource 90 | } 91 | ); 92 | }, 93 | delete: function(dataSource, tableName, id, isSoftDelete) { 94 | return axiosInstance.delete("/api/business/" + tableName + "/" + id, 95 | { 96 | params: { 97 | isSoftDelete: isSoftDelete 98 | }, 99 | dataSource: dataSource 100 | }); 101 | }, 102 | batchDelete: function(dataSource, tableName, ids, isSoftDelete) { 103 | return axiosInstance.delete("/api/business/" + tableName, 104 | { 105 | data: ids, 106 | params: { 107 | isSoftDelete: isSoftDelete 108 | }, 109 | dataSource: dataSource 110 | } 111 | ); 112 | }, 113 | import: async function(dataSource, tableName, data, progressCallback) { 114 | console.log("table->import") 115 | console.log(data) 116 | return axiosInstance.post("/api/business/" + tableName + "/import", data, 117 | { 118 | headers: HEADERS, 119 | onUploadProgress: (progressEvent) => { 120 | if (progressCallback) { 121 | progressCallback(progressEvent) 122 | } 123 | }, 124 | dataSource: dataSource 125 | }); 126 | }, 127 | multiExport: function(dataSource, ids) { 128 | return axiosInstance.post("/api/business/export", 129 | ids, 130 | { 131 | dataSource: dataSource 132 | } 133 | ); 134 | }, 135 | multiImport: async function(dataSource, data, progressCallback) { 136 | console.log("table->import") 137 | console.log(data) 138 | return axiosInstance.post("/api/business/import", data, 139 | { 140 | headers: HEADERS, 141 | onUploadProgress: (progressEvent) => { 142 | if (progressCallback) { 143 | progressCallback(progressEvent) 144 | } 145 | }, 146 | dataSource: dataSource 147 | }); 148 | }, 149 | getImportTemplate: function(dataSource, tableName) { 150 | return axiosInstance.get("/api/business/" + tableName + "/import/template", 151 | { 152 | params: { 153 | }, 154 | dataSource: dataSource 155 | } 156 | ); 157 | }, 158 | export: function(dataSource, tableName, select, search, query, filter) { 159 | let filterStrEncode; 160 | if (filter) { 161 | let filterStr = JSON.stringify(filter); 162 | filterStrEncode = encodeURIComponent(filterStr); 163 | } 164 | return axiosInstance.post("/api/business/" + tableName + "/export", 165 | {}, 166 | { 167 | params: { 168 | select: select, 169 | search: search, 170 | ...query, 171 | filter: filterStrEncode 172 | }, 173 | dataSource: dataSource 174 | } 175 | ); 176 | }, 177 | exportXml: function(dataSource, tableName, select, search, query, filter, isDisplayCaption) { 178 | let filterStrEncode; 179 | if (filter) { 180 | let filterStr = JSON.stringify(filter); 181 | filterStrEncode = encodeURIComponent(filterStr); 182 | } 183 | return axiosInstance.post("/api/business/" + tableName + "/export/xml", 184 | {}, 185 | { 186 | params: { 187 | select: select, 188 | search: search, 189 | ...query, 190 | filter: filterStrEncode, 191 | isDisplayCaption: isDisplayCaption 192 | }, 193 | dataSource: dataSource 194 | } 195 | ); 196 | } 197 | }; 198 | 199 | export { table }; 200 | -------------------------------------------------------------------------------- /src/api/user.js: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from "boot/axios"; 2 | 3 | const HEADERS = { 4 | "Content-Type": "application/x-www-form-urlencoded" 5 | }; 6 | 7 | const user = { 8 | login: function(data) { 9 | return axiosInstance.post("/api/auth/login", 10 | data, 11 | { 12 | headers: HEADERS 13 | } 14 | ); 15 | }, 16 | logout: function() { 17 | return axiosInstance.get("/api/auth/logout", 18 | { 19 | headers: HEADERS 20 | } 21 | ); 22 | }, 23 | menu: function(dataSource) { 24 | return axiosInstance.get("/api/users/me/menu", 25 | { 26 | params: {}, 27 | dataSource: dataSource 28 | } 29 | ); 30 | }, 31 | table: function(dataSource) { 32 | return axiosInstance.get("/api/users/me/table", 33 | { 34 | params: {}, 35 | dataSource: dataSource 36 | } 37 | ); 38 | } 39 | }; 40 | 41 | export { user }; 42 | -------------------------------------------------------------------------------- /src/boot/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crudapi/crudapi-admin-web/5f3e6bee0055c3f96955ce64161bf84411241a0f/src/boot/.gitkeep -------------------------------------------------------------------------------- /src/boot/axios.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import axios from 'axios' 3 | import { Notify } from "quasar"; 4 | import qs from "qs"; 5 | import Router from "../router/index"; 6 | import { permissionService } from "../service"; 7 | 8 | Vue.prototype.$axios = axios 9 | 10 | // We create our own axios instance and set a custom base URL. 11 | // Note that if we wouldn't set any config here we do not need 12 | // a named export, as we could just `import axios from 'axios'` 13 | const axiosInstance = axios.create({ 14 | baseURL: process.env.API 15 | }); 16 | 17 | axiosInstance.defaults.transformRequest = [ 18 | function(data, headers) { 19 | // Do whatever you want to transform the data 20 | let contentType = headers["Content-Type"] || headers["content-type"]; 21 | if (!contentType) { 22 | contentType = "application/json"; 23 | headers["Content-Type"] = "application/json"; 24 | } 25 | 26 | if (contentType.indexOf("multipart/form-data") >= 0) { 27 | return data; 28 | } else if (contentType.indexOf("application/x-www-form-urlencoded") >= 0) { 29 | return qs.stringify(data); 30 | } 31 | 32 | return JSON.stringify(data); 33 | } 34 | ]; 35 | 36 | // Add a request interceptor 37 | axiosInstance.interceptors.request.use( 38 | function(config) { 39 | if (config.permission && !permissionService.check(config.permission)) { 40 | throw { 41 | message: "403 forbidden" 42 | }; 43 | } 44 | 45 | if (config.dataSource) { 46 | console.log("config.dataSource = " + config.dataSource); 47 | config.headers["dataSource"] = config.dataSource; 48 | } 49 | 50 | return config; 51 | }, 52 | function(error) { 53 | // Do something with request error 54 | return Promise.reject(error); 55 | } 56 | ); 57 | 58 | function login() { 59 | setTimeout(() => { 60 | Router.push({ 61 | path: "/login" 62 | }); 63 | }, 1000); 64 | } 65 | 66 | // Add a response interceptor 67 | axiosInstance.interceptors.response.use( 68 | function(response) { 69 | // Any status code that lie within the range of 2xx cause this function to trigger 70 | // Do something with response data 71 | return response; 72 | }, 73 | function(error) { 74 | // Any status codes that falls outside the range of 2xx cause this function to trigger 75 | // Do something with response error 76 | 77 | if (error.response) { 78 | if (error.response.status === 401) { 79 | Notify.create({ 80 | message: error.response.data.message, 81 | type: 'negative' 82 | }); 83 | login(); 84 | } else if (error.response.data && error.response.data.message) { 85 | Notify.create({ 86 | message: error.response.data.message, 87 | type: 'negative' 88 | }); 89 | } else { 90 | Notify.create({ 91 | message: error.response.statusText || error.response.status, 92 | type: 'negative' 93 | }); 94 | } 95 | } else if (error.message.indexOf("timeout") > -1) { 96 | Notify.create({ 97 | message: "Network timeout", 98 | type: 'negative' 99 | }); 100 | } else if (error.message) { 101 | Notify.create({ 102 | message: error.message, 103 | type: 'negative' 104 | }); 105 | } else { 106 | Notify.create({ 107 | message: "http request error", 108 | type: 'negative' 109 | }); 110 | } 111 | 112 | return Promise.reject(error); 113 | } 114 | ); 115 | 116 | // for use inside Vue files through this.$axios 117 | Vue.prototype.$axios = axiosInstance 118 | 119 | // Here we define a named export 120 | // that we can later use inside .js files: 121 | export { axiosInstance } 122 | -------------------------------------------------------------------------------- /src/boot/cfile.js: -------------------------------------------------------------------------------- 1 | import cFile from "../components/CFile"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cFile); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/cindexlist.js: -------------------------------------------------------------------------------- 1 | import cIndexList from "../components/CIndexList"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cIndexList); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/cpage.js: -------------------------------------------------------------------------------- 1 | import cPage from "../components/CPage"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cPage); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/crawDisplayer.js: -------------------------------------------------------------------------------- 1 | import cRawDisplayer from "../components/CRawDisplayer"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cRawDisplayer); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/csqleditor.js: -------------------------------------------------------------------------------- 1 | import cSqlEditor from "../components/CSqlEditor"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cSqlEditor); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/ctableedit.js: -------------------------------------------------------------------------------- 1 | import cTableEdit from "../components/CTableEdit"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cTableEdit); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/ctablelist.js: -------------------------------------------------------------------------------- 1 | import cTableList from "../components/CTableList"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cTableList); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/ctablelistedit.js: -------------------------------------------------------------------------------- 1 | import cTableListEdit from "../components/CTableListEdit"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cTableListEdit); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/ctablelistread.js: -------------------------------------------------------------------------------- 1 | import cTableListRead from "../components/CTableListRead"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cTableListRead); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/ctablenew.js: -------------------------------------------------------------------------------- 1 | import cTableNew from "../components/CTableNew"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cTableNew); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/cvaluedialog.js: -------------------------------------------------------------------------------- 1 | import cValueDialog from "../components/CValueDialog"; 2 | 3 | export default async ({ Vue }) => { 4 | Vue.use(cValueDialog); 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/i18n.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueI18n from 'vue-i18n' 3 | import messages from 'src/i18n' 4 | 5 | Vue.use(VueI18n) 6 | 7 | const i18n = new VueI18n({ 8 | locale: 'zh-hans', 9 | fallbackLocale: 'zh-hans', 10 | messages 11 | }) 12 | 13 | export default ({ app }) => { 14 | // Set i18n instance on app 15 | app.i18n = i18n 16 | } 17 | 18 | export { i18n } 19 | -------------------------------------------------------------------------------- /src/components/CDownload/CDownloadDialog.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | -------------------------------------------------------------------------------- /src/components/CDownload/index.js: -------------------------------------------------------------------------------- 1 | import CTableListRead from "./CTableListRead.vue"; 2 | 3 | const cTableListRead = { 4 | install: function(Vue) { 5 | Vue.component("CTableListRead", CTableListRead); 6 | } 7 | }; 8 | 9 | export default cTableListRead; 10 | -------------------------------------------------------------------------------- /src/components/CFile/CFile.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 51 | 52 | 235 | -------------------------------------------------------------------------------- /src/components/CFile/index.js: -------------------------------------------------------------------------------- 1 | import CFile from "./CFile.vue"; 2 | 3 | const cFile = { 4 | install: function(Vue) { 5 | Vue.component("CFile", CFile); 6 | } 7 | }; 8 | 9 | export default cFile; 10 | -------------------------------------------------------------------------------- /src/components/CIndexList/CIndexList.vue: -------------------------------------------------------------------------------- 1 | 97 | 98 | 104 | 105 | 379 | -------------------------------------------------------------------------------- /src/components/CIndexList/index.js: -------------------------------------------------------------------------------- 1 | import CIndexList from "./CIndexList.vue"; 2 | 3 | const cIndexList = { 4 | install: function(Vue) { 5 | Vue.component("CIndexList", CIndexList); 6 | } 7 | }; 8 | 9 | export default cIndexList; 10 | -------------------------------------------------------------------------------- /src/components/CPage/CPage.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 81 | 82 | 185 | -------------------------------------------------------------------------------- /src/components/CPage/index.js: -------------------------------------------------------------------------------- 1 | import CPage from "./CPage.vue"; 2 | 3 | const cPage = { 4 | install: function(Vue) { 5 | Vue.component("CPage", CPage); 6 | } 7 | }; 8 | 9 | export default cPage; 10 | -------------------------------------------------------------------------------- /src/components/CRawDisplayer/CRawDisplayer.vue: -------------------------------------------------------------------------------- 1 | 7 | 27 | 32 | -------------------------------------------------------------------------------- /src/components/CRawDisplayer/index.js: -------------------------------------------------------------------------------- 1 | import CRawDisplayer from "./CRawDisplayer.vue"; 2 | 3 | const cRawDisplayer = { 4 | install: function(Vue) { 5 | Vue.component("CRawDisplayer", CRawDisplayer); 6 | } 7 | }; 8 | 9 | export default cRawDisplayer; 10 | -------------------------------------------------------------------------------- /src/components/CSqlEditor/CSqlEditor.vue: -------------------------------------------------------------------------------- 1 | 6 | 96 | -------------------------------------------------------------------------------- /src/components/CSqlEditor/index.js: -------------------------------------------------------------------------------- 1 | import CSqlEditor from "./CSqlEditor.vue"; 2 | 3 | const cSqlEditor = { 4 | install: function(Vue) { 5 | Vue.component("CSqlEditor", CSqlEditor); 6 | } 7 | }; 8 | 9 | export default cSqlEditor; 10 | -------------------------------------------------------------------------------- /src/components/CTableEdit/index.js: -------------------------------------------------------------------------------- 1 | import CTableEdit from "./CTableEdit.vue"; 2 | 3 | const cTableEdit = { 4 | install: function(Vue) { 5 | Vue.component("CTableEdit", CTableEdit); 6 | } 7 | }; 8 | 9 | export default cTableEdit; 10 | -------------------------------------------------------------------------------- /src/components/CTableList/index.js: -------------------------------------------------------------------------------- 1 | import CTableList from "./CTableList.vue"; 2 | 3 | const cTableList = { 4 | install: function(Vue) { 5 | Vue.component("CTableList", CTableList); 6 | } 7 | }; 8 | 9 | export default cTableList; 10 | -------------------------------------------------------------------------------- /src/components/CTableListEdit/index.js: -------------------------------------------------------------------------------- 1 | import CTableListEdit from "./CTableListEdit.vue"; 2 | 3 | const cTableListEdit = { 4 | install: function(Vue) { 5 | Vue.component("CTableListEdit", CTableListEdit); 6 | } 7 | }; 8 | 9 | export default cTableListEdit; 10 | -------------------------------------------------------------------------------- /src/components/CTableListRead/CTableListReadDialog.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/CTableListRead/index.js: -------------------------------------------------------------------------------- 1 | import CTableListRead from "./CTableListRead.vue"; 2 | 3 | const cTableListRead = { 4 | install: function(Vue) { 5 | Vue.component("CTableListRead", CTableListRead); 6 | } 7 | }; 8 | 9 | export default cTableListRead; 10 | -------------------------------------------------------------------------------- /src/components/CTableNew/index.js: -------------------------------------------------------------------------------- 1 | import CTableNew from "./CTableNew.vue"; 2 | 3 | const cTableNew = { 4 | install: function(Vue) { 5 | Vue.component("CTableNew", CTableNew); 6 | } 7 | }; 8 | 9 | export default cTableNew; 10 | -------------------------------------------------------------------------------- /src/components/CValueDialog/CValueDialog.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | -------------------------------------------------------------------------------- /src/components/CValueDialog/index.js: -------------------------------------------------------------------------------- 1 | import CValueDialog from "./CValueDialog.vue"; 2 | 3 | const cValueDialog = { 4 | install: function(Vue) { 5 | Vue.component("CValueDialog", CValueDialog); 6 | } 7 | }; 8 | 9 | export default cValueDialog; 10 | -------------------------------------------------------------------------------- /src/components/EssentialLink.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 50 | -------------------------------------------------------------------------------- /src/css/app.styl: -------------------------------------------------------------------------------- 1 | // app global css in Stylus form 2 | // app global css in Stylus form 3 | .q-field 4 | &__control 5 | height 32px 6 | &__marginal 7 | height 32px 8 | &--auto-height 9 | .q-field__control 10 | height auto 11 | .q-field__control, .q-field__native 12 | min-height 32px 13 | 14 | .bg-drawcolor 15 | background $drawcolor !important 16 | 17 | .bg-layoutcolor 18 | background $layoutcolor !important 19 | 20 | .bg-listcolor 21 | background $listactioncolor !important 22 | 23 | .bg-page 24 | background $pagecolor !important 25 | 26 | .q-tree__node-header-content 27 | height:22px; 28 | font-size:14px; 29 | font-family:PingFangSC-Regular,PingFang SC; 30 | font-weight:400; 31 | color:rgba(51,51,51,1); 32 | line-height:22px; 33 | 34 | .query-cond 35 | font-size:14px; 36 | font-family:PingFangSC-Regular,PingFang SC; 37 | font-weight:400; 38 | color:rgba(0,0,0,0.65); 39 | 40 | .query-input 41 | height:32px; 42 | background:rgba(255,255,255,1); 43 | border-radius:4px; 44 | border:1px solid rgba(238,238,238,1); 45 | 46 | .bg-table-list 47 | background:rgba(255,255,255,1); 48 | border-radius:2px; 49 | border:1px solid rgba(238,238,238,1); 50 | 51 | .q-table 52 | thead 53 | height: 22px; 54 | font-size: 14px; 55 | font-family: PingFangSC-Medium,PingFang SC; 56 | font-weight: 500; 57 | color: rgba(161,161,161,1); 58 | line-height: 22px; 59 | tbody 60 | height:22px; 61 | font-size:14px; 62 | font-family:PingFangSC-Regular,PingFang SC; 63 | font-weight:400; 64 | color:rgba(0,0,0,1); 65 | line-height:22px; 66 | 67 | .home 68 | .q-table 69 | thead 70 | height:20px; 71 | font-size:13px; 72 | font-family:PingFangSC-Regular,PingFang SC; 73 | font-weight:400; 74 | color:rgba(102,102,102,1); 75 | line-height:20px; 76 | tbody 77 | height:20px; 78 | font-size:13px; 79 | font-family:PingFangSC-Regular,PingFang SC; 80 | font-weight:400; 81 | color:rgba(161,161,161,1); 82 | line-height:20px; 83 | -------------------------------------------------------------------------------- /src/css/quasar.variables.styl: -------------------------------------------------------------------------------- 1 | // Quasar Stylus Variables 2 | // -------------------------------------------------- 3 | // To customize the look and feel of this app, you can override 4 | // the Stylus variables found in Quasar's source Stylus files. 5 | 6 | // Check documentation for full list of Quasar variables 7 | 8 | // Your own variables (that are declared here) and Quasar's own 9 | // ones will be available out of the box in your .vue/.styl files 10 | 11 | // It's highly recommended to change the default colors 12 | // to match your app's branding. 13 | // Tip: Use the "Theme Builder" on Quasar's documentation website. 14 | 15 | $primary = #35C8E8 16 | $secondary = #26A69A 17 | $accent = #9C27B0 18 | 19 | $dark = #1D1D1D 20 | 21 | $positive = #21BA45 22 | $negative = #C10015 23 | $info = #31CCEC 24 | $warning = #F2C037 25 | 26 | 27 | $drawcolor ?= #001529 28 | $layoutcolor ?= #25262A 29 | $listactioncolor ?= #EEEEEE 30 | $pagecolor ?= #FBFBFB 31 | -------------------------------------------------------------------------------- /src/i18n/en-us/index.js: -------------------------------------------------------------------------------- 1 | // This is just an example, 2 | // so you can safely delete all default props below 3 | 4 | export default { 5 | failed: 'Action failed', 6 | success: 'Action was successful' 7 | } 8 | -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import enUS from './en-us' 2 | 3 | export default { 4 | 'en-us': enUS 5 | } 6 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= productName %> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /src/models/file-md5.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import SparkMD5 from '../plugins/js-spark-md5.js' 4 | 5 | export default function (file, chunk, callback) { 6 | var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, 7 | file = file, 8 | chunkSize = chunk || 2097152, // Read in chunks of 2MB 9 | chunks = Math.ceil(file.size / chunkSize), 10 | currentChunk = 0, 11 | spark = new SparkMD5.ArrayBuffer(), 12 | fileReader = new FileReader(); 13 | 14 | fileReader.onload = function (e) { 15 | console.log('read chunk nr', currentChunk + 1, 'of', chunks); 16 | spark.append(e.target.result); // Append array buffer 17 | currentChunk++; 18 | 19 | if (currentChunk < chunks) { 20 | loadNext(); 21 | } else { 22 | callback(null, spark.end()); 23 | console.log('finished loading'); 24 | } 25 | }; 26 | 27 | fileReader.onerror = function () { 28 | callback('oops, something went wrong.'); 29 | }; 30 | 31 | function loadNext() { 32 | var start = currentChunk * chunkSize, 33 | end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize; 34 | 35 | fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); 36 | } 37 | 38 | loadNext(); 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /src/pages/About.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 30 | 31 | 65 | -------------------------------------------------------------------------------- /src/pages/Error403.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /src/pages/Error404.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /src/pages/Index.vue: -------------------------------------------------------------------------------- 1 | 113 | 114 | 127 | 128 | 238 | -------------------------------------------------------------------------------- /src/pages/Login.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 140 | 141 | 221 | -------------------------------------------------------------------------------- /src/pages/Setting.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 82 | -------------------------------------------------------------------------------- /src/pages/business/table/edit.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 27 | 28 | 129 | -------------------------------------------------------------------------------- /src/pages/business/table/import.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 33 | 34 | 158 | -------------------------------------------------------------------------------- /src/pages/business/table/list.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 102 | -------------------------------------------------------------------------------- /src/pages/business/table/multiImport.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 30 | 31 | 116 | -------------------------------------------------------------------------------- /src/pages/business/table/new.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 24 | 25 | 111 | -------------------------------------------------------------------------------- /src/pages/metadata/relation/graph.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 204 | -------------------------------------------------------------------------------- /src/pages/metadata/relation/new.vue: -------------------------------------------------------------------------------- 1 | 108 | 109 | 115 | 116 | 286 | -------------------------------------------------------------------------------- /src/pages/metadata/sequence/edit.vue: -------------------------------------------------------------------------------- 1 | 132 | 133 | 139 | 140 | 259 | -------------------------------------------------------------------------------- /src/pages/metadata/sequence/new.vue: -------------------------------------------------------------------------------- 1 | 116 | 117 | 123 | 124 | 239 | -------------------------------------------------------------------------------- /src/pages/metadata/sql/new.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 60 | 61 | 154 | -------------------------------------------------------------------------------- /src/pages/metadata/table/indexList.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 40 | 41 | 176 | -------------------------------------------------------------------------------- /src/pages/metadata/table/reverse.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 278 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | import routes from './routes' 5 | import { authService } from "../service"; 6 | import store from "../store"; 7 | 8 | Vue.use(VueRouter) 9 | 10 | /* 11 | * If not building with SSR mode, you can 12 | * directly export the Router instantiation; 13 | * 14 | * The function below can be async too; either use 15 | * async/await or return a Promise which resolves 16 | * with the Router instance. 17 | */ 18 | const Router = new VueRouter({ 19 | scrollBehavior: () => ({ x: 0, y: 0 }), 20 | routes, 21 | 22 | // Leave these as they are and change in quasar.conf.js instead! 23 | // quasar.conf.js -> build -> vueRouterMode 24 | // quasar.conf.js -> build -> publicPath 25 | mode: process.env.VUE_ROUTER_MODE, 26 | base: process.env.VUE_ROUTER_BASE 27 | }); 28 | 29 | const whiteList = ["/login", "/403"]; 30 | 31 | function hasPermission(router) { 32 | if (whiteList.indexOf(router.path) !== -1) { 33 | return true; 34 | } 35 | 36 | return true; 37 | } 38 | 39 | Router.beforeEach(async (to, from, next) => { 40 | if (window.location.hostname === "demo.crudapi.cn" && _hmt && to.path) { 41 | _hmt.push(["_trackPageview", to.fullPath]); 42 | console.log("上报百度统计", to.fullPath); 43 | } 44 | 45 | let token = authService.getToken(); 46 | if (token) { 47 | let userInfo = store.state.user.userInfo; 48 | if (!userInfo.username) { 49 | try { 50 | await store.dispatch("user/getUserInfo"); 51 | next(); 52 | } catch (e) { 53 | if (whiteList.indexOf(to.path) !== -1) { 54 | next(); 55 | } else { 56 | next("/login"); 57 | } 58 | } 59 | } else { 60 | if (hasPermission(to)) { 61 | next(); 62 | } else { 63 | next({ path: "/403", replace: true }); 64 | } 65 | } 66 | } else { 67 | if (whiteList.indexOf(to.path) !== -1) { 68 | next(); 69 | } else { 70 | next("/login"); 71 | } 72 | } 73 | }); 74 | 75 | export default Router; 76 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | const routes = [ 2 | { 3 | path: "/login", 4 | name: "login", 5 | component: () => import("pages/Login.vue") 6 | }, 7 | { 8 | path: "/403", 9 | name: "403", 10 | component: () => import("pages/Error403") 11 | }, 12 | { 13 | path: '/', 14 | component: () => import('layouts/MainLayout.vue'), 15 | children: [ 16 | { path: '', component: () => import('pages/Index.vue') }, 17 | { path: 'dataSource/:dataSource', component: () => import('pages/Index.vue') }, 18 | { 19 | name: "about", 20 | path: "about", 21 | meta: { isAllowBack: true }, 22 | component: () => import("pages/About.vue") 23 | }, 24 | { 25 | name: "setting", 26 | path: "setting", 27 | meta: { isAllowBack: true }, 28 | component: () => import("pages/Setting.vue") 29 | }, 30 | { 31 | name: "form-builder", 32 | path: "dataSource/:dataSource/metadata/tables/:id/formBuilder", 33 | meta: { isAllowBack: true }, 34 | component: () => import("pages/form-builder/index.vue") 35 | }, 36 | { 37 | name: "tablePermission", 38 | path: "dataSource/:dataSource/metadata/tables/:id/tablePermission", 39 | meta: { isAllowBack: true }, 40 | component: () => import("pages/tablePermission/index.vue") 41 | }, 42 | { 43 | name: "sequenceList", 44 | path: "dataSource/:dataSource/metadata/sequences", 45 | meta: { isAllowBack: true }, 46 | component: () => import("pages/metadata/sequence/list.vue") 47 | }, 48 | { 49 | name: "sequenceNew", 50 | path: "dataSource/:dataSource/metadata/sequences/new", 51 | meta: { isAllowBack: true }, 52 | component: () => import("pages/metadata/sequence/new.vue") 53 | }, 54 | { 55 | name: "sequenceEdit", 56 | path: "dataSource/:dataSource/metadata/sequences/:id", 57 | meta: { isAllowBack: true }, 58 | component: () => import("pages/metadata/sequence/edit.vue") 59 | }, 60 | { 61 | name: "sqlNew", 62 | path: "dataSource/:dataSource/metadata/sqls/new", 63 | meta: { isAllowBack: true }, 64 | component: () => import("pages/metadata/sql/new.vue") 65 | }, 66 | { 67 | name: "tables", 68 | path: "dataSource/:dataSource/metadata/tables", 69 | meta: { isAllowBack: true }, 70 | component: () => import("pages/metadata/table/list.vue") 71 | }, 72 | { 73 | name: "tableImport", 74 | path: "dataSource/:dataSource/metadata/tables/import", 75 | meta: { isAllowBack: true }, 76 | component: () => import("pages/metadata/table/import.vue") 77 | }, 78 | { 79 | name: "tableReverse", 80 | path: "dataSource/:dataSource/metadata/tables/reverse", 81 | meta: { isAllowBack: true }, 82 | component: () => import("pages/metadata/table/reverse.vue") 83 | }, 84 | { 85 | name: "tableNew", 86 | path: "dataSource/:dataSource/metadata/tables/new", 87 | meta: { isAllowBack: true }, 88 | component: () => import("pages/metadata/table/new.vue") 89 | }, 90 | { 91 | name: "tableEdit", 92 | path: "dataSource/:dataSource/metadata/tables/:id", 93 | meta: { isAllowBack: true }, 94 | component: () => import("pages/metadata/table/edit.vue") 95 | }, 96 | { 97 | name: "tableIndexs", 98 | path: "dataSource/:dataSource/metadata/tables/:id/indexs", 99 | meta: { isAllowBack: true }, 100 | component: () => import("pages/metadata/table/indexList.vue") 101 | }, 102 | { 103 | name: "relationGraph", 104 | path: "dataSource/:dataSource/metadata/relations/graph", 105 | meta: { isAllowBack: true }, 106 | component: () => import("pages/metadata/relation/graph.vue") 107 | }, 108 | { 109 | name: "relations", 110 | path: "dataSource/:dataSource/metadata/relations", 111 | meta: { isAllowBack: true }, 112 | component: () => import("pages/metadata/relation/list.vue") 113 | },{ 114 | name: "relationNew", 115 | path: "dataSource/:dataSource/metadata/relations/new", 116 | meta: { isAllowBack: true }, 117 | component: () => import("pages/metadata/relation/new.vue") 118 | },{ 119 | name: "relationEdit", 120 | path: "dataSource/:dataSource/metadata/relations/:id", 121 | meta: { isAllowBack: true }, 122 | component: () => import("pages/metadata/relation/edit.vue") 123 | }, 124 | { 125 | name: "businessMultiImport", 126 | path: "dataSource/:dataSource/business/import", 127 | meta: { isAllowBack: true }, 128 | component: () => import("pages/business/table/multiImport.vue") 129 | }, 130 | { 131 | name: "business", 132 | path: "dataSource/:dataSource/business/:tableName", 133 | meta: { isAllowBack: true }, 134 | component: () => import("pages/business/table/list.vue") 135 | }, 136 | { 137 | name: "businessNew", 138 | path: "dataSource/:dataSource/business/:tableName/new", 139 | meta: { isAllowBack: true }, 140 | component: () => import("pages/business/table/new.vue") 141 | }, 142 | { 143 | name: "businessImport", 144 | path: "dataSource/:dataSource/business/:tableName/import", 145 | meta: { isAllowBack: true }, 146 | component: () => import("pages/business/table/import.vue") 147 | }, 148 | { 149 | name: "businessEdit", 150 | path: "dataSource/:dataSource/business/:tableName/:recId", 151 | meta: { isAllowBack: true }, 152 | component: () => import("pages/business/table/edit.vue") 153 | } 154 | ] 155 | }, 156 | 157 | // Always leave this as last one, 158 | // but you can also remove it 159 | { 160 | path: '*', 161 | component: () => import('pages/Error404.vue') 162 | } 163 | ] 164 | 165 | export default routes 166 | -------------------------------------------------------------------------------- /src/service/authService.js: -------------------------------------------------------------------------------- 1 | import { LocalStorage } from "quasar"; 2 | 3 | const authService = { 4 | getToken: function() { 5 | return LocalStorage.getItem("token"); 6 | }, 7 | setToken: function(token) { 8 | LocalStorage.set("token", token); 9 | } 10 | }; 11 | 12 | export { authService }; 13 | -------------------------------------------------------------------------------- /src/service/fileService.js: -------------------------------------------------------------------------------- 1 | import { file } from "../api"; 2 | 3 | const fileService = { 4 | upload: async function (fileObj, progressCallback) { 5 | return file.upload(fileObj, progressCallback).then((res) => { 6 | console.log(res); 7 | return res.data; 8 | }) 9 | }, 10 | bigUpload: async function (fileObj, progressCallback) { 11 | return file.bigUpload(fileObj, progressCallback).then((res) => { 12 | console.log(res); 13 | return res.data; 14 | }) 15 | } 16 | }; 17 | 18 | export { fileService }; 19 | -------------------------------------------------------------------------------- /src/service/index.js: -------------------------------------------------------------------------------- 1 | export { settingService } from "./settingService"; 2 | export { userService } from "./userService"; 3 | export { permissionService } from "./permissionService"; 4 | export { authService } from "./authService"; 5 | export { metadataTableService } from "./metadataTableService"; 6 | export { metadataSequenceService } from "./metadataSequenceService"; 7 | export { metadataRelationService } from "./metadataRelationService"; 8 | export { tableService } from "./tableService"; 9 | export { fileService } from "./fileService"; 10 | -------------------------------------------------------------------------------- /src/service/metadataRelationService.js: -------------------------------------------------------------------------------- 1 | import { metadataRelation } from "../api"; 2 | 3 | const metadataRelationService = { 4 | create: async function(dataSource, data) { 5 | var res = await metadataRelation.create(dataSource, data); 6 | return res.data; 7 | }, 8 | update: async function(dataSource, id, data) { 9 | var res = await metadataRelation.update(dataSource, id, data); 10 | return res.data; 11 | }, 12 | list: async function(dataSource, page, rowsPerPage, search, query) { 13 | var res = await metadataRelation.list(dataSource, page, rowsPerPage, search, query); 14 | return res.data; 15 | }, 16 | count: async function(dataSource, search, query) { 17 | var res = await metadataRelation.count(dataSource, search, query); 18 | return res.data; 19 | }, 20 | get: async function(dataSource, id) { 21 | var res = await metadataRelation.get(dataSource, id); 22 | return res.data; 23 | }, 24 | getByName: async function(dataSource, name) { 25 | var res = await metadataRelation.getFromTableByName(dataSource, name); 26 | return res.data; 27 | }, 28 | delete: async function(dataSource, id) { 29 | var res = await metadataRelation.delete(dataSource, id); 30 | return res.data; 31 | }, 32 | batchDelete: async function(dataSource, ids) { 33 | var res = await metadataRelation.batchDelete(dataSource, ids); 34 | return res.data; 35 | } 36 | }; 37 | 38 | export { metadataRelationService }; 39 | -------------------------------------------------------------------------------- /src/service/metadataSequenceService.js: -------------------------------------------------------------------------------- 1 | import { metadataSequence } from "../api"; 2 | 3 | const metadataSequenceService = { 4 | create: async function(dataSource, data) { 5 | var res = await metadataSequence.create(dataSource, data); 6 | return res.data; 7 | }, 8 | update: async function(dataSource, id, data) { 9 | var res = await metadataSequence.update(dataSource, id, data); 10 | return res.data; 11 | }, 12 | list: async function(dataSource, page, rowsPerPage, search, query) { 13 | var res = await metadataSequence.list(dataSource, page, rowsPerPage, search, query); 14 | return res.data; 15 | }, 16 | count: async function(dataSource, search, query) { 17 | var res = await metadataSequence.count(dataSource, search, query); 18 | return res.data; 19 | }, 20 | get: async function(dataSource, id) { 21 | var res = await metadataSequence.get(dataSource, id); 22 | return res.data; 23 | }, 24 | delete: async function(dataSource, id) { 25 | var res = await metadataSequence.delete(dataSource, id); 26 | return res.data; 27 | }, 28 | batchDelete: async function(dataSource, ids) { 29 | var res = await metadataSequence.batchDelete(dataSource, ids); 30 | return res.data; 31 | } 32 | }; 33 | 34 | export { metadataSequenceService }; 35 | -------------------------------------------------------------------------------- /src/service/metadataTableService.js: -------------------------------------------------------------------------------- 1 | import { metadataTable } from "../api"; 2 | 3 | const metadataTableService = { 4 | create: async function(dataSource, data) { 5 | var res = await metadataTable.create(dataSource, data); 6 | return res.data; 7 | }, 8 | update: async function(dataSource, id, data) { 9 | var res = await metadataTable.update(dataSource, id, data); 10 | return res.data; 11 | }, 12 | list: async function(dataSource, page, rowsPerPage, search, query) { 13 | var res = await metadataTable.list(dataSource, page, rowsPerPage, search, query); 14 | return res.data; 15 | }, 16 | count: async function(dataSource, search, query) { 17 | var res = await metadataTable.count(dataSource, search, query); 18 | return res.data; 19 | }, 20 | get: async function(dataSource, id) { 21 | var res = await metadataTable.get(dataSource, id); 22 | return res.data; 23 | }, 24 | getByName: async function(dataSource, id) { 25 | var res = await metadataTable.getByName(dataSource, id); 26 | return res.data; 27 | }, 28 | getMetadata: async function(dataSource, name) { 29 | var res = await metadataTable.getMetadata(dataSource, name); 30 | return res.data; 31 | }, 32 | getMetadatas: async function(dataSource) { 33 | var res = await metadataTable.getMetadatas(dataSource); 34 | return res.data; 35 | }, 36 | reverse: async function(dataSource, tableName) { 37 | var res = await metadataTable.reverse(dataSource, tableName); 38 | return res.data; 39 | }, 40 | batchReverse: async function(dataSource, tableNames) { 41 | var res = await metadataTable.batchReverse(dataSource,tableNames); 42 | return res.data; 43 | }, 44 | repairMeataData: async function(dataSource, name, columnNameLsit) { 45 | var res = await metadataTable.repairMeataData(dataSource,name, columnNameLsit); 46 | return res.data; 47 | }, 48 | delete: async function(dataSource, id, isDropPhysicalTable) { 49 | var res = await metadataTable.delete(dataSource,id, isDropPhysicalTable); 50 | return res.data; 51 | }, 52 | batchDelete: async function(dataSource, ids, isDropPhysicalTable) { 53 | var res = await metadataTable.batchDelete(dataSource,ids, isDropPhysicalTable); 54 | return res.data; 55 | }, 56 | importFile: async function (dataSource, fileObj, progressCallback) { 57 | return metadataTable.import(dataSource, fileObj, progressCallback).then((res) => { 58 | console.log(res); 59 | return res.data; 60 | }) 61 | }, 62 | import: async function (dataSource, data) { 63 | var res = await metadataTable.import(dataSource, data); 64 | return res.data; 65 | }, 66 | export: async function(dataSource, ids) { 67 | var res = await metadataTable.export(dataSource, ids); 68 | return res.data; 69 | }, 70 | listDataSource: async function() { 71 | var res = await metadataTable.listDataSource(); 72 | return res.data; 73 | } 74 | }; 75 | 76 | export { metadataTableService }; 77 | -------------------------------------------------------------------------------- /src/service/permissionService.js: -------------------------------------------------------------------------------- 1 | import { LocalStorage } from "quasar"; 2 | 3 | const permissionService = { 4 | get: function() { 5 | return LocalStorage.getItem("permission"); 6 | }, 7 | set: function(data) { 8 | LocalStorage.set("permission", data); 9 | }, 10 | isSuperAdmin: function() { 11 | let data = this.get(); 12 | if (!data) { 13 | return false; 14 | } 15 | 16 | return data.isSuperAdmin; 17 | }, 18 | check: function(needPermissions) { 19 | if (needPermissions && needPermissions.length > 0) { 20 | let data = this.get(); 21 | if (!data) { 22 | return false; 23 | } 24 | 25 | let isSuperAdmin = data.isSuperAdmin; 26 | if (isSuperAdmin) { 27 | return true; 28 | } 29 | 30 | if (!data.permissions) { 31 | return false; 32 | } 33 | 34 | let permissions = data.permissions; 35 | let hasPermission = permissions.some(s => { 36 | return needPermissions.indexOf(s.authority) > -1; 37 | }); 38 | 39 | return hasPermission; 40 | } 41 | return true; 42 | } 43 | }; 44 | 45 | export { permissionService }; 46 | -------------------------------------------------------------------------------- /src/service/settingService.js: -------------------------------------------------------------------------------- 1 | import { LocalStorage, SessionStorage} from "quasar"; 2 | 3 | const settingService = { 4 | clear: function() { 5 | LocalStorage.clear(); 6 | SessionStorage.clear(); 7 | } 8 | }; 9 | 10 | export { settingService }; 11 | -------------------------------------------------------------------------------- /src/service/tableService.js: -------------------------------------------------------------------------------- 1 | import { table } from "../api"; 2 | 3 | const tableService = { 4 | create: async function(dataSource, tableName, data) { 5 | var res = await table.create(dataSource, tableName, data); 6 | return res.data; 7 | }, 8 | update: async function(dataSource, tableName, id, data) { 9 | var res = await table.update(dataSource, tableName, id, data); 10 | return res.data; 11 | }, 12 | count: async function(dataSource, tableName, search, query, filter) { 13 | var res = await table.count(dataSource, tableName, search, query, filter); 14 | return res.data; 15 | }, 16 | list: async function(dataSource, tableName, page, rowsPerPage, search, query, filter, select) { 17 | var res = await table.list(dataSource, tableName, page, rowsPerPage, search, query, filter, select); 18 | return res.data; 19 | }, 20 | listByIds: async function(dataSource, tableName, ids) { 21 | var res = await table.listAllByIds(dataSource, tableName, ids); 22 | return res.data; 23 | }, 24 | get: async function(dataSource, tableName, id) { 25 | var res = await table.get(dataSource, tableName, id); 26 | return res.data; 27 | }, 28 | delete: async function(dataSource, tableName, id, isSoftDelete) { 29 | var res = await table.delete(dataSource, tableName, id, isSoftDelete); 30 | return res.data; 31 | }, 32 | batchDelete: async function(dataSource, tableName, ids, isSoftDelete) { 33 | var res = await table.batchDelete(dataSource, tableName, ids, isSoftDelete); 34 | return res.data; 35 | }, 36 | import: async function (dataSource, tableName, fileObj, progressCallback) { 37 | return table.import(dataSource, tableName, fileObj, progressCallback).then((res) => { 38 | console.log(res); 39 | return res.data; 40 | }) 41 | }, 42 | multiImport: async function (dataSource, fileObj, progressCallback) { 43 | return table.multiImport(dataSource, fileObj, progressCallback).then((res) => { 44 | console.log(res); 45 | return res.data; 46 | }) 47 | }, 48 | multiExport: async function(dataSource, ids) { 49 | var res = await table.multiExport(dataSource, ids); 50 | return res.data; 51 | }, 52 | getImportTemplate: async function(dataSource, tableName) { 53 | var res = await table.getImportTemplate(dataSource, tableName); 54 | return res.data; 55 | }, 56 | export: async function(dataSource, tableName, select, search, query, filter) { 57 | var res = await table.export(dataSource, tableName, select, search, query, filter); 58 | return res.data; 59 | }, 60 | 61 | exportXml: async function(dataSource, tableName, select, search, query, filter, isDisplayCaption) { 62 | var res = await table.exportXml(dataSource, tableName, select, search, query, filter, isDisplayCaption); 63 | return res.data; 64 | } 65 | }; 66 | 67 | export { tableService }; 68 | -------------------------------------------------------------------------------- /src/service/userService.js: -------------------------------------------------------------------------------- 1 | import { user} from "../api"; 2 | import { LocalStorage } from "quasar"; 3 | 4 | const userService = { 5 | login: async function(data) { 6 | var res = await user.login(data); 7 | return res.data; 8 | }, 9 | logout: async function() { 10 | var res = await user.logout(); 11 | return res.data; 12 | }, 13 | menu: async function(dataSource) { 14 | var res = await user.menu(dataSource); 15 | return res.data; 16 | }, 17 | table: async function(dataSource) { 18 | var res = await user.table(dataSource); 19 | return res.data; 20 | }, 21 | getUserInfo: async function() { 22 | return LocalStorage.getItem("userInfo") || {}; 23 | }, 24 | setUserInfo: function(userInfo) { 25 | LocalStorage.set("userInfo", userInfo); 26 | } 27 | }; 28 | 29 | export { userService }; 30 | -------------------------------------------------------------------------------- /src/store/config/actions.js: -------------------------------------------------------------------------------- 1 | /* 2 | export function someAction (context) { 3 | } 4 | */ 5 | -------------------------------------------------------------------------------- /src/store/config/getters.js: -------------------------------------------------------------------------------- 1 | /* 2 | export function someGetter (state) { 3 | } 4 | */ 5 | -------------------------------------------------------------------------------- /src/store/config/index.js: -------------------------------------------------------------------------------- 1 | import state from "./state"; 2 | import * as getters from "./getters"; 3 | import * as mutations from "./mutations"; 4 | import * as actions from "./actions"; 5 | 6 | export default { 7 | namespaced: true, 8 | state, 9 | getters, 10 | mutations, 11 | actions 12 | }; 13 | -------------------------------------------------------------------------------- /src/store/config/mutations.js: -------------------------------------------------------------------------------- 1 | export const updateIsAllowBack = (state, isAllowBack) => { 2 | state.isAllowBack = isAllowBack; 3 | }; 4 | -------------------------------------------------------------------------------- /src/store/config/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | isAllowBack: false 3 | }; 4 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | // import example from './module-example' 5 | 6 | import config from "./config"; 7 | import user from "./user"; 8 | 9 | import { plugin as userPlugin } from "./user"; 10 | 11 | 12 | Vue.use(Vuex) 13 | 14 | /* 15 | * If not building with SSR mode, you can 16 | * directly export the Store instantiation; 17 | * 18 | * The function below can be async too; either use 19 | * async/await or return a Promise which resolves 20 | * with the Store instance. 21 | */ 22 | 23 | const Store = new Vuex.Store({ 24 | modules: { 25 | config, 26 | user 27 | }, 28 | 29 | plugins: [userPlugin], 30 | 31 | // enable strict mode (adds overhead!) 32 | // for dev mode only 33 | strict: process.env.DEBUGGING 34 | }); 35 | 36 | export default Store; 37 | -------------------------------------------------------------------------------- /src/store/module-example/actions.js: -------------------------------------------------------------------------------- 1 | export function someAction (/* context */) { 2 | } 3 | -------------------------------------------------------------------------------- /src/store/module-example/getters.js: -------------------------------------------------------------------------------- 1 | export function someGetter (/* state */) { 2 | } 3 | -------------------------------------------------------------------------------- /src/store/module-example/index.js: -------------------------------------------------------------------------------- 1 | import state from './state' 2 | import * as getters from './getters' 3 | import * as mutations from './mutations' 4 | import * as actions from './actions' 5 | 6 | export default { 7 | namespaced: true, 8 | getters, 9 | mutations, 10 | actions, 11 | state 12 | } 13 | -------------------------------------------------------------------------------- /src/store/module-example/mutations.js: -------------------------------------------------------------------------------- 1 | export function someMutation (/* state */) { 2 | } 3 | -------------------------------------------------------------------------------- /src/store/module-example/state.js: -------------------------------------------------------------------------------- 1 | export default function () { 2 | return { 3 | // 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/store/store-flag.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // THIS FEATURE-FLAG FILE IS AUTOGENERATED, 3 | // REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING 4 | import "quasar/dist/types/feature-flag"; 5 | 6 | declare module "quasar/dist/types/feature-flag" { 7 | interface QuasarFeatureFlags { 8 | store: true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/store/user/actions.js: -------------------------------------------------------------------------------- 1 | import { userService } from "../../service"; 2 | import { permissionService } from "../../service"; 3 | 4 | export const login = ({ commit }, userInfo) => { 5 | return new Promise((resolve, reject) => { 6 | userService 7 | .login(userInfo) 8 | .then(data => { 9 | //session方式登录,其实不需要token,这里为了JWT登录预留,用username代替。 10 | //通过Token是否为空判断本地有没有登录过,方便后续处理。 11 | commit("updateToken", data.principal.username); 12 | 13 | const newUserInfo = { 14 | username: data.principal.username, 15 | realname: data.principal.realname || data.principal.username, 16 | avatar: "", 17 | authorities: data.principal.authorities || [], 18 | roles: data.principal.roles || [] 19 | }; 20 | commit("updateUserInfo", newUserInfo); 21 | 22 | let permissions = data.authorities || []; 23 | let isSuperAdmin = false; 24 | if (permissions.findIndex(t => t.authority === "ROLE_SUPER_ADMIN") >= 0) { 25 | isSuperAdmin = true; 26 | } 27 | 28 | permissionService.set({ 29 | permissions: permissions, 30 | isSuperAdmin: isSuperAdmin 31 | }); 32 | 33 | resolve(newUserInfo); 34 | }) 35 | .catch(error => { 36 | reject(error); 37 | }); 38 | }); 39 | }; 40 | 41 | export const logout = ({ commit }) => { 42 | return new Promise((resolve, reject) => { 43 | userService 44 | .logout() 45 | .then(() => { 46 | resolve(); 47 | }) 48 | .catch(error => { 49 | console.error(error); 50 | resolve(); 51 | }) 52 | .finally(() => { 53 | commit("updateToken", ""); 54 | commit("updateUserInfo", { 55 | username: "", 56 | realname: "", 57 | avatar: "", 58 | authorities: [], 59 | roles: [] 60 | }); 61 | 62 | permissionService.set({ 63 | permissions: [], 64 | isSuperAdmin: false 65 | }); 66 | }); 67 | }); 68 | }; 69 | 70 | export const getUserInfo = ({ commit }) => { 71 | return new Promise((resolve, reject) => { 72 | userService 73 | .getUserInfo() 74 | .then(data => { 75 | commit("updateUserInfo", data); 76 | resolve(); 77 | }) 78 | .catch(error => { 79 | reject(error); 80 | }); 81 | }); 82 | }; 83 | -------------------------------------------------------------------------------- /src/store/user/getters.js: -------------------------------------------------------------------------------- 1 | /* 2 | export function someGetter (state) { 3 | } 4 | */ 5 | -------------------------------------------------------------------------------- /src/store/user/index.js: -------------------------------------------------------------------------------- 1 | import state from "./state"; 2 | import * as getters from "./getters"; 3 | import * as mutations from "./mutations"; 4 | import * as actions from "./actions"; 5 | import { plugin } from "./plugin"; 6 | 7 | export { plugin }; 8 | 9 | export default { 10 | namespaced: true, 11 | state, 12 | getters, 13 | mutations, 14 | actions 15 | }; 16 | -------------------------------------------------------------------------------- /src/store/user/mutations.js: -------------------------------------------------------------------------------- 1 | export const updateToken = (state, token) => { 2 | state.token = token; 3 | }; 4 | 5 | export const updateUserInfo = (state, userInfo) => { 6 | state.userInfo = userInfo; 7 | }; 8 | 9 | -------------------------------------------------------------------------------- /src/store/user/plugin.js: -------------------------------------------------------------------------------- 1 | import { authService } from "../../service"; 2 | import { userService } from "../../service"; 3 | 4 | const plugin = store => { 5 | // 当 store 初始化后调用 6 | store.subscribe(mutation => { 7 | // 每次 mutation 之后调用 8 | // mutation 的格式为 { type, payload } 9 | if (mutation.type === "user/updateToken") { 10 | authService.setToken(mutation.payload); 11 | } else if (mutation.type === "user/updateUserInfo") { 12 | userService.setUserInfo(mutation.payload); 13 | } 14 | }); 15 | }; 16 | 17 | export { plugin }; 18 | -------------------------------------------------------------------------------- /src/store/user/state.js: -------------------------------------------------------------------------------- 1 | import { authService } from "../../service"; 2 | 3 | export default { 4 | token: authService.getToken(), 5 | userInfo: { 6 | username: "", 7 | realname: "", 8 | avatar: "", 9 | authorities: [], 10 | roles: "" 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /src/utils/date.js: -------------------------------------------------------------------------------- 1 | import { date as qDate } from "quasar"; 2 | 3 | const DATE_TIME_FORMAT = "YYYY-MM-DD HH:mm:ss"; 4 | const DATE_FORMAT = "YYYY-MM-DD"; 5 | const TIME_FORMAT = "HH:mm:ss"; 6 | 7 | export function covertToQDateTime(date) { 8 | return qDate.formatDate(date, DATE_TIME_FORMAT); 9 | } 10 | 11 | export function covertToQDate(date) { 12 | return qDate.formatDate(date, DATE_FORMAT); 13 | } 14 | 15 | export function covertToQTime(date) { 16 | return qDate.formatDate(date, TIME_FORMAT); 17 | } 18 | 19 | export function getToday() { 20 | return this.covertToQDateTime(Date.now()); 21 | } 22 | 23 | export function dateTimeFormat(value) { 24 | if (!value) { 25 | return ""; 26 | } 27 | 28 | 29 | let formattedString = this.covertToQDateTime(new Date(value)); 30 | 31 | return formattedString; 32 | } 33 | 34 | export function timeFormat(value) { 35 | if (!value) { 36 | return ""; 37 | } 38 | 39 | if ((value + "").indexOf(":") > 0) { 40 | return value; 41 | } 42 | 43 | let formattedString = this.covertToQTime(new Date(value)); 44 | 45 | return formattedString; 46 | } 47 | 48 | export function dateFormat(value) { 49 | if (!value) { 50 | return ""; 51 | } 52 | 53 | let formattedString = this.covertToQDate(new Date(value)); 54 | 55 | return formattedString; 56 | } 57 | 58 | export default { 59 | covertToQDateTime, 60 | covertToQDate, 61 | covertToQTime, 62 | getToday, 63 | dateTimeFormat, 64 | dateFormat, 65 | timeFormat 66 | }; 67 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | import date from "./date.js"; 2 | 3 | export { date }; 4 | --------------------------------------------------------------------------------