diff --git a/index.html b/index.html index 16722a6..85b8fd9 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ /> <%= VITE_GLOB_APP_TITLE %> +
@@ -154,5 +155,7 @@
+ + diff --git a/public/learunui/learunui.css b/public/learunui/learunui.css new file mode 100644 index 0000000..f920f04 --- /dev/null +++ b/public/learunui/learunui.css @@ -0,0 +1 @@ +::-webkit-scrollbar{width:16px;height:16px}::-webkit-scrollbar-thumb{background-color:#c2c2c2;border-radius:8px;min-height:24px;border:4px solid transparent;background-clip:content-box}::-webkit-scrollbar-thumb:hover{background-color:#7e7e7e}::-webkit-scrollbar-corner{background:transparent}body{font-family:-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji;font-variant:tabular-nums;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:tabular-nums;font-variant-east-asian:normal;font-feature-settings:"tnum","tnum"}a{text-decoration:none;color:#333}*{outline:none}.mt-8{margin-top:8px}.mt-16{margin-top:16px}.ml-8{margin-left:8px}.ml-16{margin-left:16px}.l-auto-window,.l-auto-window .el-tabs--top{position:relative;height:100%;width:100%;box-sizing:border-box}.l-auto-window .el-tabs--top{padding-top:40px}.l-auto-window .el-tabs--top .el-tabs__header.is-top{position:absolute;top:0;left:0;width:100%;height:40px;margin:0;z-index:1}.l-auto-window .el-tabs--top .el-tabs__content,.l-auto-window .el-tabs--top .el-tabs__content .el-tab-pane{position:relative;height:100%;width:100%;box-sizing:border-box}.l-auto-window .el-tabs--top .el-tabs__content .el-tab-pane{overflow:hidden auto}.el-form-item__content{font-size:12px}.el-form-item__content .el-rate{padding-top:4px}.el-form-item__content .el-slider__runway{margin-top:11px}.el-form-item__content .el-slider__input{margin-top:0}.el-dropdown-menu__item{font-size:12px!important;line-height:28px!important}.el-menu--display,.el-menu--display+.el-submenu__icon-arrow{display:none}.el-table .el-switch{transform:scale(.8)}.el-tabs__item{outline:none;box-shadow:none!important;border-radius:0!important}.el-form-item__content .el-select{width:100%}.el-tree-node__content:hover{background-color:#edf3fa}.lr-tree-node{font-size:14px;color:rgba(0,0,0,.85)}.lr-tree-node i{font-size:12px;color:#909399;display:inline-block;text-align:left;width:14px;vertical-align:middle;margin-top:-2px}.el-tree-node.is-current>.el-tree-node__content .lr-tree-node,.el-tree-node.is-current>.el-tree-node__content .lr-tree-node i{color:#409eff}.el-tabs__item{vertical-align:middle}.el-cascader,.el-date-editor.el-input,.el-date-editor.el-input__inner,.el-input-number:not(.el-slider__input){width:100%}.el-dialog__wrapper{overflow:hidden}.el-upload--picture-card{line-height:98px}.el-upload--picture-card,.el-upload-list--picture-card .el-upload-list__item{border-radius:4px;width:100px;height:100px}.el-upload-list--picture-card .el-progress{width:84px}.el-upload-list--picture-card .el-progress .el-progress-circle{width:84px!important;height:84px!important}.el-upload-list--picture-card .el-upload-list__item-thumbnail{height:auto!important}.l-panel--tool-right .el-button-group:not(:last-child){margin-right:4px}.danger{color:#f56c6c!important}.el-input__inner,.el-textarea__inner{padding-left:8px}.l-tabs-container{padding-top:11px}.only-tabs .el-tabs__header{display:none}.only-tabs .el-tabs--top,.only-tabs .l-tabs-container{padding-top:0!important}.el-collapse{border:0}.el-collapse-item__header{padding-left:16px}.el-avatar{vertical-align:middle}.el-form--inline .el-date-editor--daterange{width:240px!important}.el-form-item--mini .el-form-item__content .el-checkbox-group{height:28px}.el-autocomplete{width:100%}.el-color-picker__mask{background-color:transparent!important}.el-timeline .el-card__body{padding:8px}.el-input.is-disabled .el-input__inner{background-color:#fff!important;border-color:#dcdfe6!important;color:#606266!important}.el-radio-button--mini .el-radio-button__inner{padding:6px 15px}.el-card{margin-bottom:18px}.el-card__body{padding:16px}.el-card__header{padding:8px 16px}.l-from-body{position:relative;height:100%;width:100%;box-sizing:border-box;padding:24px 24px 16px 0;overflow:hidden auto}.l-from-body .el-input-number,.l-from-body .el-select{width:100%}.l-rblock,.l-tab-page{position:relative;height:100%;width:100%;box-sizing:border-box}.l-tab-page{background-color:#f1f2f5;overflow:hidden auto}.l-title{height:40px;width:100%;line-height:39px;color:#606266;font-size:14px;padding-left:8px}.l-fullscreen-dialog{position:relative;height:100%;width:100%;box-sizing:border-box}.l-fullscreen-dialog .el-dialog__header{display:none}.l-fullscreen-dialog .el-dialog__body{position:relative;height:100%;width:100%;padding:0}.l-from-table-body{position:relative;box-sizing:border-box;padding:8px 0 8px 0;overflow:hidden auto}.l-from-table-body,.l-iframe{height:100%;width:100%}.l-form-panel,.l-page-panel{box-sizing:border-box;position:relative;height:100%;width:100%;max-width:794px;overflow:hidden auto;background-color:#fff;border-radius:4px;margin:auto;padding:24px}.s-w-72 .el-input{width:72px}.s-w-80 .el-input{width:80px}.s-w-88 .el-input{width:88px}.s-w-96 .el-input{width:96px}.l-fleft-block{position:relative;float:left;box-sizing:border-box}.tree-setting-btn{position:absolute;top:0;right:0;height:100%;text-align:center;width:32px;font-size:16px;cursor:pointer}.l-bottom{position:absolute;height:33px;width:100%;bottom:0;left:0;padding:0 4px;box-sizing:border-box}.l-info-message .ok{color:#1bb99a}.l-info-message .error{color:#ff3015}.l-set-item{box-sizing:border-box;position:relative;width:100%;height:34px;padding:2px 0;padding-left:22px;padding-right:30px;border:1px dashed transparent}.l-set-item__Num{padding-left:52px}.l-set-item .el-tag{box-sizing:border-box;position:absolute;top:2px;left:22px}.l-set-item .l-drag-item{top:8px;left:2px;font-size:16px;cursor:move}.l-set-item .l-delete-item,.l-set-item .l-drag-item{display:block;box-sizing:border-box;position:absolute}.l-set-item .l-delete-item{top:5px;right:2px}.l-color-block{height:28px;width:28px;margin:auto}.l-BMap-top{position:absolute;top:0;left:0;height:40px;width:100%;border-bottom:1px solid #d7dae2;box-sizing:border-box;padding:5px 8px}.l-BMap-autoAddress .el-icon-search{margin-top:5px}.l-BMap-autoAddress .fl{float:left}.l-BMap-autoAddress .mgr10{margin-right:8px}.l-BMap-autoAddress .title{text-overflow:ellipsis;overflow:hidden;line-height:14px;margin-top:4px}.l-BMap-autoAddress .address{line-height:12px;font-size:12px;color:#b4b4b4;margin-top:8px;margin-bottom:12px}.l-checkbox,.l-code-mirror{position:relative;height:100%;width:100%}.l-code-mirror{box-sizing:border-box}.l-code-mirror .CodeMirror{height:100%;width:100%}.l-data-board{background-color:#fff;min-height:64px;min-width:64px;cursor:pointer}.l-data-board__icon{position:absolute;top:50%;margin-top:-32px;left:16px;height:64px;width:64px;border-radius:50%;text-align:center;line-height:64px;font-size:36px}.l-data-board__text{position:absolute;top:50%;margin-top:-23px;left:96px}.l-data-board__num{font-size:20px;font-weight:600}.l-data-board__title{margin-top:4px;font-size:14px;color:#666}.l-data-board .theme1{background-color:#e1f3d7;color:#67c23a}.l-data-board .theme2{background-color:#ecf5ff;color:#409eff}.l-data-board .theme3{background-color:#faecd8;color:#e6a23c}.l-data-board .theme4{background-color:#fde2e2;color:#f56c6c}.l-data-list{padding:0 8px;overflow:hidden auto}.l-data-list--line{width:100%;line-height:32px;border-bottom:1px solid #f0f0f0;cursor:pointer;color:rgba(0,0,0,.85);font-size:14px;overflow:hidden}.l-data-list--item,.l-data-list--line{position:relative;box-sizing:border-box;height:32px}.l-data-list--item{float:left;width:25%}.l-data-list .l-empty{display:flex;align-items:center;justify-content:center;font-size:14px;color:#909399}.l-data-panel{padding-top:40px;background-color:#fff}.l-data-panel--title{box-sizing:border-box;position:absolute;top:0;left:0;height:40px;width:100%;line-height:39px;color:rgba(0,0,0,.85);font-weight:500;font-size:14px;padding-left:16px}.l-data-panel--title:after{content:"";display:block;position:absolute;box-sizing:border-box;height:1px;bottom:0;left:0;right:0;background-color:#f0f0f0}.l-data-panel .el-button--text{position:absolute;top:0;right:16px}.l-data-panel-app{border-radius:8px}.l-data-panel-app .l-data-panel--title:after{left:16px;right:16px}.l-data-panel-app .el-button--text{color:#ccc}.l-dialog-dark .el-dialog__header,.l-dialog .el-dialog__header{position:relative;box-sizing:border-box;height:40px;width:100%;border-radius:2px 2px 0 0;border-bottom:2px solid #409eff;padding:0}.l-dialog-dark .el-dialog__title,.l-dialog .el-dialog__title{display:block;position:absolute;left:0;top:0;height:100%;line-height:38px;padding-left:16px;color:#606266;font-size:14px}.l-dialog-dark .el-dialog__headerbtn,.l-dialog .el-dialog__headerbtn{top:11px;right:11px}.l-dialog-dark .el-dialog__body,.l-dialog .el-dialog__body{padding:0;min-height:64px;width:100%}.l-dialog-dark-window,.l-dialog-window{position:relative;width:100%;box-sizing:border-box}.l-dialog-dark-window-hasBtns,.l-dialog-window-hasBtns{padding-bottom:48px}.l-dialog-btns,.l-dialog-dark-btns{box-sizing:border-box;position:absolute;bottom:0;left:0;height:48px;line-height:48px;width:100%;text-align:right;padding-right:10px}.l-dialog-dark .el-steps,.l-dialog .el-steps{position:absolute;top:0;left:0;box-sizing:border-box;width:100%;height:46px;overflow:hidden}.l-dialog-dark-window-hasSteps,.l-dialog-window-hasSteps{padding-top:46px}.l-dialog-dark{background-color:#1b1e25}.l-dialog-dark .el-dialog__title{color:#eee}.l-dialog-dark .el-button{background-color:transparent!important}.l-dialog-dark .el-button--default{color:#fff!important;border-color:#fff!important}.l-dialog-dark .el-button--primary{color:#409eff!important;border-color:#409eff!important}.l-dialog-dark .el-button:hover{opacity:.8}.l-draggable{padding:10px;position:absolute;cursor:move;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.l-draggable__mask{width:100%;height:100%;border:0;position:absolute;top:0;right:0;bottom:0;left:0;z-index:1}.l-draggable--active{cursor:move;border:1px dashed #09f;background-color:rgba(115,170,229,.5)}.l-draggable--move{opacity:.6;background-color:rgba(115,170,229,.5)}.l-draggable--click{cursor:pointer}.l-draggable__line--left{position:absolute;border-top:1px dashed #09f;width:10000px;height:0;top:0;transform:translateX(-100%)}.l-draggable__line--top{position:absolute;border-left:1px dashed #09f;width:0;height:10000px;left:0;transform:translateY(-100%)}.l-draggable__line--label{top:-5px;left:-8px;position:absolute;padding:5px;transform:translate(-100%,-100%);color:#09f;font-size:18px;white-space:nowrap;cursor:move}.l-draggable__menu{position:absolute;top:0;left:0;background-color:#409eff;font-size:25px;color:#fff;z-index:9999;cursor:pointer}.l-draggable__range{position:absolute;width:10px;height:10px;border-radius:100%;z-index:9999;background-color:#09f}.l-draggable__range--left,.l-draggable__range--right{top:50%;transform:translateY(-50%)}.l-draggable__range--left:hover,.l-draggable__range--right:hover{cursor:ew-resize}.l-draggable__range--left{left:-6px}.l-draggable__range--right{right:-6px}.l-draggable__range--bottom,.l-draggable__range--top{left:50%;transform:translateX(-50%)}.l-draggable__range--bottom:hover,.l-draggable__range--top:hover{cursor:ns-resize}.l-draggable__range--top{top:-6px}.l-draggable__range--bottom{bottom:-6px}.l-draggable__range--bottom-right:hover,.l-draggable__range--top-left:hover{cursor:nwse-resize}.l-draggable__range--bottom-left:hover,.l-draggable__range--top-right:hover{cursor:nesw-resize}.l-draggable__range--top-right{top:-6px;right:-6px}.l-draggable__range--top-left{top:-6px;left:-6px}.l-draggable__range--bottom-right{bottom:-6px;right:-6px}.l-draggable__range--bottom-left{bottom:-6px;left:-6px}.el-drawer:focus{outline:none}.l-drawer--warpper{position:relative;height:100%;width:100%;box-sizing:border-box;padding-top:40px}.l-drawer--header{position:absolute;height:40px;width:100%;border-bottom:2px solid #409eff;box-sizing:border-box;left:0;top:0}.l-drawer--title{position:absolute;left:0;top:0;height:100%;line-height:38px;padding-left:16px;color:#606266;font-size:14px}.l-drawer--btns{text-align:right;padding-top:2px;padding-right:5px}.l-drawer--body,.l-drawer--btns{position:relative;height:100%;width:100%;box-sizing:border-box}.l-drawer .el-drawer__body{position:relative;height:100%}.l-drawer-bottom-btns{box-sizing:border-box;position:absolute;bottom:0;left:0;height:48px;line-height:47px;width:100%;text-align:right;padding-right:10px;border-top:1px solid #d7dae2}.l-edit-table{position:relative}.l-edit-table .el-table thead th,.l-edit-table .el-table thead tr{background-color:#f5f7fa;font-weight:500}.l-edit-table .el-table__fixed-right:before,.l-edit-table .el-table__fixed:before{height:0}.l-edit-table .el-table .cell{color:#181d1f}.l-edit-table--addbtn{margin-top:8px;border:1px dashed #f0f0f0;text-align:center;cursor:pointer}.l-edit-table--addbtn:hover{border-color:#66b1ff}.l-edit-table .el-checkbox-group{height:23px}.l-fullscreen-dialog .el-dialog__body{box-sizing:border-box}.l-fullscreen-dialog--header{box-sizing:border-box;position:absolute;top:0;left:0;height:57px;width:100%;border-bottom:1px solid #f0f0f0}.l-fullscreen-dialog--title{box-sizing:border-box;position:absolute;top:0;left:16px;height:100%;display:flex;align-items:center}.l-fullscreen-dialog--logo{height:18px;align-items:center}.l-fullscreen-dialog--title-text{margin:0;font-size:18px;margin-left:8px}.l-fullscreen-dialog--body{box-sizing:border-box;position:relative;height:100%;width:100%;background:#f0f2f5}.l-fullscreen-dialog--mid{position:relative;margin:auto;height:100%;width:480px;text-align:center}.l-fullscreen-dialog--mid .el-steps--simple{background:none;padding:18px 8%}.l-fullscreen-dialog--right{box-sizing:border-box;position:absolute;height:100%;line-height:56px;top:0;right:0;padding-right:14px}.l-input-color .el-color-picker{height:100%;display:flex}.l-input-color .el-color-picker__trigger{align-items:center}.l-input-color .el-input--small .el-color-picker--mini .el-color-picker__trigger{height:24px;width:24px}.l-input-color .el-input--mini .el-color-picker--mini .el-color-picker__trigger{height:23px;width:23px;border:0;padding:2px}.l-input-icon,.l-input-icon .el-input__inner{cursor:pointer}.l-input-icon-item{position:relative;float:left;height:64px;width:64px;text-align:center}.l-input-icon-item .el-button{padding:0;height:56px;width:56px;text-align:center;line-height:56px;font-size:16px}.l-input-icon-item .el-button:hover{font-size:48px}.l-layout,.l-layout--container,.l-layout--wrapper{position:relative;height:100%;width:100%;overflow:hidden;box-sizing:border-box;transition:all .3s}.l-layout--bottom,.l-layout--left,.l-layout--right,.l-layout--top{position:absolute;overflow:hidden;box-sizing:border-box;transition:all .3s}.l-layout--bottom,.l-layout--top{left:0;height:0;width:100%;max-height:100%}.l-layout--left,.l-layout--right{top:0;height:100%;width:0;max-width:100%}.l-layout--move{position:absolute;box-sizing:border-box}.l-layout--top{top:0;padding-bottom:4px}.l-layout--top .l-layout--move{bottom:0;left:0;height:4px;width:100%;cursor:row-resize}.l-layout--bottom{bottom:0;padding-top:4px}.l-layout--bottom .l-layout--move{top:0;left:0;height:4px;width:100%;cursor:row-resize}.l-layout--left{left:0;padding-right:4px}.l-layout--left .l-layout--move{top:0;right:0;width:4px;height:100%;cursor:col-resize}.l-layout--right{right:0;padding-left:4px}.l-layout--right .l-layout--move{top:0;left:0;width:4px;height:100%;cursor:col-resize}.l-panel{padding:8px}.l-panel,.l-panel--warpper{position:relative;box-sizing:border-box;height:100%;width:100%}.l-panel--warpper{background-color:#fff;border-radius:2px}.l-panel--body{position:relative;box-sizing:border-box;height:100%;width:100%;overflow:hidden auto}.l-panel--body-mask{position:absolute;height:100%;width:100%;z-index:100}.l-panel--title{line-height:39px;color:#606266;font-size:14px;padding-left:8px}.l-panel--title,.l-panel--tool{box-sizing:border-box;position:absolute;top:0;left:0;height:40px;width:100%;border-bottom:1px solid #d7dae2}.l-panel--tool{display:flex;justify-content:space-between}.l-panel--tool-right{padding-right:6px;flex-grow:1;display:flex;justify-content:flex-end;align-items:center}.l-panel--tool-left,.l-panel--tool-right{position:relative;box-sizing:border-box;height:100%}.l-panel--tool-left{padding-left:6px;flex-grow:10000}.l-panel--item{display:inline-flex;height:100%;box-sizing:border-box;margin-right:4px;vertical-align:middle;align-items:center}.l-panel--title-left{position:absolute;top:0;right:8px;height:100%;text-align:center}.l-query{position:relative;height:44px;width:100%;background-color:#fff;padding:8px;box-sizing:border-box;overflow:hidden;transition:all .3s}.l-query-btns{position:absolute;right:8px;bottom:8px}.l-query2{position:relative;height:40px;padding:6px 0 0 6px;box-sizing:border-box;overflow:hidden;transition:all .3s}.l-query2 .el-form-item__content{height:28px}.l-query-popper{overflow:auto;padding-top:16px;padding-right:16px;box-sizing:border-box}.l-query-popper,.l-radio{position:relative;height:100%;width:100%}.l-select{width:100%}.l-select-panel{position:relative;height:100%;width:100%;box-sizing:border-box;background-color:#f1f2f5}.l-select-panel--numText{font-size:12px;color:#409eff}.l-spinner,.l-table{width:100%}.l-table{position:relative;box-sizing:border-box}.l-table .el-table__expanded-cell[class*=cell]{padding:0;padding-left:27px;padding-bottom:8px;padding-right:8px}.l-table--pagination{position:absolute;bottom:0;left:0;height:33px;width:100%;border-top:1px solid #dde2ea;box-sizing:border-box;padding-top:3px;text-align:right}.l-table--pagination .el-input--mini .el-input__inner{height:22px;line-height:22px}.l-table--pagination .el-input--mini .el-input__icon{line-height:22px}.l-table--haspagination{padding-bottom:33px}.l-table .el-table--striped .el-table__body tr.el-table__row--striped td{background:#f2f5fa}.l-table .el-table td,.l-table .el-table th.is-leaf{border-bottom:1px solid #dde2ea}.l-table .el-table__footer td{border-bottom:none}.l-table .el-table .cell{color:#181d1f}.l-table .el-table__body tr.hover-row.current-row>td,.l-table .el-table__body tr.hover-row.el-table__row--striped.current-row>td,.l-table .el-table__body tr.hover-row.el-table__row--striped>td,.l-table .el-table__body tr.hover-row>td{background-color:#edf3fa}.l-table .el-table__fixed-right:before,.l-table .el-table__fixed:before{height:0}.l-table .el-table--border{border:none}.l-table .el-table--border:after,.l-table .el-table--group:after,.l-table .el-table:before{height:0}.l-table .el-table thead th,.l-table .el-table thead tr{background-color:#f5f7fa}.l-table .el-table--border th.gutter:last-of-type{border-bottom:0;border-bottom-width:0}.el-table__expanded-cell>.l-table{border-left:1px solid #dde2ea}.el-table__expand-column .cell{padding:0!important}.l-time.el-range-editor.el-input,.l-time.el-range-editor.el-input__inner{width:100%;padding-top:2px;box-sizing:border-box}.l-tree-select{width:100%}.l-tree-select-popper .el-scrollbar{border-radius:4px}.l-tree-select-popper .el-select-dropdown__list{padding:0}.l-tree-select-popper .el-select-dropdown__item{height:auto;padding:0}.l-tree-select-popper .el-select-dropdown__item.selected{font-weight:400}.l-tree-select-popper .l-tree-node{font-size:14px;color:#181d1f}.l-tree-select-popper .l-tree-node .fa{font-size:12px;color:#909399;display:inline-block;text-align:left;width:14px;vertical-align:middle;margin-top:-2px}.l-tree-select-popper .el-tree-node.is-current>.el-tree-node__content{background-color:#f0f7ff}.l-tree-select-popper .el-tree-node.is-current>.el-tree-node__content .l-tree-node,.l-tree-select-popper .el-tree-node.is-current>.el-tree-node__content .l-tree-node .fa{color:#409eff}.l-upload.readonly .el-upload,.l-upload.readonly .el-upload--picture-card{display:none}.l-value-to-label{min-height:24px} \ No newline at end of file diff --git a/src/api/formdesign/index.ts b/src/api/formdesign/index.ts new file mode 100644 index 0000000..1dc28ca --- /dev/null +++ b/src/api/formdesign/index.ts @@ -0,0 +1,24 @@ +import { defHttp } from '@/utils/http/lraxios'; +import { DemoOptionsItem, selectParams, AccountListGetResultModel, AccountParams } from './model/index'; + +enum Api { + OPTIONS_LIST = '/data/dataitem/details/FormSort', + FORMS_LIST = '/custmerform/scheme/page', + DATABASE_LIST = '/data/codetable/page', + OUTKEY_LIST = '/data/codetables/lrsystemdb' +} + +/** + * @description: Get sample options value + */ +export const optionsListApi = (params?: selectParams) => + defHttp.get({ url: Api.OPTIONS_LIST, params }); + +export const getFormGroupList = (params: AccountParams) => + defHttp.get({ url: Api.FORMS_LIST, params }); + +export const getDataBaseTableList = (params: AccountParams) => + defHttp.get({ url: Api.DATABASE_LIST, params }); + +export const getOutKeyList = (params: AccountParams) => + defHttp.get({ url: Api.OUTKEY_LIST, params }); diff --git a/src/api/formdesign/model/index.ts b/src/api/formdesign/model/index.ts new file mode 100644 index 0000000..d8fa2ef --- /dev/null +++ b/src/api/formdesign/model/index.ts @@ -0,0 +1,33 @@ +import { BasicPageParams, BasicFetchResult } from '@/api/model/baseModel'; + +export interface DemoOptionsItem { + name: string; + id: string; +} + +export interface selectParams { + id: number | string; +} + +export type AccountParams = BasicPageParams & { + account?: string; + nickname?: string; + [key: string]: any; +}; + +export interface AccountListItem { + id: string; + account: string; + email: string; + nickname: string; + role: number; + createTime: string; + remark: string; + status: number; +} +/** + * @description: Request list return value + */ +export type DemoOptionsGetResultModel = BasicFetchResult; + +export type AccountListGetResultModel = BasicFetchResult; diff --git a/src/components/lrLayout/index.js b/src/components/lrLayout/index.js new file mode 100644 index 0000000..0d96de4 --- /dev/null +++ b/src/components/lrLayout/index.js @@ -0,0 +1,7 @@ +import lrLayout from './src/lrLayout.vue' + +lrLayout.install = function(Vue) { + Vue.component(lrLayout.name, lrLayout) +} + +export default lrLayout \ No newline at end of file diff --git a/src/components/lrLayout/src/lrLayout.vue b/src/components/lrLayout/src/lrLayout.vue new file mode 100644 index 0000000..c9afda4 --- /dev/null +++ b/src/components/lrLayout/src/lrLayout.vue @@ -0,0 +1,208 @@ + + + diff --git a/src/components/lrPanel/index.js b/src/components/lrPanel/index.js new file mode 100644 index 0000000..2db1350 --- /dev/null +++ b/src/components/lrPanel/index.js @@ -0,0 +1,7 @@ +import lrPanel from './src/lrPanel.vue' + +lrPanel.install = function(Vue) { + Vue.component(lrPanel.name, lrPanel) +} + +export default lrPanel \ No newline at end of file diff --git a/src/components/lrPanel/src/lrPanel.vue b/src/components/lrPanel/src/lrPanel.vue new file mode 100644 index 0000000..444c57c --- /dev/null +++ b/src/components/lrPanel/src/lrPanel.vue @@ -0,0 +1,65 @@ + + + diff --git a/src/hooks/setting/index.ts b/src/hooks/setting/index.ts index 59326f3..b6f1eb8 100644 --- a/src/hooks/setting/index.ts +++ b/src/hooks/setting/index.ts @@ -3,13 +3,14 @@ import type { GlobConfig } from '#/config'; import { getAppEnvConfig } from '@/utils/env'; export const useGlobSetting = (): Readonly => { - const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL } = + const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL,VITE_LR_API_URL } = getAppEnvConfig(); // Take global configuration const glob: Readonly = { title: VITE_GLOB_APP_TITLE, apiUrl: VITE_GLOB_API_URL, + lrApi: VITE_LR_API_URL, shortName: VITE_GLOB_APP_TITLE.replace(/\s/g, '_').replace(/-/g, '_'), urlPrefix: VITE_GLOB_API_URL_PREFIX, uploadUrl: VITE_GLOB_UPLOAD_URL, diff --git a/src/router/menus/index.ts b/src/router/menus/index.ts index 0e1ee2f..7ccdec9 100644 --- a/src/router/menus/index.ts +++ b/src/router/menus/index.ts @@ -68,7 +68,6 @@ async function getAsyncMenus() { } if (isRouteMappingMode()) { // 333333 - console.log(permissionStore.getFrontMenuList) return menuFilter(permissionStore.getFrontMenuList); } return staticMenus; diff --git a/src/utils/env.ts b/src/utils/env.ts index 43c5aa3..7ed17b4 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -32,12 +32,14 @@ export function getAppEnvConfig() { : (window[ENV_NAME] as unknown as GlobEnvConfig); const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL } = ENV; let { VITE_GLOB_API_URL } = ENV; + let { VITE_LR_API_URL } = ENV; if (localStorage.getItem(API_ADDRESS)) { const address = JSON.parse(localStorage.getItem(API_ADDRESS) || '{}'); if (address?.key) VITE_GLOB_API_URL = address?.val; } return { VITE_GLOB_APP_TITLE, + VITE_LR_API_URL, VITE_GLOB_API_URL, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_UPLOAD_URL, diff --git a/src/utils/http/axios/index.ts b/src/utils/http/axios/index.ts index a757c59..415f3a4 100644 --- a/src/utils/http/axios/index.ts +++ b/src/utils/http/axios/index.ts @@ -23,7 +23,7 @@ import axios from 'axios'; const globSetting = useGlobSetting(); const urlPrefix = globSetting.urlPrefix; const { createMessage, createErrorModal, createSuccessModal } = useMessage(); - +console.log('globSetting',globSetting) /** * @description: 数据处理,方便区分多种处理方式 */ @@ -52,7 +52,6 @@ const transform: AxiosTransform = { } // 这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式 const { code, result, message } = data; - // 这里逻辑可以根据项目进行修改 const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS; if (hasSuccess) { diff --git a/src/utils/http/lraxios/Axios.ts b/src/utils/http/lraxios/Axios.ts new file mode 100644 index 0000000..17ca92f --- /dev/null +++ b/src/utils/http/lraxios/Axios.ts @@ -0,0 +1,251 @@ +import type { + AxiosRequestConfig, + AxiosInstance, + AxiosResponse, + AxiosError, + InternalAxiosRequestConfig, +} from 'axios'; +import type { RequestOptions, Result, UploadFileParams } from '#/axios'; +import type { CreateAxiosOptions } from './axiosTransform'; +import axios from 'axios'; +import qs from 'qs'; +import { AxiosCanceler } from './axiosCancel'; +import { isFunction } from '@/utils/is'; +import { cloneDeep } from 'lodash-es'; +import { ContentTypeEnum, RequestEnum } from '@/enums/httpEnum'; + +export * from './axiosTransform'; + +/** + * @description: axios module + */ +export class VAxios { + private axiosInstance: AxiosInstance; + private readonly options: CreateAxiosOptions; + + constructor(options: CreateAxiosOptions) { + this.options = options; + this.axiosInstance = axios.create(options); + this.setupInterceptors(); + } + + /** + * @description: Create axios instance + */ + private createAxios(config: CreateAxiosOptions): void { + this.axiosInstance = axios.create(config); + } + + private getTransform() { + const { transform } = this.options; + return transform; + } + + getAxios(): AxiosInstance { + return this.axiosInstance; + } + + /** + * @description: Reconfigure axios + */ + configAxios(config: CreateAxiosOptions) { + if (!this.axiosInstance) { + return; + } + this.createAxios(config); + } + + /** + * @description: Set general header + */ + setHeader(headers: any): void { + if (!this.axiosInstance) { + return; + } + Object.assign(this.axiosInstance.defaults.headers, headers); + } + + /** + * @description: Interceptor configuration 拦截器配置 + */ + private setupInterceptors() { + // const transform = this.getTransform(); + const { + axiosInstance, + options: { transform }, + } = this; + if (!transform) { + return; + } + const { + requestInterceptors, + requestInterceptorsCatch, + responseInterceptors, + responseInterceptorsCatch, + } = transform; + + const axiosCanceler = new AxiosCanceler(); + + // Request interceptor configuration processing + this.axiosInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => { + // If cancel repeat request is turned on, then cancel repeat request is prohibited + const requestOptions = + (config as unknown as any).requestOptions ?? this.options.requestOptions; + const ignoreCancelToken = requestOptions?.ignoreCancelToken ?? true; + + !ignoreCancelToken && axiosCanceler.addPending(config); + + if (requestInterceptors && isFunction(requestInterceptors)) { + config = requestInterceptors(config, this.options); + } + return config; + }, undefined); + + // Request interceptor error capture + requestInterceptorsCatch && + isFunction(requestInterceptorsCatch) && + this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch); + + // Response result interceptor processing + this.axiosInstance.interceptors.response.use((res: AxiosResponse) => { + res && axiosCanceler.removePending(res.config); + if (responseInterceptors && isFunction(responseInterceptors)) { + res = responseInterceptors(res); + } + return res; + }, undefined); + + // Response result interceptor error capture + responseInterceptorsCatch && + isFunction(responseInterceptorsCatch) && + this.axiosInstance.interceptors.response.use(undefined, (error) => { + return responseInterceptorsCatch(axiosInstance, error); + }); + } + + /** + * @description: File Upload + */ + uploadFile(config: AxiosRequestConfig, params: UploadFileParams) { + const formData = new window.FormData(); + const customFilename = params.name || 'file'; + + if (params.filename) { + formData.append(customFilename, params.file, params.filename); + } else { + formData.append(customFilename, params.file); + } + + if (params.data) { + Object.keys(params.data).forEach((key) => { + const value = params.data![key]; + if (Array.isArray(value)) { + value.forEach((item) => { + formData.append(`${key}[]`, item); + }); + return; + } + + formData.append(key, params.data![key]); + }); + } + return this.axiosInstance.request({ + ...config, + method: 'POST', + data: formData, + headers: { + 'Content-type': ContentTypeEnum.FORM_DATA, + // @ts-ignore + ignoreCancelToken: true, + }, + }); + } + + // support form-data + supportFormData(config: AxiosRequestConfig) { + const headers = config.headers || this.options.headers; + const contentType = headers?.['Content-Type'] || headers?.['content-type']; + + if ( + contentType !== ContentTypeEnum.FORM_URLENCODED || + !Reflect.has(config, 'data') || + config.method?.toUpperCase() === RequestEnum.GET + ) { + return config; + } + + return { + ...config, + data: qs.stringify(config.data, { arrayFormat: 'brackets' }), + }; + } + + get(config: AxiosRequestConfig, options?: RequestOptions): Promise { + return this.request({ ...config, method: 'GET' }, options); + } + + post(config: AxiosRequestConfig, options?: RequestOptions): Promise { + return this.request({ ...config, method: 'POST' }, options); + } + + put(config: AxiosRequestConfig, options?: RequestOptions): Promise { + return this.request({ ...config, method: 'PUT' }, options); + } + + delete(config: AxiosRequestConfig, options?: RequestOptions): Promise { + return this.request({ ...config, method: 'DELETE' }, options); + } + + request(config: AxiosRequestConfig, options?: RequestOptions): Promise { + let conf: CreateAxiosOptions = cloneDeep(config); + // cancelToken 如果被深拷贝,会导致最外层无法使用cancel方法来取消请求 + if (config.cancelToken) { + conf.cancelToken = config.cancelToken; + } + + if (config.signal) { + conf.signal = config.signal; + } + + const transform = this.getTransform(); + + const { requestOptions } = this.options; + + const opt: RequestOptions = Object.assign({}, requestOptions, options); + + const { beforeRequestHook, requestCatchHook, transformResponseHook } = transform || {}; + if (beforeRequestHook && isFunction(beforeRequestHook)) { + conf = beforeRequestHook(conf, opt); + } + conf.requestOptions = opt; + + conf = this.supportFormData(conf); + + return new Promise((resolve, reject) => { + this.axiosInstance + .request>(conf) + .then((res: AxiosResponse) => { + if (transformResponseHook && isFunction(transformResponseHook)) { + try { + const ret = transformResponseHook(res, opt); + resolve(ret); + } catch (err) { + reject(err || new Error('request error!')); + } + return; + } + resolve(res as unknown as Promise); + }) + .catch((e: Error | AxiosError) => { + if (requestCatchHook && isFunction(requestCatchHook)) { + reject(requestCatchHook(e, opt)); + return; + } + if (axios.isAxiosError(e)) { + // rewrite error message from axios in here + } + reject(e); + }); + }); + } +} diff --git a/src/utils/http/lraxios/axiosCancel.ts b/src/utils/http/lraxios/axiosCancel.ts new file mode 100644 index 0000000..f115ccf --- /dev/null +++ b/src/utils/http/lraxios/axiosCancel.ts @@ -0,0 +1,60 @@ +import type { AxiosRequestConfig } from 'axios'; + +// 用于存储每个请求的标识和取消函数 +const pendingMap = new Map(); + +const getPendingUrl = (config: AxiosRequestConfig): string => { + return [config.method, config.url].join('&'); +}; + +export class AxiosCanceler { + /** + * 添加请求 + * @param config 请求配置 + */ + public addPending(config: AxiosRequestConfig): void { + this.removePending(config); + const url = getPendingUrl(config); + const controller = new AbortController(); + config.signal = config.signal || controller.signal; + if (!pendingMap.has(url)) { + // 如果当前请求不在等待中,将其添加到等待中 + pendingMap.set(url, controller); + } + } + + /** + * 清除所有等待中的请求 + */ + public removeAllPending(): void { + pendingMap.forEach((abortController) => { + if (abortController) { + abortController.abort(); + } + }); + this.reset(); + } + + /** + * 移除请求 + * @param config 请求配置 + */ + public removePending(config: AxiosRequestConfig): void { + const url = getPendingUrl(config); + if (pendingMap.has(url)) { + // 如果当前请求在等待中,取消它并将其从等待中移除 + const abortController = pendingMap.get(url); + if (abortController) { + abortController.abort(url); + } + pendingMap.delete(url); + } + } + + /** + * 重置 + */ + public reset(): void { + pendingMap.clear(); + } +} diff --git a/src/utils/http/lraxios/axiosRetry.ts b/src/utils/http/lraxios/axiosRetry.ts new file mode 100644 index 0000000..bf44cf7 --- /dev/null +++ b/src/utils/http/lraxios/axiosRetry.ts @@ -0,0 +1,30 @@ +import { AxiosError, AxiosInstance } from 'axios'; +/** + * 请求重试机制 + */ + +export class AxiosRetry { + /** + * 重试 + */ + retry(axiosInstance: AxiosInstance, error: AxiosError) { + // @ts-ignore + const { config } = error.response; + const { waitTime, count } = config?.requestOptions?.retryRequest ?? {}; + config.__retryCount = config.__retryCount || 0; + if (config.__retryCount >= count) { + return Promise.reject(error); + } + config.__retryCount += 1; + //请求返回后config的header不正确造成重试请求失败,删除返回headers采用默认headers + delete config.headers; + return this.delay(waitTime).then(() => axiosInstance(config)); + } + + /** + * 延迟 + */ + private delay(waitTime: number) { + return new Promise((resolve) => setTimeout(resolve, waitTime)); + } +} diff --git a/src/utils/http/lraxios/axiosTransform.ts b/src/utils/http/lraxios/axiosTransform.ts new file mode 100644 index 0000000..5570bf0 --- /dev/null +++ b/src/utils/http/lraxios/axiosTransform.ts @@ -0,0 +1,57 @@ +/** + * Data processing class, can be configured according to the project + */ +import type { + AxiosInstance, + AxiosRequestConfig, + AxiosResponse, + InternalAxiosRequestConfig, +} from 'axios'; +import type { RequestOptions, Result } from '#/axios'; + +export interface CreateAxiosOptions extends AxiosRequestConfig { + authenticationScheme?: string; + transform?: AxiosTransform; + requestOptions?: RequestOptions; +} + +export abstract class AxiosTransform { + /** + * A function that is called before a request is sent. It can modify the request configuration as needed. + * 在发送请求之前调用的函数。它可以根据需要修改请求配置。 + */ + beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig; + + /** + * @description: 处理响应数据 + */ + transformResponseHook?: (res: AxiosResponse, options: RequestOptions) => any; + + /** + * @description: 请求失败处理 + */ + requestCatchHook?: (e: Error, options: RequestOptions) => Promise; + + /** + * @description: 请求之前的拦截器 + */ + requestInterceptors?: ( + config: InternalAxiosRequestConfig, + options: CreateAxiosOptions, + ) => InternalAxiosRequestConfig; + + /** + * @description: 请求之后的拦截器 + */ + responseInterceptors?: (res: AxiosResponse) => AxiosResponse; + + /** + * @description: 请求之前的拦截器错误处理 + */ + requestInterceptorsCatch?: (error: Error) => void; + + /** + * @description: 请求之后的拦截器错误处理 + */ + responseInterceptorsCatch?: (axiosInstance: AxiosInstance, error: Error) => void; +} diff --git a/src/utils/http/lraxios/checkStatus.ts b/src/utils/http/lraxios/checkStatus.ts new file mode 100644 index 0000000..a292933 --- /dev/null +++ b/src/utils/http/lraxios/checkStatus.ts @@ -0,0 +1,80 @@ +import type { ErrorMessageMode } from '#/axios'; +import { useMessage } from '@/hooks/web/useMessage'; +import { useI18n } from '@/hooks/web/useI18n'; +// import router from '@/router'; +// import { PageEnum } from '@/enums/pageEnum'; +import { useUserStoreWithOut } from '@/store/modules/user'; +import projectSetting from '@/settings/projectSetting'; +import { SessionTimeoutProcessingEnum } from '@/enums/appEnum'; + +const { createMessage, createErrorModal } = useMessage(); +const error = createMessage.error!; +const stp = projectSetting.sessionTimeoutProcessing; + +export function checkStatus( + status: number, + msg: string, + errorMessageMode: ErrorMessageMode = 'message', +): void { + const { t } = useI18n(); + const userStore = useUserStoreWithOut(); + let errMessage = ''; + + switch (status) { + case 400: + errMessage = `${msg}`; + break; + // 401: Not logged in + // Jump to the login page if not logged in, and carry the path of the current page + // Return to the current page after successful login. This step needs to be operated on the login page. + case 401: + userStore.setToken(undefined); + errMessage = msg || t('sys.api.errMsg401'); + if (stp === SessionTimeoutProcessingEnum.PAGE_COVERAGE) { + userStore.setSessionTimeout(true); + } else { + userStore.logout(true); + } + break; + case 403: + errMessage = t('sys.api.errMsg403'); + break; + // 404请求不存在 + case 404: + errMessage = t('sys.api.errMsg404'); + break; + case 405: + errMessage = t('sys.api.errMsg405'); + break; + case 408: + errMessage = t('sys.api.errMsg408'); + break; + case 500: + errMessage = t('sys.api.errMsg500'); + break; + case 501: + errMessage = t('sys.api.errMsg501'); + break; + case 502: + errMessage = t('sys.api.errMsg502'); + break; + case 503: + errMessage = t('sys.api.errMsg503'); + break; + case 504: + errMessage = t('sys.api.errMsg504'); + break; + case 505: + errMessage = t('sys.api.errMsg505'); + break; + default: + } + + if (errMessage) { + if (errorMessageMode === 'modal') { + createErrorModal({ title: t('sys.api.errorTip'), content: errMessage }); + } else if (errorMessageMode === 'message') { + error({ content: errMessage, key: `global_error_message_status_${status}` }); + } + } +} diff --git a/src/utils/http/lraxios/helper.ts b/src/utils/http/lraxios/helper.ts new file mode 100644 index 0000000..f2e05f1 --- /dev/null +++ b/src/utils/http/lraxios/helper.ts @@ -0,0 +1,56 @@ +/* + * @Author: 刘妍 + * @Date: 2024-01-13 13:04:15 + * @LastEditors: Do not edit + * @LastEditTime: 2024-01-22 08:55:09 + * @FilePath: \费县天空地大屏正式代码e:\新架构\vue-vben-admin\src\utils\http\axios\helper.ts + * @Description: + */ +import { isObject, isString } from '@/utils/is'; + +const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; + +export function joinTimestamp( + join: boolean, + restful: T, +): T extends true ? string : object; + +export function joinTimestamp(join: boolean, restful = false): string | object { + if (!join) { + return restful ? '' : {}; + } + // const now = new Date().getTime(); + // if (restful) { + // return `?_t=${now}`; + // } + // return { _t: now }; +} + +/** + * @description: Format request parameter time + */ +export function formatRequestDate(params: Recordable) { + if (Object.prototype.toString.call(params) !== '[object Object]') { + return; + } + + for (const key in params) { + const format = params[key]?.format ?? null; + if (format && typeof format === 'function') { + params[key] = params[key].format(DATE_TIME_FORMAT); + } + if (isString(key)) { + const value = params[key]; + if (value) { + try { + params[key] = isString(value) ? value.trim() : value; + } catch (error: any) { + throw new Error(error); + } + } + } + if (isObject(params[key])) { + formatRequestDate(params[key]); + } + } +} diff --git a/src/utils/http/lraxios/index.ts b/src/utils/http/lraxios/index.ts new file mode 100644 index 0000000..aeed455 --- /dev/null +++ b/src/utils/http/lraxios/index.ts @@ -0,0 +1,293 @@ +// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动 +// The axios configuration can be changed according to the project, just change the file, other files can be left unchanged + +import type { AxiosInstance, AxiosResponse } from 'axios'; +import { clone } from 'lodash-es'; +import type { RequestOptions, Result } from '#/axios'; +import type { AxiosTransform, CreateAxiosOptions } from './axiosTransform'; +import { VAxios } from './Axios'; +import { checkStatus } from './checkStatus'; +import { useGlobSetting } from '@/hooks/setting'; +import { useMessage } from '@/hooks/web/useMessage'; +import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum'; +import { isString, isUndefined, isNull, isEmpty } from '@/utils/is'; +import { getToken } from '@/utils/auth'; +import { setObjToUrlParams, deepMerge } from '@/utils'; +import { useErrorLogStoreWithOut } from '@/store/modules/errorLog'; +import { useI18n } from '@/hooks/web/useI18n'; +import { joinTimestamp, formatRequestDate } from './helper'; +import { useUserStoreWithOut } from '@/store/modules/user'; +import { AxiosRetry } from '@/utils/http/axios/axiosRetry'; +import axios from 'axios'; + +const globSetting = useGlobSetting(); +const urlPrefix = globSetting.urlPrefix; +const { createMessage, createErrorModal, createSuccessModal } = useMessage(); +/** + * @description: 数据处理,方便区分多种处理方式 + */ +const transform: AxiosTransform = { + /** + * @description: 处理响应数据。如果数据不是预期格式,可直接抛出错误 + */ + transformResponseHook: (res: AxiosResponse, options: RequestOptions) => { + const { t } = useI18n(); + const { isTransformResponse, isReturnNativeResponse } = options; + // 是否返回原生响应头 比如:需要获取响应头时使用该属性 + if (isReturnNativeResponse) { + return res; + } + // 不进行任何处理,直接返回 + // 用于页面代码可能需要直接获取code,data,message这些信息时开启 + if (!isTransformResponse) { + return res.data; + } + // 错误的时候返回 + + const { data } = res; + if (!data) { + // return '[HTTP] Request has no return value'; + throw new Error(t('sys.api.apiRequestFailed')); + } + // 这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式 + const { code, result, message } = data; + // 这里逻辑可以根据项目进行修改 + const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS; + if (hasSuccess) { + let successMsg = message; + + if (isNull(successMsg) || isUndefined(successMsg) || isEmpty(successMsg)) { + successMsg = t(`sys.api.operationSuccess`); + } + + if (options.successMessageMode === 'modal') { + createSuccessModal({ title: t('sys.api.successTip'), content: successMsg }); + } else if (options.successMessageMode === 'message') { + createMessage.success(successMsg); + } + if(data.data.rows){ + return data.data.rows + }else{ + return data.data; + } + + } + + // 在此处根据自己项目的实际情况对不同的code执行不同的操作 + // 如果不希望中断当前请求,请return数据,否则直接抛出异常即可 + let timeoutMsg = ''; + switch (code) { + case ResultEnum.TIMEOUT: + timeoutMsg = t('sys.api.timeoutMessage'); + const userStore = useUserStoreWithOut(); + userStore.logout(true); + break; + default: + if (message) { + timeoutMsg = message; + } + } + + // errorMessageMode='modal'的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误 + // errorMessageMode='none' 一般是调用时明确表示不希望自动弹出错误提示 + if (options.errorMessageMode === 'modal') { + createErrorModal({ title: t('sys.api.errorTip'), content: timeoutMsg }); + } else if (options.errorMessageMode === 'message') { + createMessage.error(timeoutMsg); + } + + throw new Error(timeoutMsg || t('sys.api.apiRequestFailed')); + }, + + // 请求之前处理config + beforeRequestHook: (config, options) => { + const { lrApi, joinPrefix, joinParamsToUrl, formatDate, joinTime = true, urlPrefix } = options; + + if (joinPrefix) { + config.url = `${urlPrefix}${config.url}`; + } + + if (lrApi && isString(lrApi)) { + config.url = `${lrApi}${config.url}`; + } + const params = config.params || {}; + const data = config.data || false; + formatDate && data && !isString(data) && formatRequestDate(data); + if (config.method?.toUpperCase() === RequestEnum.GET) { + if (!isString(params)) { + // 给 get 请求加上时间戳参数,避免从缓存中拿数据。 + config.params = Object.assign(params || {}, joinTimestamp(joinTime, false)); + } else { + // 兼容restful风格 + config.url = config.url + params + `${joinTimestamp(joinTime, true)}`; + config.params = undefined; + } + } else { + if (!isString(params)) { + formatDate && formatRequestDate(params); + if ( + Reflect.has(config, 'data') && + config.data && + (Object.keys(config.data).length > 0 || config.data instanceof FormData) + ) { + config.data = data; + config.params = params; + } else { + // 非GET请求如果没有提供data,则将params视为data + config.data = params; + config.params = undefined; + } + if (joinParamsToUrl) { + config.url = setObjToUrlParams( + config.url as string, + Object.assign({}, config.params, config.data), + ); + } + } else { + // 兼容restful风格 + config.url = config.url + params; + config.params = undefined; + } + } + return config; + }, + + /** + * @description: 请求拦截器处理 + */ + requestInterceptors: (config, options) => { + // 请求之前处理config + const token = getToken(); + if (token && (config as Recordable)?.requestOptions?.withToken !== false) { + // jwt token + (config as Recordable).headers.Authorization = options.authenticationScheme + ? `${options.authenticationScheme} ${token}` + : token; + config.headers['X-Token'] = token; + } + return config; + }, + + /** + * @description: 响应拦截器处理 + */ + responseInterceptors: (res: AxiosResponse) => { + return res; + }, + + /** + * @description: 响应错误处理 + */ + responseInterceptorsCatch: (axiosInstance: AxiosInstance, error: any) => { + const { t } = useI18n(); + const errorLogStore = useErrorLogStoreWithOut(); + errorLogStore.addAjaxErrorInfo(error); + const { response, code, message, config } = error || {}; + const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none'; + const msg: string = response?.data?.error?.message ?? ''; + const err: string = error?.toString?.() ?? ''; + let errMessage = ''; + + if (axios.isCancel(error)) { + return Promise.reject(error); + } + + try { + if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) { + errMessage = t('sys.api.apiTimeoutMessage'); + } + if (err?.includes('Network Error')) { + errMessage = t('sys.api.networkExceptionMsg'); + } + + if (errMessage) { + if (errorMessageMode === 'modal') { + createErrorModal({ title: t('sys.api.errorTip'), content: errMessage }); + } else if (errorMessageMode === 'message') { + createMessage.error(errMessage); + } + return Promise.reject(error); + } + } catch (error) { + throw new Error(error as unknown as string); + } + + checkStatus(error?.response?.status, msg, errorMessageMode); + + // 添加自动重试机制 保险起见 只针对GET请求 + const retryRequest = new AxiosRetry(); + const { isOpenRetry } = config.requestOptions.retryRequest; + config.method?.toUpperCase() === RequestEnum.GET && + isOpenRetry && + // @ts-ignore + retryRequest.retry(axiosInstance, error); + return Promise.reject(error); + }, +}; + +function createAxios(opt?: Partial) { + return new VAxios( + // 深度合并 + deepMerge( + { + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes + // authentication schemes,e.g: Bearer + // authenticationScheme: 'Bearer', + authenticationScheme: '', + timeout: 10 * 1000, + // 基础接口地址 + // baseURL: globSetting.lrApi, + + headers: { + 'Content-Type': ContentTypeEnum.JSON, + // 'T-Token':getToken() + 'Token':"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDk4NTc4MjMsImV4cCI6MTcwOTkwMTAyMywiVXNlcklkIjoiU3lzdGVtIiwiVXNlck5hbWUiOiLotoXnuqfnrqHnkIblkZgiLCJBY2NvdW50IjoiU3lzdGVtIn0.U2mHpvl4cx81XRP4JRmwrUg0dSXNkFv_EXSJQI1mtLI" + }, + // 如果是form-data格式 + // headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED }, + // 数据处理方式 + transform: clone(transform), + // 配置项,下面的选项都可以在独立的接口请求中覆盖 + requestOptions: { + // 默认将prefix 添加到url + joinPrefix: true, + // 是否返回原生响应头 比如:需要获取响应头时使用该属性 + isReturnNativeResponse: false, + // 需要对返回数据进行处理 + isTransformResponse: true, + // post请求的时候添加参数到url + joinParamsToUrl: false, + // 格式化提交参数时间 + formatDate: true, + // 消息提示类型 + errorMessageMode: 'message', + // 接口地址 + lrApi: globSetting.lrApi, + // 接口拼接地址 + urlPrefix: urlPrefix, + // 是否加入时间戳 + joinTime: true, + // 忽略重复请求 + ignoreCancelToken: true, + // 是否携带token + withToken: true, + retryRequest: { + isOpenRetry: true, + count: 5, + waitTime: 100, + }, + }, + }, + opt || {}, + ), + ); +} +export const defHttp = createAxios(); + +// other api url +// export const otherHttp = createAxios({ +// requestOptions: { +// lrApi: 'xxx', +// urlPrefix: 'xxx', +// }, +// }); diff --git a/src/views/demo/onlineform/formdesign/FormModal.vue b/src/views/demo/onlineform/formdesign/FormModal.vue new file mode 100644 index 0000000..66bb858 --- /dev/null +++ b/src/views/demo/onlineform/formdesign/FormModal.vue @@ -0,0 +1,42 @@ + + + \ No newline at end of file diff --git a/src/views/demo/onlineform/formdesign/MenuTree.vue b/src/views/demo/onlineform/formdesign/MenuTree.vue new file mode 100644 index 0000000..8538e7b --- /dev/null +++ b/src/views/demo/onlineform/formdesign/MenuTree.vue @@ -0,0 +1,95 @@ + + \ No newline at end of file diff --git a/src/views/demo/onlineform/formdesign/form.data.ts b/src/views/demo/onlineform/formdesign/form.data.ts new file mode 100644 index 0000000..8a84365 --- /dev/null +++ b/src/views/demo/onlineform/formdesign/form.data.ts @@ -0,0 +1,49 @@ +import { BasicColumn, FormSchema } from '@/components/Table'; +import { h } from 'vue'; +import { Tag } from 'ant-design-vue'; + +export const columns: BasicColumn[] = [ + + { + title: '名称', + dataIndex: 'f_Name', + }, + { + title: '分类', + dataIndex: 'f_Category_Name', + }, + { + title: '类型', + dataIndex: 'status', + width: 80, + customRender: ({ record }) => { + const status = record.f_FormType; + const enable = ~~status === 0; + const color = enable ? '#67c23a' : '#e6a23c'; + const text = enable ? '常规表单' : '视图表单'; + return h(Tag, { color: color }, () => text); + }, + }, + { + title: '创建人', + dataIndex: 'f_CreateUserName' + }, + { + title: '创建时间', + dataIndex: 'f_CreateDate' + }, + // { + // title: '备注', + // dataIndex: 'remark', + // }, +]; + +export const searchFormSchema: FormSchema[] = [ + { + field: 'key', + label: '关键字', + component: 'Input', + colProps: { span: 8 }, + }, +]; + diff --git a/src/views/demo/onlineform/formdesign/form.vue b/src/views/demo/onlineform/formdesign/form.vue new file mode 100644 index 0000000..9c4d708 --- /dev/null +++ b/src/views/demo/onlineform/formdesign/form.vue @@ -0,0 +1,802 @@ + + \ No newline at end of file diff --git a/src/views/demo/onlineform/formdesign/form/DataBaseTable.vue b/src/views/demo/onlineform/formdesign/form/DataBaseTable.vue new file mode 100644 index 0000000..773cfed --- /dev/null +++ b/src/views/demo/onlineform/formdesign/form/DataBaseTable.vue @@ -0,0 +1,60 @@ + + + \ No newline at end of file diff --git a/src/views/demo/onlineform/formdesign/form/DataObject.vue b/src/views/demo/onlineform/formdesign/form/DataObject.vue new file mode 100644 index 0000000..bec5f41 --- /dev/null +++ b/src/views/demo/onlineform/formdesign/form/DataObject.vue @@ -0,0 +1,78 @@ + + + \ No newline at end of file diff --git a/src/views/demo/onlineform/formdesign/form/databasetable.data.ts b/src/views/demo/onlineform/formdesign/form/databasetable.data.ts new file mode 100644 index 0000000..90ebcba --- /dev/null +++ b/src/views/demo/onlineform/formdesign/form/databasetable.data.ts @@ -0,0 +1,100 @@ +import { BasicColumn, FormSchema } from '@/components/Table'; +import { getOutKeyList } from '@/api/formdesign/index' +import { h } from 'vue'; +import { Tag } from 'ant-design-vue'; +let tabList + + +export function sendtabledata(data:any[]) { + console.log('aaad',data) + tabList = data +} + + +export const columns: BasicColumn[] = [ + { + title: '名称', + dataIndex: 'status', + width: 100, + editComponent: 'Select', + editable: true, + edit: true, + }, + { + title: '表名', + dataIndex: 'f_TableName', + width: 120, + }, + { + title: '外键', + editComponent: 'ApiSelect', + width: 180, + dataIndex: 'field', + editable: true, + edit: true, + editComponentProps: ({ record }) => { + return { + api: () => getOutKeyList({tableNames: record.f_TableName}).then((data: AreaRespVO[]) => { + return data[0].lr_db_codecolumnsList + }), + params: {}, + // 接口参数 + resultField: 'data', + labelField: 'f_DbColumnName', + valueField: 'f_DbColumnName', + }; + } + }, + + { + title: '关联表', + dataIndex: 'relationName', + editComponent: 'Select', + width: 180, + editable: true, + edit: true, + editComponentProps: ({ record }) => { + let arr = [] + tabList.forEach(item =>{ + if(item.f_TableName !== record.f_TableName){ + arr.push({ + label: item.f_TableName, + value: item.f_TableName, + }) + } + }) + return { + options: arr, + onChange: (e: any) => { + tabSelectId = e + } + } + }, + }, + { + title: '关联主键', + editComponent: 'ApiSelect', + width: 180, + dataIndex: 'relationField', + editable: true, + edit: true, + editComponentProps: ({ record }) => { + + return { + api: () => getOutKeyList({tableNames: record.f_TableName}).then((data: AreaRespVO[]) => { + return data[0].lr_db_codecolumnsList + }), + params: {}, + // 接口参数 + resultField: 'data', + labelField: 'f_DbColumnName', + valueField: 'f_DbColumnName', + }; + + }, + }, + +]; + + + \ No newline at end of file diff --git a/src/views/demo/onlineform/formdesign/form/dataobject.data.ts b/src/views/demo/onlineform/formdesign/form/dataobject.data.ts new file mode 100644 index 0000000..a3ab0d7 --- /dev/null +++ b/src/views/demo/onlineform/formdesign/form/dataobject.data.ts @@ -0,0 +1,25 @@ +import { BasicColumn } from '@/components/Table'; +import { h } from 'vue'; +import { Tag } from 'ant-design-vue'; + + + +export const columns: BasicColumn[] = [ + { + title: '表名', + dataIndex: 'f_TableName', + }, + { + title: '备注', + dataIndex: 'f_Description' + }, + { + title: '状态', + dataIndex: 'f_State' + } +]; + + + + + diff --git a/src/views/demo/onlineform/formdesign/form/index.data.ts b/src/views/demo/onlineform/formdesign/form/index.data.ts new file mode 100644 index 0000000..cb93cfe --- /dev/null +++ b/src/views/demo/onlineform/formdesign/form/index.data.ts @@ -0,0 +1,213 @@ +import { BasicColumn, FormSchema } from '@/components/Table'; +import { optionsListApi,getOutKeyList } from '@/api/formdesign/index' +import { h } from 'vue'; +import { Tag } from 'ant-design-vue'; +let tabList +let tabSelectId + +export function sendtabledata(data:any[]) { + tabList = data +} + + +export const columns: BasicColumn[] = [ + { + title: '类别', + dataIndex: 'status', + width: 50, + customRender: ({ record }) => { + const status = record.type; + let enable + if(status == "main"){ + enable = true + }else{ + enable = false + } + const color = enable ? '#67c23a' : '#e6a23c'; + const text = enable ? '主表' : '子表'; + return h(Tag, { color: color }, () => text); + }, + }, + { + title: '表名', + dataIndex: 'f_TableName', + width: 120, + }, + { + title: '外键', + editComponent: 'ApiSelect', + width: 180, + dataIndex: 'field', + editable: true, + edit: true, + editComponentProps: ({ record }) => { + return { + api: () => getOutKeyList({tableNames: record.f_TableName}).then((data: AreaRespVO[]) => { + return data[0].lr_db_codecolumnsList + }), + params: {}, + // 接口参数 + resultField: 'data', + labelField: 'f_DbColumnName', + valueField: 'f_DbColumnName', + }; + } + }, + + { + title: '关联表', + dataIndex: 'relationName', + editComponent: 'Select', + width: 180, + editable: true, + edit: true, + editComponentProps: ({ record }) => { + let arr = [] + tabList.forEach(item =>{ + if(item.f_TableName !== record.f_TableName){ + arr.push({ + label: item.f_TableName, + value: item.f_TableName, + }) + } + }) + return { + options: arr, + onChange: (e: any) => { + tabSelectId = e + } + } + }, + }, + { + title: '关联主键', + editComponent: 'ApiSelect', + width: 180, + dataIndex: 'relationField', + editable: true, + edit: true, + editComponentProps: ({ record }) => { + return { + api: () => getOutKeyList({tableNames: record.f_TableName}).then((data: AreaRespVO[]) => { + return data[0].lr_db_codecolumnsList + }), + params: {}, + // 接口参数 + resultField: 'data', + labelField: 'f_DbColumnName', + valueField: 'f_DbColumnName', + }; + + }, + }, + +]; + +export const formSchema: FormSchema[] = [ + { + field: 'f_Name', + component: 'Input', + label: '名称', + colProps: { + span: 24, + }, + defaultValue: '', + rules: [{ required: true }], + componentProps: { + placeholder: '请输入', + onChange: (e) => { + console.log(e); + }, + }, + }, + { + field: 'f_Category', + component: 'ApiSelect', + label: '分类', + required: true, + colProps: { + span: 24, + }, + componentProps: ({ formActionType, formModel }) => { + return { + api: optionsListApi, // 接口 + // 接口参数 + resultField: 'data', + labelField: 'f_ItemName', + valueField: 'f_ItemId', + }; + }, + }, + { + field: 'f_DbCode', + component: 'ApiSelect', + label: '数据库', + required: true, + colProps: { + span: 24, + }, + componentProps: { + options: [ + { label: '系统数据库', value: 'lrsystemdb' }, + ], + }, + }, + { + field: 'f_EnabledMark', + label: '状态', + component: 'RadioButtonGroup', + defaultValue: 1, + componentProps: { + options: [ + { label: '是', value: 1 }, + { label: '否', value: 0 }, + ], + }, + }, + { + field: 'f_FormType', + label: '类型', + component: 'ApiRadioGroup', + defaultValue: '0', + componentProps: { + options: [ + { label: '常规表单', value: '0' }, + { label: '视图表单', value: '1' }, + ], + }, + colProps: { lg: 24, md: 24 }, + }, + { + field: 'f_Description', + component: 'InputTextArea', + label: '描述', + colProps: { + span: 24, + }, + defaultValue: '', + componentProps: { + placeholder: '请输入', + onChange: (e) => { + console.log(e); + }, + }, + }, + { + field: 'field1', + label: '', + colProps: { + span: 24, + }, + slot: 'addDatabaseTableSlot', + }, + { + field: 'field1', + label: '', + colProps: { + span: 24, + }, + slot: 'addDatabaseContantSlot', + }, +] + + \ No newline at end of file diff --git a/src/views/demo/onlineform/formdesign/form/index.vue b/src/views/demo/onlineform/formdesign/form/index.vue new file mode 100644 index 0000000..424f37f --- /dev/null +++ b/src/views/demo/onlineform/formdesign/form/index.vue @@ -0,0 +1,160 @@ + + + \ No newline at end of file diff --git a/src/views/demo/onlineform/formdesign/index.vue b/src/views/demo/onlineform/formdesign/index.vue new file mode 100644 index 0000000..9df426d --- /dev/null +++ b/src/views/demo/onlineform/formdesign/index.vue @@ -0,0 +1,181 @@ + + \ No newline at end of file diff --git a/src/views/demo/onlineform/formdesign/menu.data.ts b/src/views/demo/onlineform/formdesign/menu.data.ts new file mode 100644 index 0000000..0e7026d --- /dev/null +++ b/src/views/demo/onlineform/formdesign/menu.data.ts @@ -0,0 +1,198 @@ +import { BasicColumn, FormSchema } from '@/components/Table'; +import { h } from 'vue'; +import { Tag } from 'ant-design-vue'; +import Icon from '@/components/Icon/Icon.vue'; + +export const columns: BasicColumn[] = [ + { + title: '按钮名称', + dataIndex: 'name', + }, + { + title: 'DOMID', + dataIndex: 'domId', + }, + { + title: '排序', + dataIndex: 'sort', + }, + +]; + +const isDir = (type: string) => type === '0'; +const isMenu = (type: string) => type === '1'; +const isButton = (type: string) => type === '2'; + +export const searchFormSchema: FormSchema[] = [ + { + field: 'key', + label: '关键字', + component: 'Input', + colProps: { span: 8 }, + }, +]; + +export const formSchema: FormSchema[] = [ + { + field: 'type', + label: '类型', + component: 'RadioButtonGroup', + defaultValue: '1', + componentProps: { + options: [ + // { label: '目录', value: '0' }, + { label: '菜单', value: '1' }, + { label: '按钮', value: '2' }, + ], + }, + colProps: { lg: 24, md: 24 }, + }, + { + field: 'id', + label: '名称', + component: 'Input', + ifShow:false + }, + { + field: 'name', + label: '名称', + component: 'Input', + required: true, + }, + + { + field: 'parentId', + label: '上级', + component: 'TreeSelect', + componentProps: { + fieldNames: { + label: 'name', + key: 'id', + value: 'id', + }, + getPopupContainer: () => document.body, + }, + ifShow: ({ values }) => !isButton(values.type), + }, + { + field: 'moduleId', + label: '菜单', + component: 'TreeSelect', + componentProps: { + fieldNames: { + label: 'name', + key: 'id', + value: 'id', + }, + getPopupContainer: () => document.body, + }, + ifShow: ({ values }) => isButton(values.type), + required: true, + }, + { + field: 'domId', + label: 'DMOID', + component: 'Input', + required: true, + ifShow: ({ values }) => isButton(values.type), + }, + { + field: 'sortNo', + label: '排序', + component: 'InputNumber', + ifShow: ({ values }) => !isButton(values.type), + }, + { + field: 'sort', + label: '排序', + component: 'InputNumber', + ifShow: ({ values }) => isButton(values.type), + }, + { + field: 'class', + helpMessage: ['参考参数值', 'success、warning、error'], + label: '样式', + component: 'Input', + ifShow: ({ values }) => isButton(values.type), + }, + { + field: 'iconName', + label: '图标', + component: 'IconPicker', + ifShow: ({ values }) => !isButton(values.type), + }, + + { + field: 'url', + label: '路由地址', + component: 'Input', + required: true, + ifShow: ({ values }) => !isButton(values.type), + }, + // { + // field: 'component', + // label: '组件路径', + // component: 'Input', + // ifShow: ({ values }) => isMenu(values.type), + // }, + { + field: 'code', + label: '权限标识', + component: 'Input', + ifShow: ({ values }) => !isButton(values.type), + }, + { + field: 'status', + label: '是否系统', + component: 'RadioButtonGroup', + defaultValue: 1, + componentProps: { + options: [ + { label: '是', value: 0 }, + { label: '否', value: 1 }, + ], + }, + ifShow: ({ values }) => !isButton(values.type), + }, + // { + // field: 'isExt', + // label: '是否外链', + // component: 'RadioButtonGroup', + // defaultValue: '0', + // componentProps: { + // options: [ + // { label: '否', value: '0' }, + // { label: '是', value: '1' }, + // ], + // }, + // ifShow: ({ values }) => !isButton(values.type), + // }, + + // { + // field: 'keepalive', + // label: '是否缓存', + // component: 'RadioButtonGroup', + // defaultValue: '0', + // componentProps: { + // options: [ + // { label: '否', value: '0' }, + // { label: '是', value: '1' }, + // ], + // }, + // ifShow: ({ values }) => isMenu(values.type), + // }, + + // { + // field: 'show', + // label: '是否显示', + // component: 'RadioButtonGroup', + // defaultValue: '0', + // componentProps: { + // options: [ + // { label: '是', value: '0' }, + // { label: '否', value: '1' }, + // ], + // }, + // ifShow: ({ values }) => !isButton(values.type), + // }, +]; diff --git a/src/views/demo/system/menu/MenuDrawer.vue b/src/views/demo/system/menu/MenuDrawer.vue index 2ac72f0..5189d04 100644 --- a/src/views/demo/system/menu/MenuDrawer.vue +++ b/src/views/demo/system/menu/MenuDrawer.vue @@ -52,7 +52,6 @@ const values = await validate(); setDrawerProps({ confirmLoading: true }); // TODO custom api - console.log(values) if (values.type == '1') { console.log("新增菜单") delete values.type diff --git a/src/views/demo/system/menu/index.vue b/src/views/demo/system/menu/index.vue index 0e7996d..21bb974 100644 --- a/src/views/demo/system/menu/index.vue +++ b/src/views/demo/system/menu/index.vue @@ -117,7 +117,6 @@ nextTick(expandAll); } function onBtnClicked(domId) { - console.log(domId) switch (domId) { case 'btnAdd': handleCreate() diff --git a/src/views/demo/system/position/index.vue b/src/views/demo/system/position/index.vue index 8dbf6e5..ffcc2ed 100644 --- a/src/views/demo/system/position/index.vue +++ b/src/views/demo/system/position/index.vue @@ -74,6 +74,7 @@ }); } async function editGroup(record: Recordable) { + console.log('record',record) openDrawer(true, { record, isUpdate: true, diff --git a/vite.config.ts b/vite.config.ts index 6cec6e6..0dfbab5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,8 +2,7 @@ * @Author: 刘妍 * @Date: 2024-01-13 13:04:15 * @LastEditors: Do not edit - * @LastEditTime: 2024-01-17 16:06:35 - * @FilePath: \费县天空地大屏正式代码e:\新架构\vue-vben-admin\vite.config.ts + * @LastEditTime: 2024-03-08 09:17:00 * @Description: */ import { defineApplicationConfig } from '@vben/vite-config';