├── .browserslistrc ├── .dockerignore ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .gitlab-ci.yml ├── .npmrc ├── Dockerfile ├── LICENSE ├── README.md ├── babel.config.js ├── build └── tag.sh ├── commitlint.config.js ├── config └── default.yaml ├── docs ├── README.md ├── 实时测试.md └── 技术文档.md ├── env.js ├── index.html ├── jest.config.js ├── mock └── index.js ├── package.json ├── public ├── favicon.png └── index.html ├── src ├── App.vue ├── assets │ ├── iconfont.js │ └── logo.svg ├── bus.ts ├── components │ ├── README.md │ ├── ant-design │ │ ├── icons.ts │ │ └── index.ts │ ├── baseInstall │ │ └── m-image-preivew │ │ │ ├── index.js │ │ │ └── index.vue │ ├── common │ │ ├── index.ts │ │ ├── m-drawer-footer │ │ │ └── index.vue │ │ ├── m-filter-select │ │ │ ├── FilterSelect.tsx │ │ │ ├── MultiSelect.tsx │ │ │ ├── SingleSelect.tsx │ │ │ ├── menu │ │ │ │ ├── VirtualMenu.vue │ │ │ │ ├── menu.less │ │ │ │ └── type.ts │ │ │ └── select.less │ │ ├── m-icon-font │ │ │ └── index.vue │ │ ├── m-rowselect-table │ │ │ └── index.vue │ │ ├── m-skeleton │ │ │ └── skeleton.js │ │ ├── m-virtual-scroll │ │ │ └── index.vue │ │ ├── u-divider-dash.vue │ │ ├── u-divider.vue │ │ ├── u-filter-select.vue │ │ ├── u-mutiple-select.vue │ │ ├── u-single-select.vue │ │ ├── u-spinner.vue │ │ └── u-tag.vue │ ├── layout │ │ ├── index.ts │ │ ├── topbar │ │ │ ├── Topbar.vue │ │ │ ├── assets │ │ │ │ ├── logo.svg │ │ │ │ └── toggle-bar.svg │ │ │ ├── components │ │ │ │ ├── IconFont.vue │ │ │ │ ├── Link.vue │ │ │ │ └── iconFont.js │ │ │ ├── index.ts │ │ │ ├── product-navigation │ │ │ │ ├── Group.vue │ │ │ │ ├── Panel.vue │ │ │ │ └── index.vue │ │ │ ├── product-select │ │ │ │ ├── ProductSelectModal.vue │ │ │ │ └── index.vue │ │ │ ├── quick-navigation │ │ │ │ ├── SortableContainer.vue │ │ │ │ ├── SortableItem.vue │ │ │ │ └── index.vue │ │ │ ├── styles │ │ │ │ ├── global.less │ │ │ │ └── var.less │ │ │ ├── type.ts │ │ │ ├── user-operation │ │ │ │ └── index.vue │ │ │ └── utils │ │ │ │ ├── loadJs.ts │ │ │ │ └── util.ts │ │ ├── u-collapse.vue │ │ ├── u-info-item.vue │ │ ├── u-info.vue │ │ ├── u-layout.vue │ │ ├── u-left-right.vue │ │ ├── u-sidebar.vue │ │ └── u-topbar.vue │ ├── modal │ │ └── ImagePreviewModal.vue │ ├── register-test.ts │ ├── register-vite.ts │ ├── register.ts │ └── specific │ │ ├── index.ts │ │ ├── s-editor.vue │ │ ├── s-page-top.vue │ │ ├── s-table-search.vue │ │ ├── s-upload-image.vue │ │ ├── s-user-select.vue │ │ └── upload-request.js ├── main.ts ├── middler │ ├── README.md │ ├── babelPlugin │ │ ├── FunctionInstrumentation │ │ │ └── AuthoirtyFeat.js │ │ └── installCommon.js │ ├── vitePlugin │ │ ├── antDesignVueImport.js │ │ ├── authorityFeat.js │ │ └── injectAntIcon.js │ └── webpackLoader │ │ └── authorityFeat.js ├── mixin.ts ├── ndsc-vue3 │ ├── basic-components │ │ └── lib │ │ │ ├── components │ │ │ ├── s-sync-button │ │ │ │ ├── index.ts │ │ │ │ └── s-sync-button.vue │ │ │ ├── u-copy │ │ │ │ ├── index.ts │ │ │ │ └── u-copy.vue │ │ │ ├── u-drawer │ │ │ │ ├── index.ts │ │ │ │ └── u-drawer.vue │ │ │ ├── u-hint-tooltip │ │ │ │ ├── index.ts │ │ │ │ └── u-hint-tooltip.vue │ │ │ ├── u-icon-button │ │ │ │ ├── index.ts │ │ │ │ └── u-icon-button.vue │ │ │ ├── u-icon-font │ │ │ │ ├── iconfont.js │ │ │ │ ├── index.ts │ │ │ │ └── u-icon-font.vue │ │ │ ├── u-modal │ │ │ │ ├── index.ts │ │ │ │ └── u-modal.vue │ │ │ ├── u-status-icon │ │ │ │ ├── index.ts │ │ │ │ └── u-status-icon.vue │ │ │ ├── u-text-button │ │ │ │ ├── index.ts │ │ │ │ └── u-text-button.vue │ │ │ └── u-text-tooltip │ │ │ │ ├── index.ts │ │ │ │ └── u-text-tooltip.vue │ │ │ ├── directives │ │ │ ├── v-click-outside │ │ │ │ ├── index.ts │ │ │ │ └── v-click-outside.ts │ │ │ ├── v-copy │ │ │ │ ├── index.ts │ │ │ │ └── v-copy.ts │ │ │ ├── v-drag-size │ │ │ │ ├── index.ts │ │ │ │ ├── v-drag-size.less │ │ │ │ └── v-drag-size.ts │ │ │ ├── v-ellipsis-title │ │ │ │ ├── index.ts │ │ │ │ └── v-ellipsis-title.ts │ │ │ └── v-input-number │ │ │ │ ├── index.ts │ │ │ │ └── v-input-number.ts │ │ │ ├── index.ts │ │ │ ├── shims-tsx.d.ts │ │ │ ├── shims-vue.d.ts │ │ │ ├── style │ │ │ ├── antd.less │ │ │ ├── index.less │ │ │ ├── s-sync-button.less │ │ │ ├── u-drawer.less │ │ │ ├── u-icon-button.less │ │ │ ├── u-icon-font.less │ │ │ ├── u-modal.less │ │ │ ├── u-status-icon.less │ │ │ ├── u-text-button.less │ │ │ └── u-text-tooltip.less │ │ │ └── utils │ │ │ └── clipboard.ts │ ├── exception-page │ │ ├── README.md │ │ ├── assets │ │ │ ├── 403.svg │ │ │ ├── 404.svg │ │ │ └── nobiz.svg │ │ ├── lib │ │ │ ├── index.ts │ │ │ ├── shims-vue.d.ts │ │ │ ├── u-page-nobiz.vue │ │ │ ├── u-page403.vue │ │ │ └── u-page404.vue │ │ └── package.json │ ├── sidebar │ │ ├── README.md │ │ ├── lib │ │ │ ├── VNode.tsx │ │ │ ├── components │ │ │ │ ├── IconFont.vue │ │ │ │ └── iconfont.js │ │ │ ├── flexbox.css │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ ├── shims-tsx.d.ts │ │ │ ├── shims-vue.d.ts │ │ │ ├── sidebar.less │ │ │ └── sidebar.vue │ │ └── types │ │ │ └── index.d.ts │ ├── style │ │ ├── README.md │ │ ├── lib │ │ │ ├── ant-design │ │ │ │ ├── button.less │ │ │ │ ├── collapse.less │ │ │ │ ├── form.less │ │ │ │ ├── index.less │ │ │ │ ├── index.ts │ │ │ │ ├── modal.less │ │ │ │ └── table.less │ │ │ ├── common.less │ │ │ ├── flex.less │ │ │ ├── font.less │ │ │ ├── index.less │ │ │ ├── index.ts │ │ │ ├── margin.less │ │ │ ├── padding.less │ │ │ ├── text.less │ │ │ ├── var.js │ │ │ └── var.less │ │ └── package.json │ ├── theme │ │ ├── README.md │ │ ├── index.html │ │ ├── lib │ │ │ ├── common │ │ │ │ └── index.less │ │ │ ├── dark │ │ │ │ ├── common.less │ │ │ │ ├── index.js │ │ │ │ ├── index.less │ │ │ │ └── var.less │ │ │ ├── light │ │ │ │ ├── common.less │ │ │ │ ├── index.js │ │ │ │ ├── index.less │ │ │ │ └── var.less │ │ │ ├── purple │ │ │ │ ├── common.less │ │ │ │ ├── index.js │ │ │ │ ├── index.less │ │ │ │ └── var.less │ │ │ └── utils │ │ │ │ ├── ThemeMixin.vue │ │ │ │ └── index.js │ │ ├── package.json │ │ ├── scripts │ │ │ ├── copyCSS.js │ │ │ └── copyVue.js │ │ └── webpack.config.js │ └── utils │ │ ├── README.md │ │ └── lib │ │ ├── TableMixin.vue │ │ ├── common.ts │ │ ├── index.ts │ │ ├── instance.ts │ │ ├── loadJs.ts │ │ ├── request.ts │ │ ├── shims-tsx.d.ts │ │ ├── shims-vue.d.ts │ │ ├── url.ts │ │ ├── validator.ts │ │ └── vueProto.ts ├── pv.ts ├── router.ts ├── services │ ├── README.md │ ├── app.service.ts │ ├── auth.service.ts │ ├── authority.service.ts │ ├── common.service.ts │ ├── event.service.ts │ ├── login.service.ts │ ├── object.service.ts │ ├── parameter.service.ts │ ├── record.service.ts │ ├── requirement.design.service.ts │ ├── requirement.service.ts │ ├── socket.ts │ ├── tag.service.ts │ ├── template.service.ts │ ├── terminal.service.ts │ ├── terminalVersion.service.ts │ ├── test │ │ └── realTime │ │ │ └── index.service.ts │ └── version.service.ts ├── sevice.d.ts ├── shims-tsx.d.ts ├── shims-vue.d.ts ├── store │ ├── index.tsx │ └── modules │ │ └── live-test.ts ├── style │ ├── animation.less │ ├── ant-design.less │ ├── global.less │ ├── index.less │ ├── nprogress.less │ └── scrollbar.less ├── track.ts ├── types │ ├── README.md │ ├── app.type.ts │ ├── authority.type.ts │ ├── common.type.ts │ ├── event.type.ts │ ├── object.type.ts │ ├── parameter.type.ts │ ├── realtime.type.ts │ ├── requirement.design.type.ts │ ├── requirement.type.ts │ ├── spm.type.ts │ ├── table.type.ts │ ├── template.type.ts │ ├── terminal.type.ts │ ├── terminalVersion.type.ts │ └── version.type.ts ├── utils │ ├── common.ts │ ├── cookie.ts │ ├── hooks │ │ ├── Object │ │ │ └── useFindAndroidAndIPhone.ts │ │ ├── useCopy.ts │ │ └── useCopyParams.ts │ ├── request.ts │ ├── tests │ │ └── index.ts │ ├── validator.ts │ └── vars.ts ├── views │ ├── 403.vue │ ├── 404.vue │ ├── NoBiz.vue │ └── home │ │ ├── README.md │ │ ├── authorityManagement │ │ ├── domainManage │ │ │ ├── detail │ │ │ │ └── index.vue │ │ │ ├── index.vue │ │ │ ├── member │ │ │ │ ├── OptionMember.vue │ │ │ │ └── index.vue │ │ │ ├── nav.tsx │ │ │ └── product │ │ │ │ ├── OptionPruduct.vue │ │ │ │ └── index.vue │ │ ├── index.vue │ │ ├── platformManage │ │ │ ├── components │ │ │ │ └── domain-modal │ │ │ │ │ └── index.vue │ │ │ ├── domain │ │ │ │ └── index.vue │ │ │ ├── index.vue │ │ │ └── nav.tsx │ │ └── productionManage │ │ │ ├── index.vue │ │ │ ├── info │ │ │ └── index.vue │ │ │ ├── manage │ │ │ ├── components │ │ │ │ ├── auth-tab │ │ │ │ │ ├── index.vue │ │ │ │ │ └── utils.ts │ │ │ │ ├── member-modal │ │ │ │ │ └── index.vue │ │ │ │ ├── member-tab │ │ │ │ │ └── index.vue │ │ │ │ ├── role-modal │ │ │ │ │ └── index.vue │ │ │ │ └── role-panel │ │ │ │ │ └── index.vue │ │ │ └── index.vue │ │ │ ├── member │ │ │ ├── OptionMember.vue │ │ │ └── index.vue │ │ │ └── nav.tsx │ │ ├── index.vue │ │ ├── router.ts │ │ ├── test │ │ ├── components │ │ │ ├── column-setting │ │ │ │ └── index.vue │ │ │ ├── device-detail │ │ │ │ └── index.vue │ │ │ ├── error-list │ │ │ │ └── index.vue │ │ │ ├── ignore-checkbox.vue │ │ │ ├── log-list │ │ │ │ └── index.vue │ │ │ ├── point-list │ │ │ │ ├── code-show.vue │ │ │ │ ├── detection.vue │ │ │ │ ├── index.vue │ │ │ │ └── point-code.vue │ │ │ ├── rule-drawer │ │ │ │ └── index.vue │ │ │ ├── tab-content │ │ │ │ └── index.vue │ │ │ ├── test-abstract │ │ │ │ └── index.vue │ │ │ ├── tree-table │ │ │ │ └── index.vue │ │ │ ├── types │ │ │ │ └── index.ts │ │ │ └── undefined-log-stats │ │ │ │ └── index.vue │ │ ├── index.vue │ │ ├── nav.tsx │ │ └── realtime │ │ │ ├── audit │ │ │ ├── components │ │ │ │ └── content.vue │ │ │ └── index.vue │ │ │ ├── detail │ │ │ └── index.vue │ │ │ ├── lists │ │ │ ├── content.vue │ │ │ ├── index.vue │ │ │ ├── lists.vue │ │ │ └── types │ │ │ │ └── content.ts │ │ │ └── record │ │ │ ├── components │ │ │ └── content.vue │ │ │ └── index.vue │ │ └── tracker │ │ ├── event │ │ ├── EventDetail.vue │ │ ├── Record.vue │ │ ├── VersionHistory.vue │ │ └── index.vue │ │ ├── index.vue │ │ ├── metadata │ │ ├── components │ │ │ ├── ParamBindModal.vue │ │ │ ├── ParamConfig.vue │ │ │ ├── ParamConfigDrawer.vue │ │ │ ├── VersionAddModal.vue │ │ │ └── VersionConfig.vue │ │ ├── event │ │ │ ├── EventAddModal.vue │ │ │ ├── EventEditModal.vue │ │ │ ├── List.vue │ │ │ └── index.vue │ │ ├── index.vue │ │ ├── parameter │ │ │ ├── BusinessParamAddOrEditModal.vue │ │ │ ├── BusinessParamList.vue │ │ │ ├── BusinessParamPool.vue │ │ │ ├── BusinessParamTab.vue │ │ │ ├── List.vue │ │ │ ├── ParamAddDrawer.vue │ │ │ ├── ParamDetailDrawer.vue │ │ │ ├── ParamEditDrawer.vue │ │ │ ├── ParamValueDrawer.vue │ │ │ ├── ParameterTab.vue │ │ │ ├── VariableParamSelect.vue │ │ │ └── index.vue │ │ ├── rule │ │ │ ├── components │ │ │ │ └── add-rule-modal │ │ │ │ │ └── index.vue │ │ │ └── index.vue │ │ ├── template │ │ │ ├── List.vue │ │ │ ├── ParamConfigForm.vue │ │ │ ├── index.vue │ │ │ ├── templateAddDrawer.vue │ │ │ ├── templateDetailDrawer.vue │ │ │ └── templateEditDrawer.vue │ │ └── terminal │ │ │ ├── List.vue │ │ │ ├── TerminalAddModal.vue │ │ │ ├── TerminalEditModal.vue │ │ │ └── index.vue │ │ ├── nav.tsx │ │ ├── object │ │ ├── detail │ │ │ ├── BackToList.vue │ │ │ ├── ImagePreviewModal.vue │ │ │ ├── LineageTab.vue │ │ │ ├── ObjHistoryTab.vue │ │ │ ├── ObjTrackerTable.vue │ │ │ ├── ParamConfigFilter.vue │ │ │ ├── ParamConfigTab.vue │ │ │ ├── ParamTableList.vue │ │ │ └── ParamTableModal.vue │ │ ├── index.vue │ │ └── list │ │ │ ├── BaseInfoForm.vue │ │ │ ├── BatchAddBaseInfoForm.vue │ │ │ ├── BloodRelationDrawer.vue │ │ │ ├── EventConfigForm.vue │ │ │ ├── Filter.vue │ │ │ ├── HistoryList.vue │ │ │ ├── LineageForm.vue │ │ │ ├── List.vue │ │ │ ├── ListHeaderFilter.vue │ │ │ ├── ObjTagSelect.vue │ │ │ ├── ObjectAddDrawer.vue │ │ │ ├── ObjectAddForm.vue │ │ │ ├── ObjectEditDiffDrawer.vue │ │ │ ├── ObjectEditDrawer.vue │ │ │ ├── ObjectEditForm.vue │ │ │ ├── ObjectViewDrawer.vue │ │ │ ├── ObjectViewForm.vue │ │ │ ├── ObjectViewLineage.vue │ │ │ ├── ParamConfigForm.vue │ │ │ ├── SampleData.vue │ │ │ ├── TagSetting.vue │ │ │ ├── TemplateImportModal.vue │ │ │ ├── TerminalAddModal.vue │ │ │ ├── TerminalConfigForm.vue │ │ │ ├── TerminalConfigItem.vue │ │ │ ├── TrackerConfigPane.vue │ │ │ ├── blood-relation │ │ │ ├── BindOrUnbindParentObjModal.vue │ │ │ ├── Filter.vue │ │ │ ├── ToolBar.vue │ │ │ ├── graph │ │ │ │ ├── ObjectDetail.vue │ │ │ │ ├── ObjectParamsTable.vue │ │ │ │ ├── ToolBar.vue │ │ │ │ ├── behavior │ │ │ │ │ ├── click-canvas.ts │ │ │ │ │ ├── click-node.ts │ │ │ │ │ ├── hover-node.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── index.vue │ │ │ │ ├── item │ │ │ │ │ ├── index.ts │ │ │ │ │ └── obj-node.ts │ │ │ │ ├── plugin │ │ │ │ │ ├── contextMenu.ts │ │ │ │ │ ├── index.less │ │ │ │ │ └── index.ts │ │ │ │ ├── share │ │ │ │ │ ├── color.ts │ │ │ │ │ └── util.ts │ │ │ │ └── type.ts │ │ │ └── index.vue │ │ │ ├── const.ts │ │ │ └── index.vue │ │ └── requirement │ │ ├── components │ │ ├── OptionButton.vue │ │ ├── StatusTag.vue │ │ ├── object-diff │ │ │ ├── BaseInfo.vue │ │ │ ├── LineageInfo.vue │ │ │ ├── ObjectDiffDetail.vue │ │ │ ├── ParamInfo.vue │ │ │ ├── TrackerInfo.vue │ │ │ └── util.ts │ │ └── sample-data │ │ │ └── index.vue │ │ ├── detail │ │ ├── BackToList.vue │ │ ├── BaseInfoTab.vue │ │ ├── ObjectDetail.vue │ │ ├── ObjectLineage.vue │ │ ├── ObjectViewDrawer.vue │ │ ├── RequireTaskDetail │ │ │ ├── BaseLineSetting.vue │ │ │ ├── EventPool.vue │ │ │ ├── ObjectPool.vue │ │ │ ├── RequirementEventPool.vue │ │ │ ├── RequirementObject.vue │ │ │ ├── RequirementTaskDetail.vue │ │ │ ├── RequriementObjectPool.vue │ │ │ ├── TaskPool.vue │ │ │ └── components │ │ │ │ ├── EventTracker.vue │ │ │ │ └── ReassignTasks.vue │ │ ├── TaskCollapse.vue │ │ ├── TaskObject.vue │ │ ├── TrackerTab.vue │ │ └── index.vue │ │ ├── index.vue │ │ ├── list │ │ ├── BatchOperateModal.vue │ │ ├── ExpandObjTable.vue │ │ ├── ObjectChangeDrawer.vue │ │ ├── ObjectChangeSelect.vue │ │ ├── ObjectOperateDrawer.vue │ │ ├── RejectTaskModal.vue │ │ ├── ReqAddDrawer.vue │ │ ├── ReqFilterDropDown.vue │ │ ├── ReqTask.vue │ │ ├── Requirement.vue │ │ ├── RequirementFilter.vue │ │ ├── RequirementGroup.vue │ │ ├── RequirementGroupList.vue │ │ ├── SettingSprint.vue │ │ ├── SettingVersion.vue │ │ ├── Task.vue │ │ ├── TaskFilter.vue │ │ ├── TaskList.vue │ │ ├── TerminalList.vue │ │ ├── TestingModal.vue │ │ └── index.vue │ │ ├── record │ │ └── index.vue │ │ └── version │ │ ├── DeployDetailModal.vue │ │ ├── Publish.vue │ │ ├── VersionList.vue │ │ ├── VersionNumEditModal.vue │ │ ├── VersionTree.vue │ │ ├── components │ │ ├── PublishList.vue │ │ ├── TaskList.vue │ │ └── VersionList.vue │ │ ├── graph │ │ ├── ToolBar.vue │ │ ├── context-menu.ts │ │ └── index.vue │ │ ├── index.vue │ │ └── version-publish │ │ ├── BackToList.vue │ │ ├── DeployContentTable.vue │ │ ├── ExpandTaskTable.vue │ │ ├── ObjectDiffDrawer.vue │ │ ├── ReqConflictCheck.vue │ │ ├── TrackerInfoForm.vue │ │ ├── VersionConflictCheck.vue │ │ └── index.vue ├── vue-router.d.ts └── vuex.d.ts ├── tsconfig.json ├── vue.config.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/.git -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .cache 3 | /node_modules 4 | /dist 5 | 6 | # Log files 7 | npm-debug.log* 8 | yarn-error.log* 9 | 10 | # Editor directories and files 11 | .idea 12 | .vscode 13 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | cache: 2 | untracked: true 3 | paths: 4 | - node_modules/ 5 | 6 | stages: 7 | - install 8 | - lint 9 | 10 | install: 11 | stage: install 12 | script: yarn 13 | 14 | lint: 15 | stage: lint 16 | script: yarn lint 17 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npm.taobao.org -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.9 AS origin_builder 2 | WORKDIR /app 3 | COPY package.json yarn.lock ./ 4 | RUN yarn install --frozen-lockfile 5 | COPY . . 6 | RUN yarn run build 7 | 8 | FROM ruisin/spa-webserver:0.8.0-beta2-onbuild as runtime 9 | COPY --from=origin_builder /app/dist/ ./dist/public 10 | RUN mv ./dist/public/index.html ./dist 11 | 12 | FROM scratch 13 | COPY --from=runtime /app / -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const installCommon = require('./src/middler/babelPlugin/installCommon.js') 2 | 3 | const isPro = process.env.DEPLOY_ENV === 'production' || process.env.DEPLOY_ENV === 'webpack' 4 | const config = { 5 | presets: [ 6 | [ 7 | '@vue/cli-plugin-babel/preset', 8 | { 9 | useBuiltIns: 'usage' // 按需引入polyfill 10 | } 11 | ] 12 | ], 13 | plugins: [ 14 | '@babel/plugin-syntax-jsx', 15 | // babel-plugin-import 按需加载ant-design-vue 16 | [ 17 | 'import', 18 | { 19 | libraryName: 'ant-design-vue', 20 | libraryDirectory: 'es', 21 | style: true // 加载less文件,再通过vue.config.js配置less-loader来定制主题 22 | } 23 | ], 24 | '@babel/plugin-proposal-optional-chaining' // ts可选链语法支持 25 | ] 26 | } 27 | 28 | if (isPro) { 29 | config.plugins.push(installCommon) 30 | } 31 | 32 | module.exports = config 33 | -------------------------------------------------------------------------------- /build/tag.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # 指定分支,默认为 develop 分支(yarn release develop) 4 | branch=$1 5 | if [ -z $branch ] 6 | then 7 | branch=develop 8 | fi 9 | 10 | read -p "确定将 $branch 分支合并到 master 分支并发布? (y/n)" -n 1 -r 11 | echo 12 | 13 | if [[ $REPLY =~ ^[Yy]$ ]] 14 | then 15 | # 合并代码到 master 分支 16 | git checkout master 17 | git merge $branch 18 | 19 | # 指定上线版本,如 1.0.0,最后会生成 tag v1.0.0 20 | echo "输入当前上线的版本号: " 21 | read VERSION 22 | 23 | read -p "当前版本 $VERSION - are you sure? (y/n)" -n 1 -r 24 | echo 25 | 26 | if [[ $REPLY =~ ^[Yy]$ ]] 27 | then 28 | # 修改版本号 29 | yarn version --new-version $VERSION --no-git-tag-version --allow-same-version 30 | 31 | # 生成变更日志, 打 tag 32 | yarn changelog 33 | git add . 34 | git commit -m "release: $VERSION" --no-verify 35 | git tag v$VERSION 36 | 37 | # 提交代码 38 | git push origin master 39 | git push origin refs/tags/v$VERSION 40 | 41 | # 合并代码到 develop 分支 42 | git checkout develop 43 | git rebase master 44 | git push origin develop 45 | else 46 | echo '取消发布' 47 | fi 48 | 49 | else 50 | echo '取消发布' 51 | fi 52 | 53 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'] 3 | } 4 | -------------------------------------------------------------------------------- /config/default.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventtracing/easyinsight-front/1ccfe5b7a287b6f26b48c9dc6d7d40526d947fc2/config/default.yaml -------------------------------------------------------------------------------- /docs/实时测试.md: -------------------------------------------------------------------------------- 1 | # 1.2.0 2 | > 1.2.0版本需求点有大功能需求点如下

1.增加埋点测试大模块,本期做的是大模块里的第一个模块实时测试
2. 对象管理里页面元素增加了一个路径的字段,用于绑定端路径 3 | 4 |
5 | 6 | ## 实时测试 7 | 该版需要主要是为了帮助客户端人员在埋点还未上线的时候就可以用手机扫码进行端版本需求埋点测试 8 | 9 | ### 列表页 10 | 11 | 1. 常规筛选项 12 | 2. 列表(前端分页) 13 | ``` 14 | 1.规则校验 15 | 不同版本的需求下相同的对象不能选择 16 | 不同端版本的需求不能选择 17 | 不同基线版本的需求不能选择 18 | 2.实时日志 19 | ``` 20 | 扫码进测试详情, 选择对比的需求后点击规则校验会触发后台校验机制, 若是通过的话则展示一个二维码, 所以建立起socket连接, 等待客户端连接成功的消息传回后再进入的测试详情页 21 | 22 | ### 详情页 23 | 24 | 1. 用户终端 25 | 2. 客户端连接状态 26 | 3. 用户连接时间 27 | 4. 该需求或者该对象下埋点的对象以及对应埋点对象的日志条数 28 | 5. 规则校验, 对比客户端传回的埋点对象和需求下定义的对象进行diff比较以此来获取对应的错误信息 29 | 6. 查看实时日志, 不对比 30 | 7. 规则校验和实时日志都是通过socket连接自动传回给web端的 31 | 32 | ## 对象管理 33 | 34 | ### 对象添加编辑查看 35 | 当对象类型是页面类型的时候需要添加一个路由字段, 添加规则如下: 36 | 37 | ``` 38 | 1. 对象oid可以映射多个不同的路由 39 | 2. 同一个路由只能对应同一个oid 40 | 3. Andriod与Iphone的所有路由都放置在同一个输入框里书写,最后取并集即可 41 | 4. 编辑页面元素时可以修改路由信息 42 | ``` -------------------------------------------------------------------------------- /docs/技术文档.md: -------------------------------------------------------------------------------- 1 | # 技术文档 2 | 3 | - vue ^3.2.0 4 | - template/pug 5 | - script setup / optionAPI + TS 6 | - less 7 | - vuex ^4.0.0 8 | - vue-router ^4.0.0 9 | - ant-design-vue ^2.1.7 10 | - vite(dev) 11 | - webpack(pro) 12 | 13 | ## 注意事项 14 | - vite是作为开发环境开发,而为了保证线上稳定性则在生产环境使用webpack,当更改打包配置时需要同步两个配置(vite.config.js/vue.config.js) 15 | - 开发环境遇到页面不断循环刷新的时候,采取以下步骤 16 | - 点击浏览器的x,禁止页面刷新,因为该过程其实是当前浏览器缓存里的eis-token过期,所以在检测登录状态时一直在重定向到aac认证中心页面,aac有防暴力登录的政策,会在一定登录次数后禁止某段时间再次登录,所以需要先禁止页面继续刷新 17 | - 登录到代理环境下对应的线上环境获取到对应的cookie下的eis-token的最新值 18 | - 随后返回刚才的页面将token替换掉然后刷新页面 19 | - 完成 20 | - vue3.x依旧支持optionsAPI的写法 -------------------------------------------------------------------------------- /env.js: -------------------------------------------------------------------------------- 1 | const DOMAIN = { 2 | // 开发环境域名 3 | dev: "localhost", 4 | // 测试环境域名 5 | test: "localhost", 6 | // 线上环境域名 7 | online: "localhost", 8 | }; 9 | 10 | module.exports = { DOMAIN }; 11 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%- title %> 9 | 10 | 11 | 16 |
17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "@vue/cli-plugin-unit-jest/presets/typescript-and-babel", 3 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "vue"], 4 | moduleNameMapper: { 5 | "^@/(.*)$": "/src/$1", 6 | }, 7 | testMatch: ["**/views/**/__tests__/**/unit/EventTracker.spec.ts"], 8 | transformIgnorePatterns: [ 9 | "/node_modules/(?!(ant-design-vue|@babel|@vue|@ant-design|lodash-es)/)", 10 | ], 11 | watchPathIgnorePatterns: ["/node_modules/"], 12 | setupFiles: ["/src/components/register-test.ts"], 13 | snapshotSerializers: ["/node_modules/jest-serializer-vue"], 14 | }; 15 | -------------------------------------------------------------------------------- /mock/index.js: -------------------------------------------------------------------------------- 1 | // usage: https://github.com/xuxihai123/vue-cli-plugin-mock 2 | module.exports = { 3 | 'GET /api/cjd': { 4 | // obj 5 | id: 1, 6 | username: 'kenny', 7 | sex: 6 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventtracing/easyinsight-front/1ccfe5b7a287b6f26b48c9dc6d7d40526d947fc2/public/favicon.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 49 | 50 | 55 | -------------------------------------------------------------------------------- /src/assets/iconfont.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | -------------------------------------------------------------------------------- /src/bus.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'eventemitter3' 2 | 3 | const emitter = new EventEmitter() 4 | export default { 5 | // @ts-ignore 6 | $on: (...args) => emitter.on(...args), 7 | // @ts-ignore 8 | $once: (...args) => emitter.once(...args), 9 | // @ts-ignore 10 | $off: (...args) => emitter.off(...args), 11 | // @ts-ignore 12 | $emit: (...args) => emitter.emit(...args) 13 | } 14 | -------------------------------------------------------------------------------- /src/components/README.md: -------------------------------------------------------------------------------- 1 | # components 2 | 3 | > 项目通用组件 4 | 5 | ## 目录说明 6 | 7 | ``` 8 | |-- ant-design 9 | |-- index.ts antd组件导入 10 | |-- common 通用组件 11 | |-- m-filter-select 支持虚拟滚动的下拉选择组件 12 | |-- u-divider.vue 分割线组件 13 | |-- u-filter-select.vue 筛选组件,常用于列表页筛选 14 | |-- u-single-select.vue 支持虚拟滚动的单选组件 15 | |-- u-tag.vue tag 组件 16 | |-- layout 布局组件 17 | |-- topbar topbar 组件 18 | |-- u-collapse.vue 折叠面板组件 19 | |-- u-info-item.vue 信息 item 组件 20 | |-- u-info.vue 信息组组件,子节点为 u-info-item 21 | |-- u-layout.vue 全局布局组件 22 | |-- u-left-right.vue 左右布局组件 23 | |-- u-sidebar.vue 侧边栏组件 24 | |-- u-topbar.vue 顶部栏组件 25 | |-- modal 弹窗组件 26 | |-- ImagePreviewModal.vue 图片预览弹窗 27 | |-- specific 业务组件 28 | |-- s-editor.vue 富文本组件 29 | |-- s-page-top.vue 页面顶部布局组件 30 | |-- s-table-search.vue 列表页搜索、刷新组件 31 | |-- s-upload-image.vue 图片上传组件(用于坑位创建、编辑) 32 | |-- s-user-select.vue 支持分页搜索的用户选择组件 33 | |-- upload-request.js 图片上传请求封装 34 | |-- register.ts 组件注册 35 | ``` 36 | -------------------------------------------------------------------------------- /src/components/ant-design/icons.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SearchOutlined, 3 | UploadOutlined, 4 | QuestionCircleOutlined, 5 | DownOutlined, 6 | CheckOutlined, 7 | CloseOutlined, 8 | ReloadOutlined, 9 | PlusSquareOutlined, 10 | EditOutlined, 11 | DeleteOutlined, 12 | SyncOutlined, 13 | SaveOutlined, 14 | FileSearchOutlined, 15 | SettingOutlined, 16 | ExclamationCircleOutlined, 17 | PlusOutlined, 18 | LeftOutlined, 19 | RightOutlined 20 | } from '@ant-design/icons-vue' 21 | 22 | export default { 23 | install(Vue) { 24 | Vue.component(SearchOutlined.name, SearchOutlined) 25 | Vue.component(UploadOutlined.name, UploadOutlined) 26 | Vue.component(QuestionCircleOutlined.name, QuestionCircleOutlined) 27 | Vue.component(DownOutlined.name, DownOutlined) 28 | Vue.component(CheckOutlined.name, CheckOutlined) 29 | Vue.component(CloseOutlined.name, CloseOutlined) 30 | Vue.component(ReloadOutlined.name, ReloadOutlined) 31 | Vue.component(PlusSquareOutlined.name, PlusSquareOutlined) 32 | Vue.component(EditOutlined.name, EditOutlined) 33 | Vue.component(DeleteOutlined.name, DeleteOutlined) 34 | Vue.component(SyncOutlined.name, SyncOutlined) 35 | Vue.component(SaveOutlined.name, SaveOutlined) 36 | Vue.component(FileSearchOutlined.name, FileSearchOutlined) 37 | Vue.component(SettingOutlined.name, SettingOutlined) 38 | Vue.component(ExclamationCircleOutlined.name, ExclamationCircleOutlined) 39 | Vue.component(PlusOutlined.name, PlusOutlined) 40 | Vue.component(LeftOutlined.name, LeftOutlined) 41 | Vue.component(RightOutlined.name, RightOutlined) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/baseInstall/m-image-preivew/index.js: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import Preview from "./index.vue"; 3 | import Iconfont from "../../common/m-icon-font/index.vue"; 4 | 5 | function genSingleton() { 6 | const PreviewContructor = createApp(Preview); 7 | PreviewContructor.component(Iconfont.name, Iconfont); 8 | const el = document.createElement("div"); 9 | document.body.appendChild(el); 10 | return PreviewContructor.mount(el); 11 | } 12 | 13 | export default { 14 | install(Vue) { 15 | function PreviewEntity(options) { 16 | let instance = genSingleton(); 17 | Object.assign(instance._.props, options); 18 | instance.show(true); 19 | instance.$watch("visible", (n) => { 20 | if (!n) { 21 | document.body.removeChild(instance.$el.parentNode); 22 | instance = null; 23 | } 24 | }); 25 | } 26 | 27 | Vue.config.globalProperties.$preview = PreviewEntity; 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /src/components/common/index.ts: -------------------------------------------------------------------------------- 1 | import UDividerDash from './u-divider-dash.vue' 2 | import UDivider from './u-divider.vue' 3 | import UFilterSelect from './u-filter-select.vue' 4 | import USingleSelect from './u-single-select.vue' 5 | import USpinner from './u-spinner.vue' 6 | import UTag from './u-tag.vue' 7 | import UDrawFooter from './m-drawer-footer/index.vue' 8 | import UIconFont from './m-icon-font/index.vue' 9 | import RowSelectionTable from './m-rowselect-table/index.vue' 10 | 11 | export default { 12 | install(Vue) { 13 | Vue.component(UDividerDash.name, UDividerDash) 14 | Vue.component(UDivider.name, UDivider) 15 | Vue.component(UFilterSelect.name, UFilterSelect) 16 | Vue.component(USingleSelect.name, USingleSelect) 17 | Vue.component(USpinner.name, USpinner) 18 | Vue.component(UTag.name, UTag) 19 | Vue.component(UDrawFooter.name, UDrawFooter) 20 | Vue.component(UIconFont.name, UIconFont) 21 | Vue.component(RowSelectionTable.name, RowSelectionTable) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/common/m-drawer-footer/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 35 | 36 | 50 | -------------------------------------------------------------------------------- /src/components/common/m-filter-select/menu/menu.less: -------------------------------------------------------------------------------- 1 | @hover-color: #e6efff; 2 | 3 | .mm-menu { 4 | min-width: 140px; 5 | width: auto; 6 | max-width: 400px; 7 | max-height: 330px; 8 | padding: 4px 0; 9 | border-radius: 2px; 10 | background: #fff; 11 | box-shadow: 0 2px 8px rgba(0,0,0,.15); 12 | z-index: 1050; 13 | overflow-y: auto; 14 | .mm-menu-item { 15 | height: 32px; 16 | line-height: 32px; 17 | padding: 0 20px 0 10px; 18 | color: @text-color; 19 | white-space: nowrap; 20 | position: relative; 21 | overflow: hidden; 22 | word-wrap: normal; 23 | white-space: nowrap; 24 | text-overflow: ellipsis; 25 | cursor: pointer; 26 | text-align: left; 27 | &:not(.disabled):hover { 28 | background-color: @hover-color; 29 | } 30 | &.selected:not(.hover) { 31 | font-weight: 600; 32 | background-color: #fafafa; 33 | padding-right: 32px; 34 | } 35 | .anticon-check { 36 | position: absolute; 37 | right: 10px; 38 | top: 50%; 39 | transform: translateY(-50%); 40 | color: @primary-color; 41 | } 42 | } 43 | .ant-divider-horizontal, 44 | .ant-divider { 45 | margin: 0; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/common/m-filter-select/menu/type.ts: -------------------------------------------------------------------------------- 1 | export interface SelectDropdownMenuData { 2 | key: string | number 3 | value: string 4 | isAll?: boolean // 是否是顶部的全部筛选项,如果是全部,则和清空选择的效果一样 5 | divider?: boolean // 是否显示是divider, 如果是divider,则该项的key和value值不会写在里面 6 | current?: boolean // 是否是当前用户快捷选项,如果是则选中里面的当前用户 7 | customItem?: boolean // 是否不参与全选等快捷选项 8 | name?: string // 一般用于定义回填属性 9 | [key: string]: any 10 | } 11 | -------------------------------------------------------------------------------- /src/components/common/m-icon-font/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 23 | 24 | 33 | -------------------------------------------------------------------------------- /src/components/common/u-divider-dash.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/components/common/u-divider.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | 17 | 25 | -------------------------------------------------------------------------------- /src/components/common/u-mutiple-select.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 50 | -------------------------------------------------------------------------------- /src/components/common/u-single-select.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 50 | -------------------------------------------------------------------------------- /src/components/common/u-spinner.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 25 | -------------------------------------------------------------------------------- /src/components/common/u-tag.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | 25 | 36 | -------------------------------------------------------------------------------- /src/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | import ULeftRight from './u-left-right.vue' 2 | 3 | export default { 4 | install(Vue) { 5 | Vue.component(ULeftRight.name, ULeftRight) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/components/layout/topbar/assets/toggle-bar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/layout/topbar/components/IconFont.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 25 | 26 | 35 | -------------------------------------------------------------------------------- /src/components/layout/topbar/components/Link.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 42 | -------------------------------------------------------------------------------- /src/components/layout/topbar/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | // eslint-disable-next-line 3 | import { default as TopBar } from './Topbar.vue' 4 | 5 | // Declare install function executed by Vue.use() 6 | function install(Vue) { 7 | if (install.installed) return 8 | install.installed = true 9 | Vue.component('TopBar', TopBar) 10 | } 11 | 12 | // Create module definition for Vue.use() 13 | const plugin = { 14 | install 15 | } 16 | 17 | // Auto-install when vue is found (eg. in browser via 16 | -------------------------------------------------------------------------------- /src/components/layout/topbar/quick-navigation/SortableItem.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /src/components/layout/topbar/styles/global.less: -------------------------------------------------------------------------------- 1 | @import './var.less'; 2 | 3 | *, 4 | *::before, 5 | *::after { 6 | box-sizing: border-box; 7 | } 8 | 9 | input[type='checkbox'] { 10 | padding: 0; 11 | touch-action: manipulation; 12 | } 13 | 14 | input, 15 | button, 16 | select, 17 | optgroup, 18 | textarea { 19 | margin: 0; 20 | color: inherit; 21 | font-size: inherit; 22 | font-family: inherit; 23 | line-height: inherit; 24 | } 25 | 26 | .m-topbar, 27 | .m-topbar-dropdown { 28 | ul, 29 | li { 30 | margin: 0; 31 | padding: 0; 32 | list-style: none; 33 | } 34 | a, a:focus { 35 | text-decoration: none; 36 | } 37 | a.a-link, a.spa-link { 38 | display: block; 39 | padding: 0 10px; 40 | color: @topbar-text-color !important; 41 | &:not(.disabled):hover { 42 | color: @topbar-text-hover-color !important; 43 | } 44 | 45 | &.active, &.router-link-active { 46 | color: @topbar-text-hover-color !important; 47 | } 48 | 49 | &.disabled { 50 | pointer-events: none; 51 | } 52 | } 53 | } 54 | 55 | .topbar-tooltip .ant-tooltip-content { 56 | .ant-tooltip-arrow::before { 57 | background: #3a3e50 !important; 58 | } 59 | .ant-tooltip-inner { 60 | font-size: 12px !important; 61 | padding: 5px 8px !important; 62 | border-radius: 2px !important; 63 | line-height: 1.5 !important; 64 | min-height: auto !important; 65 | background: #3a3e50 !important; 66 | box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.16) !important; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/components/layout/topbar/styles/var.less: -------------------------------------------------------------------------------- 1 | @header-height: 48px; 2 | @primary-color: #103ffa; // 主色 3 | @topbar-text-color: rgba(255, 255, 255, 0.72); // topbar 文本色 4 | @topbar-text-hover-color: #fff; // topbar hover 文本色 5 | @topbar-background-color-secondary: #3a3e50; // 用户操作、项目集群等面板的背景色 6 | @topbar-text-color-base: #787890; // 导航 panel 文本色 7 | @topbar-icon-color: #d8d9e3; // 产品 logo 默认颜色 8 | -------------------------------------------------------------------------------- /src/components/layout/topbar/type.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 项目信息 3 | */ 4 | export interface ProductItem { 5 | name: string 6 | id: number | string 7 | } 8 | 9 | /** 10 | * 项目、集群切换信息 11 | */ 12 | export type SwitchInfo = ProductItem 13 | 14 | export interface NavigationItem { 15 | key: string // 子产品唯一标识符 16 | category: string // 组类别 17 | order: number // 组排序 18 | name: string // 子产品名 19 | iconName: string | Function // 图标名或渲染函数 20 | url: string // 子产品地址 21 | prefixIcon?: string // 后缀图标,逗号分隔:'new' 22 | clickAction?: 0 | 1 // 鼠标点击后的行为,0: 当前页面跳转,1: 新开页面跳转 23 | } 24 | 25 | export interface NavigationGroup { 26 | category: string 27 | order: number 28 | navItems: NavigationItem[] 29 | } 30 | 31 | export interface OuterLinkItem { 32 | name: string // 模块名 33 | url: string // 地址 34 | prefixIcon: string // 后缀图标,逗号分隔:'new' 35 | } 36 | 37 | export type UserActionItem = 38 | | { 39 | type: 'link' 40 | name: string 41 | url: string 42 | clickAction?: 0 | 1 // 鼠标点击后的行为,0: 当前页面跳转,1: 新开页面跳转 43 | } 44 | | { 45 | type: 'button' 46 | name: string 47 | onClick: Function 48 | } 49 | -------------------------------------------------------------------------------- /src/components/layout/topbar/utils/loadJs.ts: -------------------------------------------------------------------------------- 1 | const customCache = new Set() 2 | 3 | export default function loadJs(scriptUrl: string) { 4 | if ( 5 | typeof document !== 'undefined' && 6 | typeof window !== 'undefined' && 7 | typeof document.createElement === 'function' && 8 | typeof scriptUrl === 'string' && 9 | scriptUrl.length && 10 | !customCache.has(scriptUrl) 11 | ) { 12 | const script = document.createElement('script') 13 | script.setAttribute('src', scriptUrl) 14 | script.setAttribute('data-namespace', scriptUrl) 15 | customCache.add(scriptUrl) 16 | document.body.appendChild(script) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/layout/u-collapse.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 33 | 34 | 64 | -------------------------------------------------------------------------------- /src/components/layout/u-info.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 36 | -------------------------------------------------------------------------------- /src/components/layout/u-layout.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 31 | 32 | 50 | -------------------------------------------------------------------------------- /src/components/layout/u-left-right.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 31 | -------------------------------------------------------------------------------- /src/components/layout/u-sidebar.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 33 | 34 | 39 | -------------------------------------------------------------------------------- /src/components/register-test.ts: -------------------------------------------------------------------------------- 1 | import { config } from '@vue/test-utils' 2 | import AntDesign from './ant-design/index' 3 | import BasicComponents from '@/ndsc-vue3/basic-components/lib' 4 | import '@/ndsc-vue3/basic-components/lib/style/index.less' 5 | import Preview from '@/components/baseInstall/m-image-preivew/index.js' 6 | import Icons from './ant-design/icons' 7 | import JsonViewer from 'vue3-json-viewer' 8 | import Register from './common' 9 | import Layout from './layout' 10 | import Specific from './specific' 11 | import { RouterLink } from 'vue-router' 12 | import { Menu, Form, Select, Radio } from 'ant-design-vue' 13 | 14 | const RadioGroup = Radio.Group 15 | const MenuItem = Menu.Item 16 | const FormItem = Form.Item 17 | const SelectOptions = Select.Option 18 | const Install = { 19 | use(component) { 20 | this.component(component.name, component) 21 | }, 22 | component(name, component) { 23 | config.global.components[name] = component 24 | }, 25 | directive(name, directive) { 26 | config.global.directives[name] = directive 27 | }, 28 | config: { 29 | globalProperties: {} 30 | } 31 | } 32 | 33 | AntDesign(Install) 34 | Preview.install(Install) 35 | BasicComponents.install(Install) 36 | Icons.install(Install) 37 | JsonViewer.install(Install) 38 | Register.install(Install) 39 | Layout.install(Install) 40 | Specific.install(Install) 41 | Install.use(RouterLink) 42 | Install.use(MenuItem) 43 | Install.use(FormItem) 44 | Install.use(RadioGroup) 45 | Install.directive('auth', { 46 | mounted() { 47 | return true 48 | } 49 | }) 50 | 51 | // @ts-ignore 52 | window.matchMedia = function () { 53 | return { 54 | addListener() { 55 | return undefined 56 | }, 57 | removeListener() { 58 | return undefined 59 | } 60 | } 61 | } 62 | config.global.mocks.$get = function () { 63 | return 'get' 64 | } 65 | config.global.components[SelectOptions.displayName] = SelectOptions 66 | -------------------------------------------------------------------------------- /src/components/register.ts: -------------------------------------------------------------------------------- 1 | import AntDesign from './ant-design/index' 2 | import BasicComponents from '@/ndsc-vue3/basic-components/lib' 3 | import '@/ndsc-vue3/basic-components/lib/style/index.less' 4 | import Preview from '@/components/baseInstall/m-image-preivew/index.js' 5 | import Icons from './ant-design/icons' 6 | import JsonViewer from 'vue3-json-viewer' 7 | 8 | export default function Register(Vue) { 9 | Vue.use(AntDesign) 10 | Vue.use(Preview) 11 | Vue.use(BasicComponents) 12 | Vue.use(Icons) 13 | Vue.use(JsonViewer) 14 | /** 15 | * 使用 require.context 批量注册组件注意点 16 | 1. 组件需指定 name 属性, 如: 17 | 23 | 2. 注册时使用 component.options.name,component.name 默认是class名称,打包后会被压缩,名称会变化 24 | */ 25 | // UI组件 26 | 27 | const commonContext = require.context('./common', true, /\.vue$/) 28 | 29 | commonContext.keys().forEach((key) => { 30 | const component = commonContext(key).default 31 | Vue.component(component.name, component) 32 | }) // 布局组件 33 | 34 | const layoutContext = require.context('./layout', false, /\.vue$/) 35 | 36 | layoutContext.keys().forEach((key) => { 37 | const component = layoutContext(key).default 38 | Vue.component(component.name, component) 39 | }) // 业务组件 40 | 41 | const specificContext = require.context('./specific', false, /\.vue$/) 42 | 43 | specificContext.keys().forEach((key) => { 44 | const component = specificContext(key).default 45 | Vue.component(component.name, component) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /src/components/specific/index.ts: -------------------------------------------------------------------------------- 1 | import SUserSelect from './s-user-select.vue' 2 | 3 | export default { 4 | install(Vue) { 5 | Vue.component(SUserSelect.name, SUserSelect) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/components/specific/s-page-top.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 31 | 32 | 37 | -------------------------------------------------------------------------------- /src/components/specific/s-table-search.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 49 | -------------------------------------------------------------------------------- /src/middler/README.md: -------------------------------------------------------------------------------- 1 | # middler文件夹需知 2 | > middler文件夹是给打包编译时做自动转换处理的文件包 3 | 4 | ## 前言 5 | 背景条件是目前项目开发打包工具迁移到了vite,但是生产环境考虑到线上稳定性还是依旧沿用了webpack打包编译方式 6 | 7 | ## babelPlugin 8 | 由于vite的全局注册API与webpack具有差异,故分别编写了一份注册文件,installCommon.js在打包编译的时候根据当前打包软件来区别使用哪个注册文件 9 | 10 | ## vitePlugin 11 | 由于vite已经放弃了es5的转译功能,支持的浏览器是es6及其以上的版本,所以vite舍弃掉了babel,然后这里写的自定义的vitePlugin,用来模拟babel的转译 12 | 13 | #### antDesignVueImport 14 | 作用: 将ant-design-vue按需加载 15 | 16 | #### injectAction 17 | 作用: 由于ant-design-vue1.x存在浏览器esm兼容问题,所以自定义插件来做处理,现在升级到2.x可以不再使用 18 | 19 | 20 | ## 结束 21 | 其余的文件暂时没有用到,可以忽略 -------------------------------------------------------------------------------- /src/middler/babelPlugin/FunctionInstrumentation/AuthoirtyFeat.js: -------------------------------------------------------------------------------- 1 | const { declare } = require("@babel/helper-plugin-utils"); 2 | 3 | module.exports = declare(function (api) { 4 | api.assertVersion("^7.0.0"); 5 | 6 | return { 7 | visitor: { 8 | Program() { 9 | // something 10 | }, 11 | }, 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /src/middler/babelPlugin/installCommon.js: -------------------------------------------------------------------------------- 1 | const { declare } = require('@babel/helper-plugin-utils') 2 | 3 | module.exports = declare(function (api) { 4 | api.assertVersion('^7.0.0') 5 | return { 6 | name: 'babel-plugin-replace-import-meta', 7 | visitor: { 8 | ImportDeclaration(path) { 9 | const { source } = path.node 10 | if (source.value === './components/register-vite') { 11 | source.value = './components/register' 12 | } 13 | } 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /src/middler/vitePlugin/authorityFeat.js: -------------------------------------------------------------------------------- 1 | // const babelParser = require('@babel/parser') 2 | // const traverse = require('@babel/traverse').default 3 | // const generator = require('@babel/generator').default 4 | 5 | function authorityFeat() { 6 | return { 7 | name: 'antd-vite-authority-feat-plugin', 8 | transform(code) { 9 | return { 10 | code 11 | } 12 | } 13 | } 14 | } 15 | module.exports = authorityFeat 16 | authorityFeat.default = authorityFeat 17 | -------------------------------------------------------------------------------- /src/middler/vitePlugin/injectAntIcon.js: -------------------------------------------------------------------------------- 1 | const babelParser = require('@babel/parser') 2 | const traverse = require('@babel/traverse').default 3 | const generator = require('@babel/generator').default 4 | 5 | function antdViteImportPlugin() { 6 | return { 7 | name: 'antd-vite-modeify-icon-plugin', 8 | transform(code) { 9 | if (code.includes('@ant-design')) { 10 | const ast = babelParser.parse(code, { 11 | sourceType: 'unambiguous' 12 | }) 13 | 14 | traverse(ast, { 15 | FunctionDeclaration(path) { 16 | const declarationPath = path.get('declaration').parentPath 17 | if (declarationPath.node.id.name === 'withSuffix') { 18 | const casesPath = declarationPath.get('body.body.0.cases') 19 | const replaceAst = casesPath[0].node.consequent 20 | casesPath[3].node.consequent = replaceAst 21 | } 22 | } 23 | }) 24 | 25 | return { 26 | code: generator(ast).code, 27 | map: null 28 | } 29 | } 30 | return { 31 | code, 32 | map: null 33 | } 34 | } 35 | } 36 | } 37 | module.exports = antdViteImportPlugin 38 | antdViteImportPlugin.default = antdViteImportPlugin 39 | -------------------------------------------------------------------------------- /src/middler/webpackLoader/authorityFeat.js: -------------------------------------------------------------------------------- 1 | // const compiler = require('vue-template-compiler') 2 | 3 | module.exports = function (code) { 4 | return code; 5 | }; 6 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/s-sync-button/index.ts: -------------------------------------------------------------------------------- 1 | import SSyncButton from './s-sync-button.vue' 2 | 3 | SSyncButton.install = function (Vue) { 4 | Vue.component(SSyncButton.options.name, SSyncButton) 5 | } 6 | 7 | export default SSyncButton 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/s-sync-button/s-sync-button.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 32 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-copy/index.ts: -------------------------------------------------------------------------------- 1 | import UCopy from './u-copy.vue' 2 | 3 | UCopy.install = function (Vue) { 4 | Vue.component(UCopy.options.name, UCopy) 5 | } 6 | 7 | export default UCopy 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-copy/u-copy.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 42 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-drawer/index.ts: -------------------------------------------------------------------------------- 1 | import UDrawer from './u-drawer.vue' 2 | 3 | UDrawer.install = function (Vue) { 4 | Vue.component(UDrawer.options.name, UDrawer) 5 | } 6 | 7 | export default UDrawer 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-hint-tooltip/index.ts: -------------------------------------------------------------------------------- 1 | import UHintTooltip from './u-hint-tooltip.vue' 2 | 3 | UHintTooltip.install = function (Vue) { 4 | Vue.component(UHintTooltip.options.name, UHintTooltip) 5 | } 6 | 7 | export default UHintTooltip 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-icon-button/index.ts: -------------------------------------------------------------------------------- 1 | import UIconButton from './u-icon-button.vue' 2 | 3 | UIconButton.install = function (Vue) { 4 | Vue.component(UIconButton.options.name, UIconButton) 5 | } 6 | 7 | export default UIconButton 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-icon-font/index.ts: -------------------------------------------------------------------------------- 1 | import UIconFont from './u-icon-font.vue' 2 | 3 | UIconFont.install = function (Vue) { 4 | Vue.component(UIconFont.options.name, UIconFont) 5 | } 6 | 7 | export default UIconFont 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-icon-font/u-icon-font.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 33 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-modal/index.ts: -------------------------------------------------------------------------------- 1 | import UModal from './u-modal.vue' 2 | 3 | UModal.install = function (Vue) { 4 | Vue.component(UModal.options.name, UModal) 5 | } 6 | 7 | export default UModal 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-modal/u-modal.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 51 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-status-icon/index.ts: -------------------------------------------------------------------------------- 1 | import UStatusIcon from './u-status-icon.vue' 2 | 3 | UStatusIcon.install = function (Vue) { 4 | Vue.component(UStatusIcon.options.name, UStatusIcon) 5 | } 6 | 7 | export default UStatusIcon 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-status-icon/u-status-icon.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 40 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-text-button/index.ts: -------------------------------------------------------------------------------- 1 | import UTextButton from './u-text-button.vue' 2 | 3 | UTextButton.install = function (Vue) { 4 | Vue.component(UTextButton.options.name, UTextButton) 5 | } 6 | 7 | export default UTextButton 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-text-button/u-text-button.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 35 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/components/u-text-tooltip/index.ts: -------------------------------------------------------------------------------- 1 | import UTextTooltip from './u-text-tooltip.vue' 2 | 3 | UTextTooltip.install = function (Vue) { 4 | Vue.component(UTextTooltip.options.name, UTextTooltip) 5 | } 6 | 7 | export default UTextTooltip 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/directives/v-click-outside/index.ts: -------------------------------------------------------------------------------- 1 | import VClickOutside from './v-click-outside' 2 | 3 | export default { 4 | name: 'click-outside', 5 | install: function (Vue: any) { 6 | Vue.directive('click-outside', VClickOutside) 7 | }, 8 | ...VClickOutside 9 | } 10 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/directives/v-click-outside/v-click-outside.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | const clear = (el) => { 3 | if (el._clickOutsideHandler) { 4 | document.removeEventListener("click", el._clickOutsideHandler); 5 | } 6 | delete el._clickOutsideHandler; 7 | }; 8 | 9 | const bind = (el, value) => { 10 | el._clickOutsideHandler = function clickOutsideHandle(e) { 11 | if (el.contains(e.target)) return false; 12 | 13 | if (value && typeof value === "function") { 14 | value(e); 15 | } 16 | }; 17 | setTimeout(() => { 18 | document.addEventListener("click", el._clickOutsideHandler); 19 | }); 20 | }; 21 | 22 | export default { 23 | mounted(el, { value } = {} as any) { 24 | clear(el); 25 | bind(el, value); 26 | }, 27 | update(el, { value, oldValue } = {} as any) { 28 | if (value === oldValue) return; 29 | 30 | clear(el); 31 | bind(el, value); 32 | }, 33 | unmounted(el) { 34 | clear(el); 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/directives/v-copy/index.ts: -------------------------------------------------------------------------------- 1 | import VCopy from './v-copy' 2 | 3 | export default { 4 | name: 'copy', 5 | install: function (Vue: any) { 6 | Vue.directive('copy', VCopy) 7 | }, 8 | ...VCopy 9 | } 10 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/directives/v-copy/v-copy.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { copy } from "../../utils/clipboard"; 3 | import { message } from "ant-design-vue"; 4 | 5 | const clear = (el) => { 6 | if (el._clickHanlder) { 7 | el.removeEventListener("click", el._clickHanlder); 8 | } 9 | delete el._clickHanlder; 10 | }; 11 | 12 | const bind = (el, value) => { 13 | el._clickHanlder = function clickHandler() { 14 | const disabled = el.getAttribute("disabled"); 15 | if (disabled !== null) return; 16 | 17 | if (value) { 18 | const success = copy(value); 19 | if (success) { 20 | message.success("复制成功!"); 21 | } 22 | } 23 | }; 24 | el.addEventListener("click", el._clickHanlder); 25 | }; 26 | 27 | export default { 28 | mounted(el, binding) { 29 | clear(el); 30 | bind(el, binding?.value); 31 | }, 32 | updated(el, { value, oldValue } = {} as any) { 33 | if (value === oldValue) return; 34 | 35 | clear(el); 36 | bind(el, value); 37 | }, 38 | unmounted(el) { 39 | clear(el); 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/directives/v-drag-size/index.ts: -------------------------------------------------------------------------------- 1 | import VDragSize from './v-drag-size' 2 | import './v-drag-size.less' 3 | 4 | export default { 5 | name: 'drag-size', 6 | install: function (Vue: any) { 7 | Vue.directive('drag-size', VDragSize) 8 | }, 9 | ...VDragSize 10 | } 11 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/directives/v-drag-size/v-drag-size.less: -------------------------------------------------------------------------------- 1 | .v-drag-size-anchor { 2 | position: absolute; 3 | z-index: 999; 4 | width: 10px; 5 | height: 28px; 6 | background-color: #e0e2e6; 7 | border-radius: 2px; 8 | &::before { 9 | content: ''; 10 | width: 2px; 11 | height: 2px; 12 | display: block; 13 | position: absolute; 14 | box-shadow: 4px 8px 0 #4e5973, 4px 13px 0 #4e5973, 4px 18px 0 #4e5973; 15 | border-radius: 50%; 16 | } 17 | &__left, 18 | &__right { 19 | top: 50%; 20 | transform: translateY(-50%); 21 | cursor: ew-resize; 22 | } 23 | &__top, 24 | &__bottom { 25 | left: 50%; 26 | transform: translateX(-50%); 27 | transform: rotate(90deg); 28 | cursor: ns-resize; 29 | } 30 | &__left { 31 | left: -5px; 32 | } 33 | &__right { 34 | right: -5px; 35 | } 36 | &__top { 37 | top: -14px; 38 | } 39 | &__bottom { 40 | bottom: -14px; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/directives/v-ellipsis-title/index.ts: -------------------------------------------------------------------------------- 1 | import VEllipsisTitle from './v-ellipsis-title' 2 | 3 | export default { 4 | name: 'ellipsis-title', 5 | install: function (Vue: any) { 6 | Vue.directive('ellipsis-title', VEllipsisTitle) 7 | }, 8 | ...VEllipsisTitle 9 | } 10 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/directives/v-ellipsis-title/v-ellipsis-title.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | mounted(el: HTMLElement, binding: any) { 3 | const lineCount = 4 | !isNaN(Number(binding.arg)) && Number(binding.arg) > 1 ? binding.arg : 1; 5 | if (lineCount === 1) { 6 | el.style.overflow = "hidden"; 7 | el.style.whiteSpace = "nowrap"; 8 | el.style.textOverflow = "ellipsis"; 9 | } else { 10 | el.style.overflow = "hidden"; 11 | el.style.textOverflow = "ellipsis"; 12 | el.style.display = `-webkit-box`; 13 | el.style["-webkit-line-clamp"] = lineCount; 14 | el.style["-webkit-box-orient"] = "vertical"; 15 | } 16 | requestAnimationFrame(() => { 17 | if ( 18 | el.scrollWidth > el.offsetWidth || 19 | el.scrollHeight > el.offsetHeight 20 | ) { 21 | const title = binding?.value || el.innerText; 22 | el.setAttribute("title", title); 23 | } else { 24 | el.removeAttribute("title"); 25 | } 26 | }); 27 | }, 28 | update(el: HTMLElement, binding: any) { 29 | requestAnimationFrame(() => { 30 | if ( 31 | el.scrollWidth > el.offsetWidth || 32 | el.scrollHeight > el.offsetHeight 33 | ) { 34 | el.setAttribute("title", binding?.value || el.innerText); 35 | } else { 36 | el.removeAttribute("title"); 37 | } 38 | }); 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/directives/v-input-number/index.ts: -------------------------------------------------------------------------------- 1 | import VInputNumber from './v-input-number' 2 | 3 | export default { 4 | name: 'input-number', 5 | install: function (Vue: any) { 6 | Vue.directive('input-number', VInputNumber) 7 | }, 8 | ...VInputNumber 9 | } 10 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | 6 | declare module 'vue-clipboard2' 7 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/antd.less: -------------------------------------------------------------------------------- 1 | // antd 2 | @import '~ant-design-vue/lib/tooltip/style/index.less'; 3 | @import '~ant-design-vue/lib/icon/style/index.less'; 4 | @import '~ant-design-vue/lib/modal/style/index.less'; 5 | @import '~ant-design-vue/lib/drawer/style/index.less'; -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/index.less: -------------------------------------------------------------------------------- 1 | @import 's-sync-button.less'; 2 | @import 'u-drawer.less'; 3 | @import 'u-icon-button.less'; 4 | @import 'u-icon-font.less'; 5 | @import 'u-status-icon.less'; 6 | @import 'u-text-button.less'; 7 | @import 'u-text-tooltip.less'; 8 | @import 'u-modal.less'; 9 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/s-sync-button.less: -------------------------------------------------------------------------------- 1 | .s-sync-button { 2 | font-size: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/u-drawer.less: -------------------------------------------------------------------------------- 1 | .u-drawer-wrapper.ant-drawer { 2 | &.c-object__container { 3 | .ant-drawer-main { 4 | position: relative; 5 | padding: 0 !important; 6 | overflow: hidden !important; 7 | } 8 | } 9 | 10 | .ant-drawer-wrapper-body { 11 | display: flex; 12 | flex-direction: column; 13 | 14 | .ant-drawer-title { 15 | margin-right: 24px; // 避免压盖 close icon 16 | } 17 | 18 | .ant-drawer-body { 19 | display: flex; 20 | flex-direction: column; 21 | flex: 1; 22 | min-height: 0; 23 | padding: 0 !important; 24 | 25 | .ant-drawer-main { 26 | flex: 1; 27 | min-height: 0; 28 | padding: 24px; 29 | overflow-y: auto; 30 | } 31 | 32 | .ant-drawer-footer { 33 | border-top: 1px solid rgb(233, 233, 233); 34 | padding: 16px 24px; 35 | background: rgb(255, 255, 255); 36 | text-align: left; 37 | } 38 | } 39 | } 40 | 41 | &.ant-drawer-left { 42 | .ant-drawer-footer { 43 | text-align: right !important; 44 | } 45 | } 46 | 47 | &.ant-drawer-bottom { 48 | .ant-drawer-header { 49 | padding: 10px 24px !important; 50 | 51 | .ant-drawer-title { 52 | margin-right: 0 !important; 53 | } 54 | 55 | .ant-drawer-close { 56 | height: 44px !important; 57 | line-height: 44px !important; 58 | } 59 | } 60 | } 61 | } 62 | 63 | .u-drawer-wrapper.edit-diff-dialog .ant-drawer-wrapper-body .ant-drawer-body .ant-drawer-main { 64 | padding-bottom: 0; 65 | margin-bottom: 53px; 66 | overflow: auto; 67 | } -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/u-icon-button.less: -------------------------------------------------------------------------------- 1 | .u-icon-button { 2 | cursor: pointer; 3 | &:hover { 4 | color: #103ffa !important; 5 | } 6 | &__disabled { 7 | cursor: not-allowed !important; 8 | color: #a9a9b8 !important; 9 | } 10 | } 11 | 12 | .u-icon-button-tooltip.ant-tooltip { 13 | .ant-tooltip-inner { 14 | font-size: 12px; 15 | padding: 6px 10px; 16 | border-radius: 4px; 17 | line-height: 1.2; 18 | min-height: auto; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/u-icon-font.less: -------------------------------------------------------------------------------- 1 | .u-icon { 2 | width: 1em; 3 | height: 1em; 4 | fill: currentColor; 5 | overflow: hidden; 6 | } 7 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/u-modal.less: -------------------------------------------------------------------------------- 1 | .u-modal-wrapper .ant-modal { 2 | .ant-modal-footer { 3 | padding: 10px 24px; 4 | } 5 | .ant-modal-footer__left { 6 | position: absolute; 7 | left: 24px; 8 | bottom: 10px; 9 | height: 32px; 10 | line-height: 32px; 11 | } 12 | } -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/u-status-icon.less: -------------------------------------------------------------------------------- 1 | .u-status-icon { 2 | display: inline-flex; 3 | align-items: center; 4 | 5 | &-dot { 6 | position: relative; 7 | width: 8px; 8 | height: 8px; 9 | margin-right: 8px; 10 | border-radius: 50%; 11 | background-color: var(--color); 12 | z-index: 0; 13 | &.wave { 14 | &::before { 15 | content: ''; 16 | .wave-circle(); 17 | animation: circleAnimationIn 2s ease-out; 18 | animation-iteration-count: infinite; 19 | } 20 | &::after { 21 | content: ''; 22 | .wave-circle(); 23 | animation: circleAnimationOut 2s ease-out; 24 | animation-iteration-count: infinite; 25 | } 26 | } 27 | } 28 | 29 | .wave-circle { 30 | position: absolute; 31 | z-index: 1; 32 | width: 16px; 33 | height: 16px; 34 | top: -4px; 35 | left: -4px; 36 | border-radius: 50%; 37 | box-sizing: border-box; 38 | border: 1px solid var(--color); 39 | box-shadow: 1px 1px 10px var(--color); 40 | } 41 | } 42 | 43 | @keyframes circleAnimationIn { 44 | 0% { 45 | transform: scale(0.3); 46 | opacity: 0; 47 | } 48 | 25% { 49 | transform: scale(0.3); 50 | opacity: 0.1; 51 | } 52 | 50% { 53 | transform: scale(0.5); 54 | opacity: 0.3; 55 | } 56 | 75% { 57 | transform: scale(0.8); 58 | opacity: 0.5; 59 | } 60 | 100% { 61 | transform: scale(1); 62 | opacity: 0; 63 | } 64 | } 65 | 66 | @keyframes circleAnimationOut { 67 | 0% { 68 | transform: scale(0.3); 69 | opacity: 0; 70 | } 71 | 25% { 72 | transform: scale(0.3); 73 | opacity: 0.1; 74 | } 75 | 50% { 76 | transform: scale(0.3); 77 | opacity: 0.3; 78 | } 79 | 75% { 80 | transform: scale(0.5); 81 | opacity: 0.5; 82 | } 83 | 100% { 84 | transform: scale(0.8); 85 | opacity: 0; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/u-text-button.less: -------------------------------------------------------------------------------- 1 | .u-text-button { 2 | display: inline-block; 3 | cursor: pointer; 4 | user-select: none; 5 | color: #103ffa; 6 | &.disabled { 7 | cursor: not-allowed; 8 | color: #a9a9b8; 9 | } 10 | & + .u-text-button { 11 | margin-left: 8px; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ndsc-vue3/basic-components/lib/style/u-text-tooltip.less: -------------------------------------------------------------------------------- 1 | .u-text-tooltip.ant-tooltip { 2 | max-width: 500px; 3 | .ant-tooltip-arrow { 4 | display: none; 5 | } 6 | .ant-tooltip-content { 7 | .ant-tooltip-inner { 8 | min-height: unset; 9 | min-width: unset; 10 | padding: 2px 8px; 11 | font-size: 13px; 12 | background-color: #fff; 13 | color: #79809a; 14 | border: 1px solid #dedede; 15 | box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.08); 16 | } 17 | } 18 | } 19 | 20 | .u-text-ellipsis { 21 | overflow: hidden; 22 | text-overflow: ellipsis; 23 | white-space: nowrap; 24 | } 25 | 26 | .u-text-ellipsis-multiline { 27 | overflow: hidden; 28 | text-overflow: ellipsis; 29 | display: -webkit-box !important; 30 | -webkit-line-clamp: var(--count); 31 | -webkit-box-orient: vertical; 32 | } 33 | -------------------------------------------------------------------------------- /src/ndsc-vue3/exception-page/README.md: -------------------------------------------------------------------------------- 1 | # `exception-page` 2 | 3 | > 403、404、无项目异常页组件 4 | 5 | ## Usage 6 | 7 | ``` 8 | import { UPage403, UPage404, UPageNoBiz } from '@/ndsc-vue3/exception-page' 9 | 10 | 11 | ``` 12 | -------------------------------------------------------------------------------- /src/ndsc-vue3/exception-page/lib/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import UPage403 from './u-page403.vue' 3 | import UPage404 from './u-page404.vue' 4 | import UPageNoBiz from './u-page-nobiz.vue' 5 | 6 | let Vue 7 | 8 | function install(_Vue) { 9 | if (Vue && _Vue === Vue) { 10 | return 11 | } 12 | Vue = _Vue 13 | Vue.component('u-page403', UPage403) 14 | Vue.component('u-page404', UPage404) 15 | Vue.component('u-page-nobiz', UPageNoBiz) 16 | } 17 | 18 | const plugin = { 19 | install 20 | } 21 | 22 | // Auto-install when vue is found (eg. in browser via 26 | 27 | 43 | -------------------------------------------------------------------------------- /src/ndsc-vue3/exception-page/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@/ndsc-vue3/exception-page", 3 | "version": "1.1.3", 4 | "description": "403、404、nobiz pages", 5 | "homepage": "", 6 | "license": "MIT", 7 | "main": "lib/index.ts", 8 | "module": "dist/lib/u-exception-page.esm.js", 9 | "types": "dist/types/index.d.ts", 10 | "directories": { 11 | "lib": "lib", 12 | "test": "__tests__" 13 | }, 14 | "files": [ 15 | "lib", 16 | "dist" 17 | ], 18 | "publishConfig": { 19 | "access": "public" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "" 24 | }, 25 | "scripts": { 26 | "prepublish": "npm run build", 27 | "build": "rollup --config", 28 | "test": "echo \"Error: run tests from root\" && exit 1" 29 | }, 30 | "peerDependencies": { 31 | "vue-property-decorator": "^8.4.1" 32 | }, 33 | "devDependencies": { 34 | "rollup": "^2.30.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ndsc-vue3/sidebar/lib/VNode.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | name: 'v-nodes', 4 | functional: true, 5 | props: { 6 | vnodes: { 7 | type: [Object, Array], 8 | } 9 | }, 10 | render: (ctx: any) => { 11 | return ctx.vnodes 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ndsc-vue3/sidebar/lib/components/IconFont.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 32 | 33 | 41 | -------------------------------------------------------------------------------- /src/ndsc-vue3/sidebar/lib/flexbox.css: -------------------------------------------------------------------------------- 1 | .FBH, 2 | .FBV { 3 | display: flex; 4 | } 5 | .FBV { 6 | flex-direction: column; 7 | } 8 | .FBW { 9 | flex-wrap: wrap; 10 | } 11 | .FBAS { 12 | align-items: flex-start; 13 | } 14 | .FBAC { 15 | align-items: center; 16 | } 17 | .FBAE { 18 | align-items: flex-end; 19 | } 20 | .FBAST { 21 | align-items: stretch; 22 | } 23 | .FBAB { 24 | align-items: baseline; 25 | } 26 | .FBJS { 27 | justify-content: flex-start; 28 | } 29 | .FBJC { 30 | justify-content: center; 31 | } 32 | .FBJE { 33 | justify-content: flex-end; 34 | } 35 | .FBJ { 36 | justify-content: space-between; 37 | } 38 | .FBJB { 39 | justify-content: space-between; 40 | } 41 | .FBJA { 42 | justify-content: space-around; 43 | } 44 | .FBAS-M { 45 | align-content: flex-start; 46 | } 47 | .FBAC-M { 48 | align-content: center; 49 | } 50 | .FBAE-M { 51 | align-content: flex-end; 52 | } 53 | .FBAST-M { 54 | align-content: stretch; 55 | } 56 | .FBAB { 57 | align-content: space-between; 58 | } 59 | .FBAA { 60 | align-content: space-around; 61 | } 62 | .FBAS-S { 63 | align-self: flex-start; 64 | } 65 | .FBAC-S { 66 | align-self: center; 67 | } 68 | .FBAE-S { 69 | align-self: flex-end; 70 | } 71 | .FBAB-S { 72 | align-self: baseline; 73 | } 74 | .FBAST-S { 75 | align-self: stretch; 76 | } 77 | .FB1 { 78 | flex: 1; 79 | } 80 | .FB2 { 81 | flex: 2; 82 | } 83 | .FB3 { 84 | flex: 3; 85 | } 86 | .FB4 { 87 | flex: 4; 88 | } 89 | .FB5 { 90 | flex: 5; 91 | } 92 | .FB6 { 93 | flex: 6; 94 | } 95 | .FB7 { 96 | flex: 7; 97 | } 98 | .FB8 { 99 | flex: 8; 100 | } 101 | .FB9 { 102 | flex: 9; 103 | } 104 | .FB10 { 105 | flex: 10; 106 | } 107 | -------------------------------------------------------------------------------- /src/ndsc-vue3/sidebar/lib/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import Sidebar from "./sidebar.vue"; 3 | export * from "./interface"; 4 | 5 | function install(Vue) { 6 | if (install.installed) return; 7 | install.installed = true; 8 | Vue.component("sidebar", Sidebar); 9 | } 10 | 11 | let GlobalVue = null; 12 | 13 | if (typeof window !== "undefined") { 14 | GlobalVue = window.Vue; 15 | } else if (typeof global !== "undefined") { 16 | GlobalVue = (global as any).Vue; 17 | } 18 | 19 | if (GlobalVue) { 20 | GlobalVue.use({ install }); 21 | } 22 | 23 | Sidebar.install = install; 24 | 25 | export default Sidebar; 26 | -------------------------------------------------------------------------------- /src/ndsc-vue3/sidebar/lib/interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 菜单信息 3 | */ 4 | export interface NavListDto { 5 | id: string 6 | name: string 7 | to: string // 路由 8 | hide?: boolean // 是否隐藏 9 | // 支持slot和函数 10 | icon?: any // ant-design icon 名称, 优先级:icon > iconFont 11 | // iconType?: 'antdIcon' | 'svgIcon' | 'iconFont' | 'slot' // icon 的类型,支持 antd 、svgicon、iconFont、自定义slot 12 | children?: NavListDto[] 13 | includeTab?: string[] // 需要菜单高亮的路由标识数组 14 | [propName: string]: any // 组件内部使用 15 | } 16 | 17 | /** 18 | * 菜单key的数组item 19 | */ 20 | export interface MenuIncludeDto { 21 | id: string 22 | tab: string[] // id 下包含的 array 23 | } 24 | 25 | export interface ProjectDto { 26 | // 项目logo的名字 27 | iconName?: string 28 | icon?: any 29 | // 项目名 30 | name: string 31 | to?: string 32 | } 33 | 34 | export interface ConfigDto { 35 | hide: boolean 36 | to: string 37 | name: string 38 | } 39 | 40 | export interface NavConfigDto { 41 | // 项目信息 42 | project?: ProjectDto 43 | // 主菜单信息 44 | menu: NavListDto[] 45 | // 是否业务设置页 46 | isConfigPage?: boolean 47 | // 底部业务设置 48 | config?: ConfigDto 49 | } 50 | -------------------------------------------------------------------------------- /src/ndsc-vue3/sidebar/lib/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ndsc-vue3/sidebar/lib/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /src/ndsc-vue3/sidebar/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@/ndsc-vue3/sidebar' 2 | 3 | export * from '../lib' 4 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/README.md: -------------------------------------------------------------------------------- 1 | # `@/ndsc-vue3/style` 2 | 3 | > 共享 style 4 | 5 | ## Usage 6 | 7 | ```ts 8 | import '@/ndsc-vue3/style' 9 | ``` 10 | 11 | ```less 12 | @import '~@/ndsc-vue3/style/lib/var.less'; 13 | ``` 14 | 15 | `vue.config.js` 16 | 17 | ```js 18 | const { getModifyVars } = require('@/ndsc-vue3/style/lib/var') 19 | 20 | module.exports = { 21 | css: { 22 | loaderOptions: { 23 | less: { 24 | modifyVars: getModifyVars(), 25 | javascriptEnabled: true 26 | } 27 | } 28 | } 29 | //...其他配置 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/ant-design/button.less: -------------------------------------------------------------------------------- 1 | // .ant-btn { 2 | // color: #103ffa; 3 | // border-color: #103ffa; 4 | // } 5 | 6 | // .ant-btn-primary { 7 | // color: #ffffff; 8 | // } 9 | 10 | // .ant-btn-link { 11 | // border-color: transparent; 12 | // } 13 | 14 | // .ant-btn-danger { 15 | // background-color: #f24957; 16 | // color: #ffffff; 17 | // border-color: #f24957; 18 | // } 19 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/ant-design/collapse.less: -------------------------------------------------------------------------------- 1 | .ant-collapse { 2 | &-content { 3 | padding-left: 24px; 4 | } 5 | 6 | &-header { 7 | font-weight: 600; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/ant-design/form.less: -------------------------------------------------------------------------------- 1 | .ant-form-horizontal { 2 | .ant-form-item { 3 | margin-bottom: 20px; 4 | &-label { 5 | text-align: left !important; 6 | margin-left: -10px; 7 | & > label { 8 | margin-left: 10px; 9 | } 10 | } 11 | 12 | &-required { 13 | &::before { 14 | position: absolute; 15 | left: -10px; 16 | top: 1px; 17 | } 18 | } 19 | 20 | .ant-alert { 21 | font-size: 12px; 22 | padding-top: 5px; 23 | padding-bottom: 5px; 24 | border-color: transparent; 25 | 26 | .ant-alert-icon { 27 | top: 8px; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/ant-design/index.less: -------------------------------------------------------------------------------- 1 | @import './button.less'; 2 | @import './collapse.less'; 3 | @import './form.less'; 4 | @import './table.less'; 5 | @import './modal.less'; -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/ant-design/index.ts: -------------------------------------------------------------------------------- 1 | // 引入全局样式 2 | import './index.less' 3 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/ant-design/modal.less: -------------------------------------------------------------------------------- 1 | @import '../var.less'; 2 | 3 | .ant-modal-root{ 4 | .ant-modal-footer{ 5 | .ant-btn{ 6 | padding: 0 17px; 7 | &:not(.ant-btn-primary){ 8 | border-color: @border-color-split; 9 | color: @text-color; 10 | } 11 | } 12 | } 13 | &.ant-modal-confirm{ 14 | .ant-modal-body{ 15 | padding: 24px 16px 16px 24px; 16 | } 17 | .ant-modal-confirm-btns{ 18 | .ant-modal-footer() 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/ant-design/table.less: -------------------------------------------------------------------------------- 1 | .ant-table-thead { 2 | & > tr { 3 | & > th { 4 | padding: 16px 12px; 5 | background: #f1f2f4; 6 | font-size: 14px; 7 | color: #102048; 8 | vertical-align: top; 9 | } 10 | } 11 | } 12 | 13 | .ant-table-tbody { 14 | & > tr { 15 | & > td { 16 | padding: 16px 12px; 17 | font-size: 14px; 18 | color: #79809a; 19 | vertical-align: top; 20 | .table-action { 21 | .ant-btn-link { 22 | height: 20px; 23 | line-height: 1; 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/common.less: -------------------------------------------------------------------------------- 1 | .fill { 2 | height: 100%; 3 | } 4 | 5 | .clearfix { 6 | zoom: 1; 7 | &::before, 8 | &::after { 9 | display: table; 10 | content: ''; 11 | } 12 | &::after { 13 | clear: both; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/flex.less: -------------------------------------------------------------------------------- 1 | /* flex 布局 */ 2 | .flex { 3 | display: flex; 4 | } 5 | 6 | .flex-wrap { 7 | .flex(); 8 | flex-wrap: wrap; 9 | } 10 | 11 | .flex-reverse { 12 | .flex(); 13 | flex-direction: row-reverse 14 | } 15 | 16 | .flex-column { 17 | .flex(); 18 | flex-direction: column; 19 | } 20 | 21 | .flex-ac { 22 | .flex(); 23 | align-items: center; 24 | } 25 | 26 | .flex-as { 27 | .flex(); 28 | align-items: flex-start; 29 | } 30 | 31 | .flex-c { 32 | .flex-ac(); 33 | justify-content: center; 34 | } 35 | 36 | .inline-flex { 37 | display: inline-flex; 38 | } 39 | 40 | .inline-flex-ac { 41 | .inline-flex(); 42 | align-items: center; 43 | } 44 | 45 | .inline-flex-as { 46 | .flex(); 47 | align-items: flex-start; 48 | } 49 | 50 | .inline-flex-c { 51 | .inline-flex-ac(); 52 | justify-content: center; 53 | } 54 | 55 | .flex-1 { 56 | flex: 1; 57 | min-width: 0; 58 | min-height: 0; 59 | } 60 | 61 | .flex-1-w { 62 | flex: 1; 63 | min-width: 0; 64 | } 65 | 66 | .flex-1-h { 67 | flex: 1; 68 | min-height: 0; 69 | } -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/font.less: -------------------------------------------------------------------------------- 1 | .font { 2 | &-lg { 3 | font-size: 16px; 4 | line-height: 24px; 5 | } 6 | 7 | &-md { 8 | font-size: 14px; 9 | line-height: 22px; 10 | } 11 | 12 | &-sm { 13 | font-size: 12px; 14 | line-height: 20px; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/index.less: -------------------------------------------------------------------------------- 1 | @import './var.less'; 2 | @import './flex.less'; 3 | @import './text.less'; 4 | @import './margin.less'; 5 | @import './padding.less'; 6 | @import './common.less'; 7 | @import './font.less'; 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/index.ts: -------------------------------------------------------------------------------- 1 | // 引入全局样式 2 | import './index.less' 3 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/margin.less: -------------------------------------------------------------------------------- 1 | .make-margin(@size) { 2 | &-m-@{size} { 3 | margin: ~'@{size}px'; 4 | } 5 | &-m-lr { 6 | &-@{size} { 7 | margin-left: ~'@{size}px'; 8 | margin-right: ~'@{size}px'; 9 | } 10 | } 11 | &-m-tb { 12 | &-@{size} { 13 | margin-top: ~'@{size}px'; 14 | margin-bottom: ~'@{size}px'; 15 | } 16 | } 17 | &-ml-@{size} { 18 | margin-left: ~'@{size}px'; 19 | } 20 | &-mt-@{size} { 21 | margin-top: ~'@{size}px'; 22 | } 23 | &-mr-@{size} { 24 | margin-right: ~'@{size}px'; 25 | } 26 | &-mb-@{size} { 27 | margin-bottom: ~'@{size}px'; 28 | } 29 | } 30 | 31 | .g { 32 | .make-margin(4); 33 | .make-margin(8); 34 | .make-margin(12); 35 | .make-margin(16); 36 | .make-margin(24); 37 | } 38 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/padding.less: -------------------------------------------------------------------------------- 1 | .make-padding(@size) { 2 | &-p-@{size} { 3 | padding: ~'@{size}px'; 4 | } 5 | &-p-lr { 6 | &-@{size} { 7 | padding-left: ~'@{size}px'; 8 | padding-right: ~'@{size}px'; 9 | } 10 | } 11 | &-p-tb { 12 | &-@{size} { 13 | padding-top: ~'@{size}px'; 14 | padding-bottom: ~'@{size}px'; 15 | } 16 | } 17 | &-pl-@{size} { 18 | padding-left: ~'@{size}px'; 19 | } 20 | &-pt-@{size} { 21 | padding-top: ~'@{size}px'; 22 | } 23 | &-pr-@{size} { 24 | padding-right: ~'@{size}px'; 25 | } 26 | &-pb-@{size} { 27 | padding-bottom: ~'@{size}px'; 28 | } 29 | } 30 | 31 | .g { 32 | .make-padding(4); 33 | .make-padding(8); 34 | .make-padding(12); 35 | .make-padding(16); 36 | .make-padding(24); 37 | } 38 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/text.less: -------------------------------------------------------------------------------- 1 | @import './var.less'; 2 | 3 | .text-link { 4 | cursor: pointer; 5 | user-select: none; 6 | color: @primary-color; 7 | } 8 | 9 | .text-disabled { 10 | color: @disabled-color; 11 | } 12 | 13 | .text-error { 14 | font-size: 12px; 15 | color: @error-color; 16 | } 17 | 18 | .text-secondary { 19 | font-size: 12px; 20 | color: @text-color-secondary; 21 | } 22 | 23 | .text-ellipsis { 24 | white-space: nowrap; 25 | overflow: hidden; 26 | text-overflow: ellipsis; 27 | } 28 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/var.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | /** 5 | * 用于 vue.config.js 获取 less modifyVars 配置 6 | */ 7 | const getModifyVars = function () { 8 | const css = fs.readFileSync(path.join(__dirname, 'var.less'), 'utf-8') 9 | const arr = css.match(/(?<=@)[^;]+(?=;)/g) 10 | const modifyVars = {} 11 | arr.reduce((pre, cur) => { 12 | const vars = cur.split(':') 13 | pre[vars[0].trim()] = vars[1].trim() 14 | return pre 15 | }, modifyVars) 16 | 17 | Object.keys(modifyVars).forEach((key) => { 18 | const value = modifyVars[key] 19 | if (value.startsWith('@')) { 20 | const equalKey = value.slice(1) 21 | modifyVars[key] = modifyVars[equalKey] 22 | } 23 | }) 24 | return modifyVars 25 | } 26 | 27 | module.exports = { 28 | getModifyVars 29 | } 30 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/lib/var.less: -------------------------------------------------------------------------------- 1 | @primary-color: #103ffa; // 全局主色 2 | @link-color: #103ffa; // 链接色 3 | @info-color: #1890ff; // 信息提示色 4 | @success-color: #26bd71; // 成功色 5 | @warning-color: #ffaf0f; // 警告色 6 | @error-color: #f24957; // 错误色 7 | 8 | @font-family: 'Helvetica Neue', Helvetica, Arial, 'Hiragino Sans GB', 9 | 'PingFang SC', 'Microsoft YaHei', tahoma, simsun, '宋体'; 10 | @body-background: #f6f7fa; // 背景色 11 | @heading-color: #102048; // 标题色 12 | @text-color: #79809a; // 正文色 13 | @text-color-secondary: #c0c0ca; // 次文本色(提示文本色) 14 | @border-color-split: #e8e8f0; // 分割线 15 | @border-radius-base: 2px; 16 | 17 | @disabled-color: #a9a9b8; // 失效色 18 | @disabled-bg: #f2f2f5; 19 | 20 | // Table 21 | @table-header-bg: #f1f2f4; // 表头背景色 22 | @table-header-color: @heading-color; // 表头文本色 23 | -------------------------------------------------------------------------------- /src/ndsc-vue3/style/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@/ndsc-vue3/style", 3 | "version": "1.3.4", 4 | "description": "the package of shared style", 5 | "keywords": [ 6 | "style", 7 | "less", 8 | "css" 9 | ], 10 | "license": "MIT", 11 | "main": "lib/index.ts", 12 | "directories": { 13 | "lib": "lib" 14 | }, 15 | "files": [ 16 | "lib" 17 | ], 18 | "publishConfig": { 19 | "access": "public" 20 | }, 21 | "repository": { 22 | "type": "git" 23 | }, 24 | "scripts": { 25 | "test": "echo \"Error: run tests from root\" && exit 1" 26 | }, 27 | "devDependencies": { 28 | "rollup": "^2.10.2", 29 | "eslint": "^6.7.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/README.md: -------------------------------------------------------------------------------- 1 | ## 生成多套主题 css 2 | 3 | 1. `lib/`下开发主题,新增主题文件,一般包括: 4 | 5 | - `index.js`入口文件 6 | - `index.less`引入 antd 样式包、自定义变量`var.less`、`common.less`公共样式包 7 | - `common.less`定义 css 变量提供复用 8 | - `var.less`自定义样式变量 9 | 10 | 2. `webpack.config.js`中`entry`增加主题类型入口。 11 | 12 | 3. `yarn build`生成多套 css,并拷贝到 docs 目录下,供文档站点使用 13 | 14 | 4. 设置版本 15 | 16 | 5. `nenpm publish`发布 17 | 18 | ## 引入 19 | 20 | 1. 安装 21 | `yarn add @/ndsc-vue3/theme` 22 | 23 | 2. `vue.config.js`中配置 24 | 25 | ```js 26 | const CopyWebpackPlugin = require('copy-webpack-plugin') 27 | 28 | module.exports = { 29 | ..., 30 | configureWebpack: config => {} 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventtracing/easyinsight-front/1ccfe5b7a287b6f26b48c9dc6d7d40526d947fc2/src/ndsc-vue3/theme/index.html -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/common/index.less: -------------------------------------------------------------------------------- 1 | .mm-theme-dark, 2 | .mm-theme-light, 3 | .mm-theme-purple { 4 | --primary-color: @primary-color; 5 | --link-color: @link-color; 6 | --info-color: @info-color; 7 | --error-color: @error-color; 8 | --disabled-color: @disabled-color; 9 | --text-color-secondary: @text-color-secondary; 10 | --border-color-base: @border-color-base; 11 | --shadow-color: @shadow-color; 12 | --item-hover-bg: @item-hover-bg; 13 | 14 | // 仅供demo使用,正常项目中会去掉按需加载等 15 | 16 | .ant-btn { 17 | color: @primary-color; 18 | border-color: @primary-color; 19 | } 20 | .ant-btn-primary { 21 | color: #ffffff; 22 | background-color: @primary-color; 23 | } 24 | .ant-btn-link { 25 | border-color: transparent; 26 | } 27 | .ant-btn-danger { 28 | background-color: @error-color; 29 | color: #ffffff; 30 | border-color: @error-color; 31 | } 32 | .ant-btn-primary:hover, 33 | .ant-btn-primary:focus { 34 | background-color: @primary-color; 35 | border-color: @primary-color; 36 | } 37 | 38 | .ant-input:hover { 39 | border-color: @primary-color; 40 | } 41 | .ant-radio-checked::after { 42 | border: 1px solid @primary-color; 43 | } 44 | .ant-radio-checked .ant-radio-inner { 45 | border-color: @primary-color; 46 | } 47 | .ant-radio-inner::after { 48 | background-color: @primary-color; 49 | } 50 | .ant-switch-checked { 51 | background-color: @primary-color; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/dark/common.less: -------------------------------------------------------------------------------- 1 | @import '../common/index.less'; 2 | 3 | .mm-theme-dark { 4 | } 5 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/dark/index.js: -------------------------------------------------------------------------------- 1 | import './index.less' 2 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/dark/index.less: -------------------------------------------------------------------------------- 1 | @import '~ant-design-vue/dist/antd.less'; 2 | @import './var.less'; 3 | @import './common.less'; 4 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/dark/var.less: -------------------------------------------------------------------------------- 1 | @primary-color: #141720; // 全局主色 2 | @link-color: #141720; // 链接色 3 | @info-color: #eb2f96; // 信息提示色 4 | @success-color: #26bd71; // 成功色 5 | @warning-color: #ffaf0f; // 警告色 6 | @error-color: #f24957; // 错误色 7 | 8 | @font-family: 'Helvetica Neue', Helvetica, Arial, 'Hiragino Sans GB', 9 | 'PingFang SC', 'Microsoft YaHei', tahoma, simsun, '宋体'; 10 | @body-background: #722ed1; // 背景色 11 | @heading-color: #141720; // 标题色 12 | @text-color: #722ed1; // 正文色 13 | @text-color-secondary: #722ed1; // 次文本色(提示文本色) 14 | @border-color-split: #722ed1; // 分割线 15 | @border-radius-base: 2px; 16 | 17 | @disabled-color: #a9a9b8; // 失效色 18 | @disabled-bg: #f2f2f5; 19 | 20 | // Table 21 | @table-header-bg: #f9f0ff; // 表头背景色 22 | @table-header-color: @heading-color; // 表头文本色 23 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/light/common.less: -------------------------------------------------------------------------------- 1 | @import '../common/index.less'; 2 | 3 | .mm-theme-light { 4 | } 5 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/light/index.js: -------------------------------------------------------------------------------- 1 | import './index.less' 2 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/light/index.less: -------------------------------------------------------------------------------- 1 | @import '~ant-design-vue/dist/antd.less'; 2 | @import './var.less'; 3 | @import './common.less'; 4 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/light/var.less: -------------------------------------------------------------------------------- 1 | @primary-color: #103ffa; // 全局主色 2 | @link-color: #103ffa; // 链接色 3 | @info-color: #1890ff; // 信息提示色 4 | @success-color: #26bd71; // 成功色 5 | @warning-color: #ffaf0f; // 警告色 6 | @error-color: #f24957; // 错误色 7 | 8 | @font-family: 'Helvetica Neue', Helvetica, Arial, 'Hiragino Sans GB', 9 | 'PingFang SC', 'Microsoft YaHei', tahoma, simsun, '宋体'; 10 | @body-background: #f6f7fa; // 背景色 11 | @heading-color: #102048; // 标题色 12 | @text-color: #79809a; // 正文色 13 | @text-color-secondary: #c0c0ca; // 次文本色(提示文本色) 14 | @border-color-split: #e8e8f0; // 分割线 15 | @border-radius-base: 2px; 16 | 17 | @disabled-color: #a9a9b8; // 失效色 18 | @disabled-bg: #f2f2f5; 19 | 20 | // Table 21 | @table-header-bg: #f1f2f4; // 表头背景色 22 | @table-header-color: @heading-color; // 表头文本色 23 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/purple/common.less: -------------------------------------------------------------------------------- 1 | @import '../common/index.less'; 2 | 3 | .mm-theme-purple { 4 | } 5 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/purple/index.js: -------------------------------------------------------------------------------- 1 | import './index.less' 2 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/purple/index.less: -------------------------------------------------------------------------------- 1 | @import '~ant-design-vue/dist/antd.less'; 2 | @import './var.less'; 3 | @import './common.less'; 4 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/purple/var.less: -------------------------------------------------------------------------------- 1 | @primary-color: #722ed1; // 全局主色 2 | @link-color: #722ed1; // 链接色 3 | 4 | @info-color: #1890ff; // 信息提示色 5 | @success-color: #26bd71; // 成功色 6 | @warning-color: #ffaf0f; // 警告色 7 | @error-color: #f24957; // 错误色 8 | 9 | @font-family: 'Helvetica Neue', Helvetica, Arial, 'Hiragino Sans GB', 10 | 'PingFang SC', 'Microsoft YaHei', tahoma, simsun, '宋体'; 11 | @body-background: #f6f7fa; // 背景色 12 | @heading-color: #102048; // 标题色 13 | @text-color: #79809a; // 正文色 14 | @text-color-secondary: #c0c0ca; // 次文本色(提示文本色) 15 | @border-color-split: #e8e8f0; // 分割线 16 | @border-radius-base: 2px; 17 | 18 | @disabled-color: #a9a9b8; // 失效色 19 | @disabled-bg: #f2f2f5; 20 | 21 | // Table 22 | @table-header-bg: #f1f2f4; // 表头背景色 23 | @table-header-color: @heading-color; // 表头文本色 24 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/utils/ThemeMixin.vue: -------------------------------------------------------------------------------- 1 | 60 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/lib/utils/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eventtracing/easyinsight-front/1ccfe5b7a287b6f26b48c9dc6d7d40526d947fc2/src/ndsc-vue3/theme/lib/utils/index.js -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@/ndsc-vue3/theme", 3 | "version": "1.0.0-1", 4 | "description": "主题", 5 | "main": "index.js", 6 | "files": [ 7 | "dist" 8 | ], 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "dev": "webpack-dev-server --open --mode development", 12 | "build-only": "webpack --mode production", 13 | "build": "yarn build-only && yarn copy-vue && yarn copy-css", 14 | "copy-vue": "node ./scripts/copyVue", 15 | "copy-css": "node ./scripts/copyCSS" 16 | }, 17 | "publishConfig": { 18 | "access": "public" 19 | }, 20 | "repository": { 21 | "type": "git" 22 | }, 23 | "author": "", 24 | "license": "ISC", 25 | "devDependencies": { 26 | "chalk": "^4.1.0", 27 | "clean-webpack-plugin": "^3.0.0", 28 | "css-loader": "^5.1.3", 29 | "cssnano": "^4.1.10", 30 | "extract-text-webpack-plugin": "^3.0.2", 31 | "html-webpack-plugin": "^5.3.1", 32 | "less": "^3.9.0", 33 | "less-loader": "^6.0.0", 34 | "mini-css-extract-plugin": "^1.3.9", 35 | "optimize-css-assets-webpack-plugin": "^5.0.4", 36 | "progress-bar-webpack-plugin": "^2.1.0", 37 | "style-loader": "^2.0.0", 38 | "terser-webpack-plugin": "^5.1.1", 39 | "webpack": "^5.26.3", 40 | "webpack-cli": "^4.5.0", 41 | "webpack-dev-server": "^3.11.2" 42 | }, 43 | "dependencies": { 44 | "ant-design-vue": "^1.7.4", 45 | "fs": "^0.0.1-security", 46 | "vue-property-decorator": "^9.1.2" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/scripts/copyCSS.js: -------------------------------------------------------------------------------- 1 | const { copyFile } = require('../../../scripts/lerna/util.js') 2 | const path = require('path') 3 | const rootPath = path.resolve(__dirname, '../../../') 4 | const publicPath = path.resolve(rootPath, './docs/.vuepress/public') 5 | const fs = require('fs') 6 | 7 | const files = fs.readdirSync(path.resolve(__dirname, '../dist')) 8 | files.forEach((e) => { 9 | if (e.indexOf('.css') > -1) { 10 | copyFile(path.resolve(__dirname, '../dist/' + e), path.resolve(publicPath, e)) 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/ndsc-vue3/theme/scripts/copyVue.js: -------------------------------------------------------------------------------- 1 | const { copyFile } = require('../../../scripts/lerna/util.js') 2 | const path = require('path') 3 | 4 | copyFile( 5 | path.resolve(__dirname, '../lib/utils/ThemeMixin.vue'), 6 | path.resolve(__dirname, '../dist/ThemeMixin.vue') 7 | ) 8 | -------------------------------------------------------------------------------- /src/ndsc-vue3/utils/lib/TableMixin.vue: -------------------------------------------------------------------------------- 1 | 37 | -------------------------------------------------------------------------------- /src/ndsc-vue3/utils/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./common"; 2 | export * from "./instance"; 3 | export * from "./loadJs"; 4 | export * from "./request"; 5 | export * from "./url"; 6 | export * from "./validator"; 7 | 8 | export * from "./vueProto"; 9 | 10 | export { default as TableMixin } from "./TableMixin.vue"; 11 | 12 | export function usePagination() { 13 | const _total = 0; 14 | const _current = 1; 15 | const _pageSizeOptions = ["25", "50", "100"]; 16 | const _pageSize = 25; 17 | 18 | return { 19 | total: _total, 20 | current: _current, 21 | pageSize: _pageSize, 22 | pageSizeOptions: _pageSizeOptions, 23 | showQuickJumper: true, 24 | showSizeChanger: true, 25 | hideOnSinglePage: _total <= Number(_pageSizeOptions[0]), 26 | showTotal: (total: number) => `共 ${total} 条记录`, 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/ndsc-vue3/utils/lib/loadJs.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 动态加载 js 文件 3 | */ 4 | const customCache = new Set() 5 | 6 | /** 7 | * 动态引入 js 8 | * @param scriptUrl 脚本 url 9 | */ 10 | export function loadJs(scriptUrl: string) { 11 | if ( 12 | typeof document !== 'undefined' && 13 | typeof window !== 'undefined' && 14 | typeof document.createElement === 'function' && 15 | typeof scriptUrl === 'string' && 16 | scriptUrl.length && 17 | !customCache.has(scriptUrl) 18 | ) { 19 | const script = document.createElement('script') 20 | script.setAttribute('src', scriptUrl) 21 | script.setAttribute('data-namespace', scriptUrl) 22 | customCache.add(scriptUrl) 23 | document.body.appendChild(script) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ndsc-vue3/utils/lib/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ndsc-vue3/utils/lib/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /src/ndsc-vue3/utils/lib/url.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断 url 是否是绝对路径 3 | * @param url url 字符串 4 | */ 5 | export function isAbsoluteURL(url: string) { 6 | // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). 7 | // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed 8 | // by any combination of letters, digits, plus, period, or hyphen. 9 | return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url) 10 | } 11 | 12 | /** 13 | * 返回基础路径或拼接后的路径 14 | * @param baseURL 基础路径 15 | * @param relativeURL 相对路径 16 | */ 17 | export function combineURLs(baseURL: string, relativeURL: string) { 18 | return relativeURL ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') : baseURL 19 | } 20 | 21 | /** 22 | * 获取url的query参数 23 | * @param name 参数名 24 | * @param url url 字符串 25 | */ 26 | export function getQueryString(name: string, url: string) { 27 | const arr = url.split('?') 28 | if (arr.length) { 29 | const search = arr[arr.length - 1] 30 | const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)') 31 | const r = search.match(reg) 32 | if (r != null) { 33 | return unescape(r[2]) 34 | } 35 | } 36 | return null 37 | } 38 | -------------------------------------------------------------------------------- /src/ndsc-vue3/utils/lib/validator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 校验规则 3 | */ 4 | 5 | /** 6 | * 大于0的整数 7 | */ 8 | export const greaterThan0Validator = { 9 | message: '大于0的整数', 10 | pattern: /^[1-9]\d*$/, 11 | validator: (_rule: any, value: any, callback: any) => { 12 | if (!/^[1-9]\d*$/.test(value)) { 13 | return callback(new Error('此字段为大于0的整数')) 14 | } 15 | callback() 16 | } 17 | } 18 | 19 | /** 20 | * 不小于0的整数 21 | */ 22 | export const noLessThan0Validator = { 23 | message: '不小于0的整数', 24 | pattern: /^[1-9]\d*$|^0$/, 25 | validator: (_rule: any, value: any, callback: any) => { 26 | if (!/^[1-9]\d*$|^0$/.test(value)) { 27 | return callback(new Error('此字段为不小于0的整数')) 28 | } 29 | callback() 30 | } 31 | } 32 | 33 | /** 34 | * 以字母开头 35 | */ 36 | export const beginWithLetterValidator = { 37 | message: '以字母开头', 38 | pattern: /^[a-zA-Z]/, 39 | validator: (_rule: any, value: any, callback: any) => { 40 | if (value && !/^[a-zA-Z]/.test(value)) { 41 | callback(new Error('此字段必须以字母开头')) 42 | } 43 | callback() 44 | } 45 | } 46 | 47 | /** 48 | * 字段包含字母、数字、_ 49 | */ 50 | export const letterValidator = { 51 | message: '字段包含字母、数字、_', 52 | pattern: /^[a-zA-Z0-9_]*$/, 53 | validator: (_rule: any, value: any, callback: any) => { 54 | if (!/^[a-zA-Z0-9_]*$/.test(value)) { 55 | return callback(new Error('此字段只能包含字母、数字、_')) 56 | } 57 | callback() 58 | } 59 | } 60 | 61 | /** 62 | * 字段包含中英文、数字、_ 和 - 63 | */ 64 | export const nameValidator = { 65 | message: '字段包含中英文、数字、_ 和 - ', 66 | pattern: /^[\u4e00-\u9fa5a-zA-Z0-9_-]*$/, 67 | validator: (_rule: any, value: any, callback: any) => { 68 | if (!/^[\u4e00-\u9fa5a-zA-Z0-9_-]*$/.test(value)) { 69 | return callback(new Error('此字段只能包含中英文、数字、_ 和 - ')) 70 | } 71 | callback() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/ndsc-vue3/utils/lib/vueProto.ts: -------------------------------------------------------------------------------- 1 | declare module "@vue/runtime-core" { 2 | interface Vue { 3 | $hasData: (data: any) => boolean; 4 | $get: ( 5 | obj: Record, 6 | arr: string[] | string, 7 | defaultValue: any 8 | ) => any; 9 | } 10 | } 11 | 12 | // 扩展vue 13 | export const vueProto = () => {}; 14 | -------------------------------------------------------------------------------- /src/pv.ts: -------------------------------------------------------------------------------- 1 | interface PVInfoRouteMap { 2 | [props: string]: { 3 | id: string; 4 | data?: Record; 5 | query?: Record; 6 | }; 7 | } 8 | 9 | export const PVInfoMap: PVInfoRouteMap = { 10 | "/tracker/requirement/list": { 11 | id: "page_requirement", 12 | }, 13 | "/tracker/requirement/version": { 14 | id: "page_version", 15 | }, 16 | "/tracker/object/list": { 17 | id: "page_object", 18 | }, 19 | "/tracker/metadata/parameter": { 20 | id: "page_parameter", 21 | }, 22 | "/tracker/metadata/template": { 23 | id: "page_template", 24 | }, 25 | "/tracker/metadata/event": { 26 | id: "page_event", 27 | }, 28 | "/tracker/metadata/terminal": { 29 | id: "page_terminal", 30 | }, 31 | "/test/realtime": { 32 | id: "page_realtime", 33 | }, 34 | "/test/realtime/detail": { 35 | id: "page_realtime_detail", 36 | }, 37 | "/authorityManage/production/info": { 38 | id: "page_product_info", 39 | }, 40 | "/authorityManage/production/member": { 41 | id: "page_product_member", 42 | }, 43 | "/authorityManage/production/manage": { 44 | id: "page_product_role", 45 | }, 46 | "/authorityManage/production/notification": { 47 | id: "page_product_notification", 48 | }, 49 | "/authorityManage/domain/detail": { 50 | id: "page_domain_info", 51 | }, 52 | "/authorityManage/domain/member": { 53 | id: "page_domain_member", 54 | }, 55 | "/authorityManage/domain/product": { 56 | id: "page_domain_product", 57 | }, 58 | "/authorityManage/platform/domain": { 59 | id: "page_platform_domain", 60 | }, 61 | "/tracker/requirement/detail": { 62 | id: "page_requirement_detail", 63 | }, 64 | }; 65 | -------------------------------------------------------------------------------- /src/services/README.md: -------------------------------------------------------------------------------- 1 | # services 2 | 3 | > 项目 api 请求文件 4 | 5 | ## 目录说明 6 | 7 | ``` 8 | |-- app.service.ts 项目集群等请求 9 | |-- event.service.ts 事件类型模块请求 10 | |-- login.service.ts 登录服务 11 | |-- object.service.ts 对象模块请求 12 | |-- parameter.service.ts 参数模块请求 13 | |-- requirement.service.ts 需求模块请求 14 | |-- template.service.ts 参数模板模块请求 15 | |-- terminal.service.ts 终端管理模块请求 16 | |-- version.service.ts 版本管理模块请求 17 | ``` 18 | -------------------------------------------------------------------------------- /src/services/app.service.ts: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | import { AppDto } from "@/types/app.type"; 3 | import { User } from "@/types/common.type"; 4 | 5 | // 获取用户权限菜单列表 6 | export function getAuthorMenu(userEmail): Promise { 7 | return request.get("/et/v1/auth/menu/authorized/list", { 8 | params: { userEmail }, 9 | }); 10 | } 11 | 12 | // 获取用户可选产品 13 | export async function getAppList(): Promise { 14 | return request.get("/eis/v1/domain/getByEmail", { useToken: true }); 15 | } 16 | 17 | // 获取该产品下所有成员 18 | export async function getAllUsers(): Promise { 19 | return request.get("/eis/v1/user/getall"); 20 | } 21 | -------------------------------------------------------------------------------- /src/services/common.service.ts: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | 3 | // 获取用户权限菜单列表 4 | export function getAppManagers(appId): Promise { 5 | return request.get("/et/v1/auth/user/managers", { params: { appId } }); 6 | } 7 | -------------------------------------------------------------------------------- /src/services/login.service.ts: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | import { User, AppDto } from "@/types/app.type"; 3 | import store from "../store/index"; 4 | 5 | interface LoginStatus { 6 | logon: boolean; // 是否登录 7 | localToken: string; 8 | user: User; 9 | domainId: number; 10 | app: AppDto; 11 | } 12 | 13 | class LoginService { 14 | // 检测登录接口,登录成功后返回用户信息 15 | checkLoginIn() { 16 | return request 17 | .get("/check/login", { 18 | useToken: false, 19 | }) 20 | .then((res: LoginStatus) => { 21 | const { logon, localToken, user, domainId, app } = res || {}; 22 | if (logon) { 23 | localToken && store.commit("setLocalToken", localToken); 24 | user && store.commit("setUser", user); 25 | domainId && store.commit("setDomainId", domainId); 26 | app && store.commit("setApp", app); 27 | } 28 | return logon; 29 | }); 30 | } 31 | 32 | login() { 33 | location.href = "/api/login"; 34 | } 35 | 36 | logOut() { 37 | location.href = "/api/logout"; 38 | } 39 | } 40 | 41 | const loginService = new LoginService(); 42 | 43 | export default loginService; 44 | -------------------------------------------------------------------------------- /src/services/record.service.ts: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | 3 | // 获取测试需求 4 | export function getAllRequire() { 5 | return request.get("/eis/reqPool/query/all"); 6 | } 7 | 8 | export function getRecordList(data) { 9 | return request.post("/realtime/test/history/search", data); 10 | } 11 | -------------------------------------------------------------------------------- /src/services/tag.service.ts: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | import { 3 | Pagination, 4 | Search, 5 | PaginationList, 6 | ItemDto, 7 | } from "@/types/common.type"; 8 | 9 | // 获取标签列表 10 | export async function getTagList( 11 | params: Pagination & Search & { type: number } 12 | ): Promise> { 13 | return request.get("/et/v1/tag/list", { params: { type: 1, ...params } }); 14 | } 15 | 16 | // 创建标签 17 | export async function createTag(name: string, type = 1): Promise { 18 | return request.post("/et/v1/tag/create", { type, name }); 19 | } 20 | -------------------------------------------------------------------------------- /src/services/template.service.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import { PaginationList } from '@/types/common.type' 3 | import { 4 | TemplateSimpleDto, 5 | GetTemplateListReq, 6 | NewTemplateReq, 7 | TemplateDetailDto 8 | } from '@/types/template.type' 9 | 10 | export async function getTempalteList( 11 | params: GetTemplateListReq 12 | ): Promise> { 13 | return request.get('/et/v1/template/list', { params, supportCancel: true }) 14 | } 15 | 16 | export async function deleteTemplate(id: number) { 17 | return request.delete(`/et/v1/template/delete?id=${id}`, { 18 | handleError: false 19 | }) 20 | } 21 | 22 | export async function addNewTemplate(data: NewTemplateReq) { 23 | return request.post('/et/v1/template/create', data, { handleError: false }) 24 | } 25 | 26 | export async function updateTemplate(data: NewTemplateReq & { id: number }) { 27 | return request.put('/et/v1/template/edit', data, { handleError: false }) 28 | } 29 | 30 | export async function getTemplateDetail(id: number): Promise { 31 | return request.get(`/et/v1/template/get?id=${id}`) 32 | } 33 | -------------------------------------------------------------------------------- /src/services/terminal.service.ts: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | import { PaginationList } from "@/types/common.type"; 3 | import { 4 | GetTerminalListReq, 5 | TerminalSimpleDto, 6 | NewTerminalReq, 7 | } from "@/types/terminal.type"; 8 | 9 | export async function getTerminalList( 10 | data: GetTerminalListReq 11 | ): Promise> { 12 | return request.post("/et/v1/terminal/list", data); 13 | } 14 | 15 | export async function deleteTerminal(id: number) { 16 | return request.delete(`/et/v1/terminal/delete?id=${id}`, { 17 | handleError: false, 18 | }); 19 | } 20 | 21 | export async function addNewTerminal(data: NewTerminalReq) { 22 | return request.post("/et/v1/terminal/create", data, { handleError: false }); 23 | } 24 | 25 | export async function updateTerminal(data: NewTerminalReq & { id: number }) { 26 | return request.put("/et/v1/terminal/edit", data, { handleError: false }); 27 | } 28 | 29 | export async function getTerminalDetail( 30 | id: number 31 | ): Promise { 32 | return request.get(`/et/v1/terminal/get?id=${id}`); 33 | } 34 | -------------------------------------------------------------------------------- /src/services/terminalVersion.service.ts: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | 3 | // 查看产品各端前置版本列表 4 | export async function getPreTerminalVersion(reqPoolId: number): Promise< 5 | { 6 | terminalId: number; 7 | terminalVersionId: number; 8 | terminalName: string; 9 | terminalVersionName: string; 10 | baseReleaseId: string; 11 | }[] 12 | > { 13 | return request.get("/eis/v2/obj/base/version/get", { params: { reqPoolId } }); 14 | } 15 | 16 | // 有端版本 17 | export function getVersionList(params) { 18 | return request.get("/eis/v2/release/aggregate/tasks/get", { params }); 19 | } 20 | 21 | // 无端版本 22 | export function getNoVersionList(params) { 23 | return request.get("/eis/v2/release/tasks/get", { params }); 24 | } 25 | 26 | // 历史列表 27 | export function getHistoryList(params: { 28 | terminalId: number | string; 29 | ascend: boolean; 30 | }) { 31 | return request.get("/eis/v2/release/tasks/history/get", { params }); 32 | } 33 | 34 | // 发布端版本上线 35 | export function publishVersion(data: { 36 | terminalId: string | number; 37 | terminalVersionId: number; 38 | taskIds: number[]; 39 | }) { 40 | return request.post("/eis/v2/release/releaseByVersion", data); 41 | } 42 | 43 | // 发布任务上线 44 | export function publishTaskVersion(params: { 45 | taskId: number; 46 | terminalId: string | number; 47 | }) { 48 | return request.get("/eis/v2/release/releaseByTask", { params }); 49 | } 50 | -------------------------------------------------------------------------------- /src/services/version.service.ts: -------------------------------------------------------------------------------- 1 | import request from "@/utils/request"; 2 | import { 3 | VersionDto, 4 | GetVersionListReq, 5 | NewVersionReq, 6 | DelVersionReq, 7 | UpdateVersionBindsReq, 8 | CopyVersionReq, 9 | } from "@/types/version.type"; 10 | import { ParamBindItemDto } from "@/types/template.type"; 11 | 12 | // 获取版本绑定的参数列表 13 | export async function getVersionParamDetail( 14 | params: DelVersionReq 15 | ): Promise[]> { 16 | return request.get("/et/v1/parambind/get", { params }); 17 | } 18 | 19 | // 更新版本名称及绑定参数列表 20 | export async function updateVersionParam(data: UpdateVersionBindsReq) { 21 | return request.put("/et/v1/parambind/edit", data, { handleError: false }); 22 | } 23 | 24 | // 创建、复制新的参数版本 25 | export async function copyVersion(data: CopyVersionReq) { 26 | return request.post("/et/v1/parambind/copy", data, { handleError: false }); 27 | } 28 | 29 | export async function getVersionList( 30 | params: Partial 31 | ): Promise { 32 | return request.get("/et/v1/version/list", { params }); 33 | } 34 | 35 | export async function addNewVersion(data: NewVersionReq): Promise { 36 | return request.post("/et/v1/version/create", data, { handleError: false }); 37 | } 38 | 39 | export async function deleteVersion(params: DelVersionReq) { 40 | return request.delete("/et/v1/version/delete", { 41 | params, 42 | handleError: false, 43 | }); 44 | } 45 | 46 | export async function setCurrentVersion(data: DelVersionReq) { 47 | return request.put("/et/v1/version/setusing", data, { handleError: false }); 48 | } 49 | -------------------------------------------------------------------------------- /src/sevice.d.ts: -------------------------------------------------------------------------------- 1 | declare interface ResponseDTO { 2 | result: any 3 | } 4 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import { VNode } from 'vue' 2 | 3 | declare module '*.svg' { 4 | const content: any 5 | export default content 6 | } 7 | 8 | declare global { 9 | namespace JSX { 10 | // tslint:disable no-empty-interface 11 | interface Element extends VNode {} 12 | // tslint:disable no-empty-interface 13 | interface ElementClass extends Vue {} 14 | interface IntrinsicElements { 15 | [elem: string]: any 16 | } 17 | } 18 | 19 | interface Window { 20 | DATracker: any 21 | Quill: any 22 | mozRequestAnimationFrame: any 23 | oRequestAnimationFrame: any 24 | msRequestAnimationFrame: any 25 | } 26 | 27 | interface Promise { 28 | allSettled: any 29 | } 30 | } 31 | 32 | declare module '@vue/runtime-core' { 33 | interface ComponentCustomProperties { 34 | // 统一错误处理 35 | $handleError(e: Error, type?: string): void 36 | // 弹窗、抽屉统一解决方案 37 | $createInstance( 38 | vueConstructor: any, 39 | props: Omit & Record 40 | ): { destroy: () => void } 41 | $confirm(options: any): unknown 42 | $message: { 43 | success(str: string): unknown 44 | error(str: string): unknown 45 | } 46 | $warning(options: any): unknown 47 | $getPopupContainer(node: HTMLElement): HTMLElement 48 | $navToNewTab(to: _RouteLocationBase): void 49 | $filterOption(input: any, option: any): void 50 | getQueryParams: any 51 | setQueryParams: any 52 | checkAuth(code: number | number[]): boolean 53 | menuLists: any 54 | $preview(options: unknown): unknown 55 | $get(object: Record, path: string, defaultValue: any): any 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue'; 4 | 5 | const component: DefineComponent<{}, {}, any>; 6 | 7 | export default component; 8 | }; 9 | 10 | declare module 'vue3-json-view' { 11 | import JsonView from 'vue3-json-view' 12 | 13 | export default JsonView; 14 | }; 15 | 16 | declare module 'hooks/*' { 17 | export default (...args) => any; 18 | }; -------------------------------------------------------------------------------- /src/style/animation.less: -------------------------------------------------------------------------------- 1 | @keyframes slide-top { 2 | from { 3 | opacity: 0; 4 | transform: translate3d(0, -100%, 0); 5 | } 6 | to { 7 | opacity: 1; 8 | transform: translate3d(0, 0, 0); 9 | } 10 | } 11 | 12 | @keyframes slide-in-bottom { 13 | from { 14 | opacity: 0; 15 | transform: translate3d(0, 50%, 0); 16 | } 17 | to { 18 | opacity: 1; 19 | transform: translate3d(0, 0, 0); 20 | } 21 | } 22 | 23 | @keyframes opacity { 24 | from { 25 | position: absolute; 26 | right: 0; 27 | top: 0; 28 | width: 100%; 29 | opacity: 0; 30 | } 31 | to { 32 | position: absolute; 33 | right: 0; 34 | top: 0; 35 | width: 100%; 36 | opacity: 1; 37 | } 38 | } 39 | 40 | .initAnimation(@option, @time: .5) { 41 | @key: ~'@{option}'; 42 | .@{key}-enter-active { 43 | animation: @key ~'@{time}s' ease-in-out; 44 | } 45 | .@{key}-leave-active { 46 | animation: @key ~'@{time}s' ease-in-out reverse; 47 | } 48 | } 49 | 50 | .initAnimation(slide-top); 51 | .initAnimation(slide-in-bottom); 52 | .initAnimation(opacity, .15); 53 | 54 | .opacitys-enter-active { 55 | animation: opacitys .3s ease-in-out; 56 | } 57 | .opacitys-leave-active { 58 | animation: opacitys .3s ease-in-out reverse; 59 | } 60 | 61 | @keyframes opacitys { 62 | from { 63 | opacity: 0; 64 | } 65 | to { 66 | opacity: 1; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/style/index.less: -------------------------------------------------------------------------------- 1 | @import 'scrollbar.less'; 2 | @import 'global.less'; 3 | @import 'ant-design.less'; 4 | @import 'animation.less'; 5 | @import 'nprogress.less'; 6 | -------------------------------------------------------------------------------- /src/style/nprogress.less: -------------------------------------------------------------------------------- 1 | /* Make clicks pass-through */ 2 | #nprogress { 3 | pointer-events: none; 4 | } 5 | 6 | #nprogress .bar { 7 | background: #103FFA; 8 | position: fixed; 9 | z-index: 1031; 10 | top: 0; 11 | left: 0; 12 | width: 100%; 13 | height: 2px; 14 | } 15 | 16 | /* Fancy blur effect */ 17 | #nprogress .peg { 18 | display: block; 19 | position: absolute; 20 | right: 0px; 21 | width: 100px; 22 | height: 100%; 23 | box-shadow: 0 0 10px #103FFA, 0 0 5px #103FFA; 24 | opacity: 1.0; 25 | -webkit-transform: rotate(3deg) translate(0px, -4px); 26 | -ms-transform: rotate(3deg) translate(0px, -4px); 27 | transform: rotate(3deg) translate(0px, -4px); 28 | } 29 | 30 | /* Remove these to get rid of the spinner */ 31 | .nprogress-custom-parent { 32 | overflow: hidden; 33 | position: relative; 34 | } 35 | 36 | .nprogress-custom-parent #nprogress .bar { 37 | position: absolute; 38 | } 39 | -------------------------------------------------------------------------------- /src/style/scrollbar.less: -------------------------------------------------------------------------------- 1 | /* 设置滚动条的样式 */ 2 | ::-webkit-scrollbar { 3 | width:8px; 4 | height: 10px; 5 | } 6 | /* 滚动槽 */ 7 | ::-webkit-scrollbar-track { 8 | border-radius:10px; 9 | } 10 | /* 滚动条滑块 */ 11 | ::-webkit-scrollbar-thumb { 12 | border-radius:10px; 13 | background:rgba(0,0,0,0.2); 14 | } 15 | -------------------------------------------------------------------------------- /src/track.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * PV 埋点装饰器 3 | * @param id 页面id 4 | * @returns 日志上报代码注入函数 5 | */ 6 | export const PV = (id) => { 7 | return (target) => { 8 | ;(target.options.mounted || (target.options.mounted = [])).push(() => { 9 | window.DATracker && window.DATracker.track && window.DATracker.track(id) 10 | }) 11 | } 12 | } 13 | 14 | /** 15 | * PV 埋点 16 | * @param id 页面id 17 | * @param data 上报数据 18 | */ 19 | export const trackPage = (id, data = {}) => { 20 | window.DATracker && window.DATracker.track && window.DATracker.track(id, data) 21 | } 22 | 23 | /** 24 | * 日志上报 25 | * @param id 事件id 26 | * @param data 上报数据 27 | */ 28 | export const trackEvent = (id, data = {}) => { 29 | window.DATracker && window.DATracker.track && window.DATracker.track(id, data) 30 | } 31 | -------------------------------------------------------------------------------- /src/types/README.md: -------------------------------------------------------------------------------- 1 | # type 2 | 3 | > 项目类型文件 4 | 5 | ## 目录说明 6 | 7 | ``` 8 | |-- app.type.ts 产品、集群等类型 9 | |-- common.type.ts 通用类型:用户、分页、排序、搜索等 10 | |-- event.type.ts 事件模块类型 11 | |-- object.type.ts 对象模块类型 12 | |-- parameter.type.ts 参数模块类型 13 | |-- requirement.type.ts 需求模块类型 14 | |-- table.type.ts 表格相关类型 15 | |-- template.type.ts 参数模板类型 16 | |-- terminal.type.ts 终端管理类型 17 | |-- version.type.ts 终端、事件类型参数版本相关类型 18 | ``` 19 | -------------------------------------------------------------------------------- /src/types/app.type.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | email: string; 3 | userName: string; 4 | } 5 | 6 | // 产品信息 7 | export interface AppDto { 8 | id: number; 9 | code: string; 10 | name: string; 11 | description: string; 12 | creator: User; 13 | createTime: number; 14 | updateTime: number; 15 | } 16 | 17 | // 项目配置信息 18 | // TODO: 去除猛犸依赖 19 | export interface BizConfigDto { 20 | bdmsUrl: string; // mammaut url,用于获取导航栏 21 | bdmsPermissionUrl: string; // mammut用户项目、权限申请页面url(用于403、nobiz页面) 22 | musicRoutingUrl: string; // 路由平台地址 23 | } 24 | 25 | export interface WsDataDto { 26 | ruleList: any[]; 27 | logData: any; 28 | oldRuleList: any[]; 29 | oldLOgData: any; 30 | exceptions: any[]; 31 | expLogList: any[]; 32 | undefinedStatistics: any; 33 | baseTree: any[]; 34 | taskTree: any[]; 35 | taskName: string; 36 | } 37 | 38 | export interface FilterDto { 39 | key: string | number; 40 | value: string; 41 | } 42 | -------------------------------------------------------------------------------- /src/types/authority.type.ts: -------------------------------------------------------------------------------- 1 | export interface RoleDto { 2 | id: string | number 3 | roleName: string 4 | description: string 5 | createTime: string 6 | updateTime: string 7 | } 8 | 9 | export interface UserDto { 10 | userId: number 11 | email: string 12 | userName: string 13 | apps: string[] 14 | createTime: string 15 | roles: RoleDto 16 | } 17 | 18 | export interface OwnerVo { 19 | id?: string | number 20 | userName: string 21 | email: string 22 | } 23 | 24 | export interface DomainDetailVo extends Omit { 25 | name: string 26 | owner: OwnerVo 27 | creator: OwnerVo 28 | admins: OwnerVo[] 29 | code: string 30 | } 31 | 32 | export interface NoticeDto { 33 | channelType: number 34 | modeTypes: number[] 35 | dailyNotifyTime?: string 36 | } 37 | -------------------------------------------------------------------------------- /src/types/common.type.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | id?: string | number 3 | email: string 4 | userName: string 5 | } 6 | 7 | // 分页请求 8 | export type Pagination = { 9 | currentPage: number 10 | pageSize: number 11 | } 12 | 13 | // 排序请求 14 | export type Order = { 15 | orderBy: string 16 | orderRule: 'ascend' | 'descend' 17 | } 18 | 19 | // 搜索请求 20 | export type Search = { 21 | search: string 22 | } 23 | 24 | // 分页响应结果 25 | export type PaginationList = { 26 | totalNum: number 27 | pageNum: number 28 | list: T[] 29 | } 30 | 31 | export type BaseObjectDto = { 32 | id: number 33 | code: string 34 | name: string 35 | description: string 36 | creator: User 37 | createTime: number 38 | updateTime: number 39 | updater: User 40 | } 41 | 42 | export type TagAggreDto = { 43 | key: number | string // 传递值 44 | value: string // 展示值 45 | } 46 | 47 | export type ItemDto = { 48 | id: number 49 | name: string 50 | } 51 | -------------------------------------------------------------------------------- /src/types/event.type.ts: -------------------------------------------------------------------------------- 1 | import { BaseObjectDto, Pagination, Order, Search } from './common.type' 2 | import { ObjTypeEnum } from './object.type' 3 | 4 | export type EventSimpleDto = BaseObjectDto & { 5 | selectedByDefault: boolean // 是否默认选中 6 | applicableObjTypes: ObjTypeEnum[] 7 | } 8 | 9 | /** ----------------------------- Requst Parameter Type -------------------------- */ 10 | 11 | export type GetEventListReq = Pagination & Partial & Search 12 | 13 | export type NewEventReq = { 14 | code: string 15 | name: string 16 | description: string 17 | selectedByDefault: boolean // 是否默认选中 18 | applicableObjTypes: ObjTypeEnum[] 19 | } 20 | -------------------------------------------------------------------------------- /src/types/requirement.design.type.ts: -------------------------------------------------------------------------------- 1 | export type StatDto = Record< 2 | | 'allSpms' 3 | | 'assignedSpms' 4 | | 'unAssignedSpms' 5 | | 'allEvents' 6 | | 'assignedEvents' 7 | | 'unAssignedEvents' 8 | | 'tasks', 9 | number 10 | > 11 | -------------------------------------------------------------------------------- /src/types/spm.type.ts: -------------------------------------------------------------------------------- 1 | interface SPMIds { 2 | spmIds: number[] 3 | } 4 | 5 | export interface ConditionDTO { 6 | terminalId: string | number | null 7 | isMapped?: boolean 8 | mapStatus?: number[] 9 | spmStatus?: number 10 | tagId?: number 11 | } 12 | 13 | export interface SPMListsResponseDTO { 14 | id: number 15 | spm: string 16 | spmOldList: string[] 17 | name: string 18 | tags: string 19 | mapStatus: number 20 | spmStatus: number 21 | version: string 22 | note: string 23 | } 24 | 25 | export interface SPMListsRequestDTO extends ConditionDTO { 26 | spmOldOrMapVersion?: string 27 | spmOrName?: string 28 | } 29 | 30 | export interface OldSPMMapRequestDTO { 31 | spmId: number 32 | spmOldList: string[] 33 | terminalId: number 34 | } 35 | 36 | export interface BindSPMTagRequestDTO extends SPMIds { 37 | tagIds: number[] 38 | terminalId: number 39 | } 40 | 41 | export interface UpdateSPMStatusRequestDTO extends SPMIds { 42 | status: number 43 | terminalId: number 44 | } 45 | 46 | export interface UpdateSPMVersionRequestDTO extends SPMIds { 47 | version: string 48 | terminalId: number 49 | } 50 | 51 | export interface UpdateSPMRemarkRequestDTO { 52 | spmId: number 53 | note: string 54 | } 55 | export interface CreateSPMRequestDTO { 56 | spmId?: number 57 | spm: string 58 | name: string 59 | appId: number 60 | terminalId: number 61 | spmStatus?: number 62 | status?: number 63 | } 64 | -------------------------------------------------------------------------------- /src/types/table.type.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | export type TablePagination = Partial 3 | export type Column = any 4 | 5 | export type TableColumn = Partial> & { 6 | customCell?: ( 7 | record: any, 8 | rowIndex: number 9 | ) => { 10 | props?: Record 11 | attrs?: Record 12 | on?: Record 13 | class?: string | Record 14 | style?: string | Record 15 | nativeOn?: Record 16 | } 17 | customHeaderCell?: ( 18 | record?: any, 19 | rowIndex?: number 20 | ) => { 21 | props?: Record 22 | attrs?: Record 23 | on?: Record 24 | class?: string | Record 25 | style?: string | Record 26 | nativeOn?: Record 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/types/template.type.ts: -------------------------------------------------------------------------------- 1 | import { BaseObjectDto, Pagination, Order, Search } from './common.type' 2 | import { ParamValueTypeEnum, ParamValueDto, ParamTypeEnum, ParameterDto } from './parameter.type' 3 | 4 | export type TemplateSimpleDto = Omit 5 | 6 | export interface ParamBindItemDto { 7 | uuid: string // 展示所构造的唯一值 8 | id: number // param id 9 | code: string 10 | name: string 11 | paramType: ParamTypeEnum 12 | valueType: ParamValueTypeEnum 13 | notEmpty: boolean // 非空 14 | must?: boolean // 必传 15 | description: string // 取值描述 16 | selectedValues: number[] // 选中的值 17 | values: ParamValueDto[] // 可选择的值 18 | names: ParameterDto[] // 对象业务私参可选的参数集合 19 | needTest?: boolean // 用于测试 20 | } 21 | 22 | export interface ParamBindSimpleDto { 23 | paramId: number 24 | values: number[] 25 | description: string 26 | notEmpty: boolean 27 | must: boolean // 必传 28 | } 29 | 30 | export interface TemplateDetailDto extends TemplateSimpleDto { 31 | binds: Omit[] 32 | } 33 | 34 | /** ----------------------------- Requst Parameter Type -------------------------- */ 35 | 36 | export type GetTemplateListReq = Pagination & Search & Partial 37 | 38 | export type NewTemplateReq = { 39 | name: string 40 | description: string 41 | binds: ParamBindSimpleDto[] 42 | } 43 | -------------------------------------------------------------------------------- /src/types/terminal.type.ts: -------------------------------------------------------------------------------- 1 | import { BaseObjectDto, Pagination, Order, Search } from './common.type' 2 | 3 | export enum TerminalTypeEnum { 4 | PC = 1, 5 | WIRELESS = 2, // 无线 6 | SERVER = 3 // 服务端 7 | } 8 | 9 | export const terminalTypeNameMap = { 10 | [TerminalTypeEnum.PC]: 'PC', 11 | [TerminalTypeEnum.WIRELESS]: '客户端', 12 | [TerminalTypeEnum.SERVER]: '服务端' 13 | } 14 | 15 | export type TerminalSimpleDto = BaseObjectDto & { 16 | type: TerminalTypeEnum 17 | terminalType?: string 18 | preset: boolean // 是否是预置信息 19 | } 20 | 21 | /** ----------------------------- Requst Parameter Type -------------------------- */ 22 | 23 | export type GetTerminalListReq = Pagination & 24 | Partial & 25 | Search & { 26 | terminalTypes?: TerminalTypeEnum[] 27 | } 28 | 29 | export interface NewTerminalReq { 30 | type: TerminalTypeEnum 31 | name: string 32 | } 33 | -------------------------------------------------------------------------------- /src/types/version.type.ts: -------------------------------------------------------------------------------- 1 | import { ParamBindSimpleDto } from '@/types/template.type' 2 | 3 | export interface VersionDto { 4 | id: string 5 | name: string 6 | preset: boolean // 是否是预置版本 7 | currentUsing: boolean // 是否是当前使用版本 8 | used: boolean // 是否被使用过,被使用过不可删除 9 | } 10 | 11 | export enum EntityTypeEnum { 12 | TERMINAL = 1, // 终端 13 | EVENT = 2, // 事件类型 14 | OBJ = 3, // 对象 15 | TEMPLATE = 4 // 模板 16 | } 17 | 18 | /** ----------------------------- Requst Parameter Type -------------------------- */ 19 | 20 | export type GetVersionListReq = { 21 | search: string 22 | entityId: number 23 | entityType: EntityTypeEnum 24 | } 25 | 26 | export interface NewVersionReq { 27 | entityId: number 28 | entityType: EntityTypeEnum 29 | name: string 30 | } 31 | 32 | export interface DelVersionReq { 33 | entityId: number 34 | entityType: EntityTypeEnum 35 | versionId: number 36 | } 37 | 38 | export interface UpdateVersionBindsReq { 39 | entityId: number 40 | entityType: EntityTypeEnum 41 | versionId: number 42 | version: string // 版本名称 43 | paramBinds: ParamBindSimpleDto[] 44 | } 45 | 46 | export interface CopyVersionReq { 47 | name: string 48 | entityId: number 49 | entityType: EntityTypeEnum 50 | versionId: number 51 | } 52 | -------------------------------------------------------------------------------- /src/utils/common.ts: -------------------------------------------------------------------------------- 1 | export function isArrayShallowEqual(a: any[], b: any[]) { 2 | if (!Array.isArray(a) || !Array.isArray(b)) return false; 3 | if (a.length !== b.length) return false; 4 | 5 | return a.every((v) => b.includes(v)) && b.every((v) => a.includes(v)); 6 | } 7 | 8 | export function removeObjectNullProperty(obj: T): T { 9 | Object.keys(obj).forEach((k) => { 10 | if (obj[k] == null) { 11 | delete obj[k]; 12 | } 13 | }); 14 | 15 | return obj; 16 | } 17 | 18 | export const genBackground = (level: number) => { 19 | if (level > 0) { 20 | let background = "linear-gradient(to right, #dee2e6 0%, #dee2e6 16px,"; 21 | let color = "#fff"; 22 | let stop: any = 16; 23 | 24 | for (let i = 0; i < level; i++) { 25 | background += ` ${color} ${stop}px,`; 26 | 27 | if (i === level - 1) { 28 | background += ` ${color} 100%`; 29 | } else { 30 | stop += 16; 31 | background += ` ${color} ${stop}px,`; 32 | } 33 | 34 | color = color === "#f6f7fa" ? "#fff" : "#f6f7fa"; 35 | } 36 | 37 | background += ")"; 38 | return background; 39 | } 40 | 41 | return "#fff;"; 42 | }; 43 | 44 | export function generateExpandKeys(spmList = []) { 45 | const result = []; 46 | 47 | if (spmList && spmList.length) { 48 | spmList.forEach((spm) => { 49 | const list = spm.split("|").reverse(); 50 | 51 | list.reduce((prev, cur) => { 52 | prev.unshift(cur); 53 | result.push(prev.join("|")); 54 | 55 | return prev; 56 | }, []); 57 | }); 58 | } 59 | 60 | return result; 61 | } 62 | -------------------------------------------------------------------------------- /src/utils/cookie.ts: -------------------------------------------------------------------------------- 1 | function fomattCookie() { 2 | const cookie = document.cookie; 3 | 4 | return cookie.split(";").reduce((objectCookie, next) => { 5 | const [key, value] = next.split("="); 6 | 7 | objectCookie[key] = value; 8 | 9 | return objectCookie; 10 | }, {}); 11 | } 12 | 13 | export function getCookie(param) { 14 | const objectCookie = fomattCookie(); 15 | 16 | return objectCookie[param]; 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/hooks/Object/useFindAndroidAndIPhone.ts: -------------------------------------------------------------------------------- 1 | export default function useFindAndroidAndIPhone(trackers) { 2 | const terminalMap: string[] = ["android", "iphone"]; 3 | 4 | return trackers.filter((next) => 5 | terminalMap.some((v) => next.terminalName.toLowerCase().includes(v)) 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/hooks/useCopy.ts: -------------------------------------------------------------------------------- 1 | import { message } from "ant-design-vue"; 2 | 3 | export default function useCopy(param): void { 4 | const input = document.createElement("textarea"); 5 | 6 | document.body.appendChild(input); 7 | 8 | input.value = param; 9 | input.select(); 10 | 11 | const status = document.execCommand("copy"); 12 | const text = status ? "成功" : "失败"; 13 | 14 | message.success(`复制${text}~`); 15 | 16 | document.body.removeChild(input); 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/hooks/useCopyParams.ts: -------------------------------------------------------------------------------- 1 | import { ParamValueTypeEnum } from '@/types/parameter.type' 2 | 3 | export default function useCopyParams(paramBinds = [], hasBox = true) { 4 | return ( 5 | (paramBinds || []).reduce( 6 | (params, v, index) => { 7 | const isLast = index === paramBinds.length - 1 8 | const value = 9 | (v.valueType === ParamValueTypeEnum.CONSTANT && 10 | Array.isArray(v.selectedValues) && 11 | v.selectedValues.length > 1 && 12 | v.values.find((b) => v.selectedValues.includes(b.id))?.code) || 13 | '' 14 | 15 | params += `${v.code}: ${value || JSON.stringify('')}${isLast ? '' : ','}` 16 | params += v.description || value ? ` // ${v.description} ${value} \n` : ' \n' 17 | 18 | return params 19 | }, 20 | hasBox ? '{ \n' : '' 21 | ) + (hasBox ? ' }' : '') 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/request.ts: -------------------------------------------------------------------------------- 1 | import { Request, CustomAxiosRequestConfig } from "@/ndsc-vue3/utils/lib"; 2 | import loginService from "@/services/login.service"; 3 | import store from "@/store/index"; 4 | 5 | const instance = new Request({ 6 | baseURL: "/api/", 7 | addTokenFunc: (config: CustomAxiosRequestConfig) => { 8 | // 将 domainId 和 appId 添加到请求的 url 上 9 | const domainId = store.state.domainId; 10 | const app = store.state.app; 11 | const searchList = []; 12 | 13 | domainId && searchList.push("domainId=" + domainId); 14 | app?.id && searchList.push("appId=" + app.id); 15 | 16 | if (searchList.length) { 17 | config.url += 18 | (config.url.indexOf("?") === -1 ? "?" : "&") + searchList.join("&"); 19 | } 20 | }, 21 | loginFunc: () => loginService.login(), // code === -4 时跳转登录操作 22 | }); 23 | 24 | export default instance; 25 | -------------------------------------------------------------------------------- /src/utils/tests/index.ts: -------------------------------------------------------------------------------- 1 | export function getData(wrapper: any, type = 1): any { 2 | const vm: any = wrapper.vm 3 | 4 | return type === 1 ? vm._.setupState : vm 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/validator.ts: -------------------------------------------------------------------------------- 1 | export const codeValidator = (rule, value) => { 2 | if (value === "") return Promise.resolve(true); 3 | 4 | if (!/^[a-zA-Z0-9_-]*$/.test(value)) { 5 | return Promise.reject(new Error("支持英文、数字、'_'和'-'")); 6 | } 7 | 8 | return Promise.resolve(true); 9 | }; 10 | 11 | export const nameValidator = (rule, value) => { 12 | if (value === "") return Promise.resolve(true); 13 | 14 | if (!/^[\u4e00-\u9fa5a-zA-Z0-9_-]*$/.test(value)) { 15 | return Promise.reject(new Error("支持中英文、数字、'_'和'-'")); 16 | } 17 | 18 | return Promise.resolve(true); 19 | }; 20 | -------------------------------------------------------------------------------- /src/utils/vars.ts: -------------------------------------------------------------------------------- 1 | export const diffColors = { 2 | new: '#ecfdf0', 3 | mod: '#fafad7', 4 | del: '#fbe9eb' 5 | } 6 | -------------------------------------------------------------------------------- /src/views/403.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | -------------------------------------------------------------------------------- /src/views/404.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /src/views/NoBiz.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 26 | -------------------------------------------------------------------------------- /src/views/home/README.md: -------------------------------------------------------------------------------- 1 | # home 2 | 3 | > 项目页面结构 4 | 5 | ## 目录说明 6 | ``` 7 | |-- tracker 埋点管理模块 8 | |-- metadata 元数据管理模块 9 | |-- components 公用组件 10 | |-- event 事件类型模块 11 | |-- parameter 参数管理模块 12 | |-- template 参数模板模块 13 | |-- terminal 终端管理模块 14 | |-- obejct 对象管理模块 15 | |-- detail 对象详情页面 16 | |-- list 对象管理页面 17 | |-- blood-relation 对象血缘模块 18 | |-- requirement 需求管理模块 19 | |-- components 公用组件 20 | |-- detail 需求详情页面 21 | |-- list 需求管理页面 22 | |-- version 版本管理页面 23 | |-- conflict-result 版本冲突结果页 24 | |-- graph 版本链图 25 | ``` -------------------------------------------------------------------------------- /src/views/home/authorityManagement/domainManage/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 20 | -------------------------------------------------------------------------------- /src/views/home/authorityManagement/domainManage/nav.tsx: -------------------------------------------------------------------------------- 1 | import { NavListDto, NavConfigDto } from '@/ndsc-vue3/sidebar/lib' 2 | const navList: NavListDto[] = [ 3 | { 4 | id: 'domain', 5 | name: '域信息', 6 | to: '/authorityManage/domain/detail', 7 | iconType: 'custom', 8 | icon: (h) => { 9 | return 10 | } 11 | }, 12 | { 13 | id: 'member', 14 | name: '成员管理', 15 | to: '/authorityManage/domain/member', 16 | iconType: 'custom', 17 | icon: (h) => { 18 | return 19 | } 20 | }, 21 | { 22 | id: 'product', 23 | name: '产品配置', 24 | to: '/authorityManage/domain/product', 25 | iconType: 'custom', 26 | icon: (h) => { 27 | return 28 | } 29 | } 30 | ] 31 | export default defineComponent({}) 32 | -------------------------------------------------------------------------------- /src/views/home/authorityManagement/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /src/views/home/authorityManagement/platformManage/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 22 | -------------------------------------------------------------------------------- /src/views/home/authorityManagement/platformManage/nav.tsx: -------------------------------------------------------------------------------- 1 | import { NavListDto, NavConfigDto } from '@/ndsc-vue3/sidebar/lib' 2 | const navList: NavListDto[] = [ 3 | { 4 | id: 'domain', 5 | name: '域配置', 6 | to: '/authorityManage/platform/domain', 7 | iconType: 'custom', 8 | icon: (h) => { 9 | return 10 | } 11 | } 12 | ] 13 | export default defineComponent({}) 14 | -------------------------------------------------------------------------------- /src/views/home/authorityManagement/productionManage/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /src/views/home/authorityManagement/productionManage/manage/components/auth-tab/utils.ts: -------------------------------------------------------------------------------- 1 | export function transformTreeData(list) { 2 | list.forEach((item) => { 3 | item.title = item.menuName 4 | item.key = item.id 5 | 6 | if (item.children) { 7 | transformTreeData(item.children) 8 | } 9 | }) 10 | } 11 | 12 | export function getTreeNodeList(list, result) { 13 | list.forEach((item) => { 14 | if (item.assigned) { 15 | result.push(item.id) 16 | } 17 | 18 | if (item.children) { 19 | getTreeNodeList(item.children, result) 20 | } 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /src/views/home/authorityManagement/productionManage/manage/components/role-panel/index.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 48 | 49 | 59 | -------------------------------------------------------------------------------- /src/views/home/authorityManagement/productionManage/nav.tsx: -------------------------------------------------------------------------------- 1 | import { NavListDto, NavConfigDto } from '@/ndsc-vue3/sidebar/lib' 2 | const navList: NavListDto[] = [ 3 | { 4 | id: 'info', 5 | name: '产品信息', 6 | to: '/authorityManage/production/info', 7 | iconType: 'custom', 8 | icon: (h) => { 9 | return 10 | } 11 | }, 12 | { 13 | id: 'member', 14 | name: '成员管理', 15 | to: '/authorityManage/production/member', 16 | iconType: 'custom', 17 | icon: (h) => { 18 | return 19 | } 20 | }, 21 | { 22 | id: 'manage', 23 | name: '角色管理', 24 | to: '/authorityManage/production/manage', 25 | iconType: 'custom', 26 | icon: (h) => { 27 | return 28 | } 29 | } 30 | ] 31 | export default defineComponent({}) 32 | -------------------------------------------------------------------------------- /src/views/home/test/components/device-detail/index.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 55 | 56 | 64 | -------------------------------------------------------------------------------- /src/views/home/test/components/point-list/detection.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 43 | 44 | 63 | -------------------------------------------------------------------------------- /src/views/home/test/components/types/index.ts: -------------------------------------------------------------------------------- 1 | export type deviceInfoDto = { 2 | appName: string 3 | appVer: string 4 | deviceName: string 5 | platform: string 6 | sysVer: string 7 | } 8 | 9 | export type statisticsDto = { 10 | eventStatistics: any[] 11 | logNum: number 12 | objTrackerNum: number 13 | } 14 | 15 | export type detailDto = { 16 | ruleCheck: any 17 | statistics: statisticsDto 18 | } 19 | -------------------------------------------------------------------------------- /src/views/home/test/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 56 | 57 | 66 | -------------------------------------------------------------------------------- /src/views/home/test/nav.tsx: -------------------------------------------------------------------------------- 1 | import { NavListDto, NavConfigDto } from '@/ndsc-vue3/sidebar/lib' 2 | const navList: NavListDto[] = [ 3 | { 4 | id: 'realtime', 5 | name: '实时测试', 6 | to: '/test/realtime', 7 | iconType: 'custom', 8 | icon: (h) => { 9 | return 10 | } 11 | }, 12 | { 13 | id: 'recordList', 14 | name: '测试记录', 15 | to: '/test/record', 16 | iconType: 'custom', 17 | icon: (h) => { 18 | return 19 | } 20 | }, 21 | { 22 | id: 'requirement', 23 | name: '需求测试', 24 | to: '/test/requirement', 25 | iconType: 'custom', 26 | icon: (h) => { 27 | return 28 | } 29 | } 30 | ] 31 | export const navConfig: NavConfigDto = { 32 | project: { 33 | name: '埋点测试', 34 | to: '/test/realtime', 35 | icon: (h) => { 36 | return 37 | } 38 | }, 39 | menu: navList 40 | } 41 | -------------------------------------------------------------------------------- /src/views/home/test/realtime/audit/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 31 | 32 | 39 | -------------------------------------------------------------------------------- /src/views/home/test/realtime/lists/types/content.ts: -------------------------------------------------------------------------------- 1 | import { TagAggreDto } from '@/types/common.type' 2 | 3 | type configKeys = 'requirementName' | 'taskName' | 'status' | 'objType' | 'owner' | 'terminal' 4 | 5 | export type configDto = { 6 | [P in configKeys]: TagAggreDto[] 7 | } 8 | -------------------------------------------------------------------------------- /src/views/home/test/realtime/record/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 25 | -------------------------------------------------------------------------------- /src/views/home/tracker/event/EventDetail.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 41 | -------------------------------------------------------------------------------- /src/views/home/tracker/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 54 | 55 | 64 | -------------------------------------------------------------------------------- /src/views/home/tracker/metadata/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /src/views/home/tracker/nav.tsx: -------------------------------------------------------------------------------- 1 | import { NavConfigDto } from '@/ndsc-vue3/sidebar/lib' 2 | 3 | export const navConfig: Omit = { 4 | project: { 5 | name: '埋点管理', 6 | icon: () => 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/detail/BackToList.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 43 | 44 | 50 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/detail/ParamConfigFilter.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 71 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/detail/ParamTableModal.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 31 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/BloodRelationDrawer.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 53 | 54 | 61 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/ObjectAddDrawer.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 73 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/ObjectDetail.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 35 | 36 | 45 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/ObjectParamsTable.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 40 | 41 | 46 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/behavior/click-canvas.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | getEvents() { 3 | return { 4 | 'canvas:click': 'onClick' 5 | } 6 | }, 7 | onClick() { 8 | const graph = this.graph 9 | // 将所有当前是 click 状态的节点置为非 click 状态 10 | const clickNodes = graph.findAllByState('node', 'click') 11 | clickNodes.forEach((cn) => { 12 | graph.setItemState(cn, 'click', false) 13 | }) 14 | // 将所有当前是 click 状态的边置为非 click 状态 15 | const clickEdges = graph.findAllByState('edge', 'click') 16 | clickEdges.forEach((ce) => { 17 | graph.setItemState(ce, 'click', false) 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/behavior/click-node.ts: -------------------------------------------------------------------------------- 1 | import { INode } from "@antv/g6-core/lib/interface/item"; 2 | import { CustomNodeConfig } from "../type"; 3 | 4 | const startAnchors = ["start-anchor", "start-anchor-icon"]; 5 | const endAnchors = ["end-anchor", "end-anchor-icon"]; 6 | 7 | export default { 8 | getEvents() { 9 | return { 10 | "node:click": "onClick", 11 | }; 12 | }, 13 | onClick(e) { 14 | const graph = this.graph; 15 | const name = e.shape?.cfg?.name; 16 | 17 | if (!startAnchors.concat(endAnchors).includes(name)) return; 18 | 19 | const item: INode = e.item; 20 | const cfg = item.getModel() as CustomNodeConfig; 21 | const { id, edges } = cfg; 22 | const isSource = edges.findIndex((e) => e.source === id) > -1; 23 | const eventEmitter = graph.eventEmitter; 24 | 25 | if (startAnchors.includes(name)) { 26 | const args = isSource ? ["fold-node", id] : ["expand-node", id, "son"]; 27 | 28 | eventEmitter.emit(...args); 29 | } else { 30 | cfg.expandParent && eventEmitter.emit("expand-node", id, "parent"); 31 | } 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/behavior/hover-node.ts: -------------------------------------------------------------------------------- 1 | import { INode } from "@antv/g6-core/lib/interface/item"; 2 | 3 | export default { 4 | getEvents() { 5 | return { 6 | "node:mouseover": "onMouseOver", 7 | "node:mouseleave": "onMouseLeave", 8 | }; 9 | }, 10 | onMouseOver(e) { 11 | const graph = this.graph; 12 | const item: INode = e.item; 13 | 14 | graph.setItemState(item, "hover", true); 15 | }, 16 | onMouseLeave(e) { 17 | const graph = this.graph; 18 | const item: INode = e.item; 19 | 20 | graph.setItemState(item, "hover", false); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/behavior/index.ts: -------------------------------------------------------------------------------- 1 | import G6 from '@antv/g6' 2 | import canvasClick from './click-canvas' 3 | import clickNode from './click-node' 4 | import hoverNode from './hover-node' 5 | 6 | const behavors = { 7 | 'click-node': clickNode, 8 | 'click-canvas': canvasClick, 9 | 'hover-node': hoverNode 10 | } 11 | 12 | export const initBehaviors = () => { 13 | for (const key in behavors) { 14 | G6.registerBehavior(key, behavors[key]) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/item/index.ts: -------------------------------------------------------------------------------- 1 | import { registerNode } from './obj-node' 2 | 3 | export const registerItems = () => { 4 | registerNode() 5 | } 6 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/plugin/contextMenu.ts: -------------------------------------------------------------------------------- 1 | import G6 from "@antv/g6"; 2 | import { Item } from "@antv/g6-core/lib/types"; 3 | 4 | export const getContextMenu = (eventEmitter, supportSwitchCurrent = true) => { 5 | const nodeContextMenu = new G6.Menu({ 6 | getContent(e) { 7 | const model = e.item?.getModel(); 8 | 9 | return ` 10 |
    11 |
  • 查看对象详情
  • 12 | ${ 13 | supportSwitchCurrent && model?.objType !== "cur" 14 | ? "
  • 切换为当前对象
  • " 15 | : "" 16 | } 17 |
18 | `; 19 | }, 20 | handleMenuClick: (target: HTMLElement, item: Item) => { 21 | const title = target.getAttribute("data-title"); 22 | 23 | title && eventEmitter.emit(title, item?.getModel()); 24 | }, 25 | offsetX: 8, 26 | offsetY: 8, 27 | // 在哪些类型的元素上响应 28 | itemTypes: ["node"], 29 | }); 30 | return [nodeContextMenu]; 31 | }; 32 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/plugin/index.less: -------------------------------------------------------------------------------- 1 | // 右键菜单 2 | .g6-component-contextmenu { 3 | padding: 4px 0px !important; 4 | border: none !important; 5 | border-radius: 0 !important; 6 | background: #ffffff !important; 7 | box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25) !important; 8 | li { 9 | padding: 4px 10px !important; 10 | cursor: pointer !important; 11 | &:hover { 12 | background-color: #e6efff !important; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/plugin/index.ts: -------------------------------------------------------------------------------- 1 | import { getContextMenu } from './contextMenu' 2 | 3 | // 引入 css 4 | import './index.less' 5 | 6 | export { getContextMenu } 7 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/share/color.ts: -------------------------------------------------------------------------------- 1 | export const nodeColorMap = { 2 | cur: { 3 | stroke: '#FB8D18', 4 | fill: '#FFF7E6', 5 | color: '#fb8d18' 6 | }, 7 | page: { 8 | stroke: '#26BD71', 9 | fill: '#E9F8F1', 10 | color: 'rgba(38, 189, 113, 1)' 11 | }, 12 | element: { 13 | stroke: '#1890FF', 14 | fill: '#E6F7FF', 15 | color: '#1890FF' 16 | } 17 | } 18 | 19 | export const nodeBgColorMap = { 20 | page: '#ebf7f1', 21 | element: '#e8f7fe', 22 | popover: '#fef7e8', 23 | bridge: '#f4dfe1' 24 | } 25 | 26 | export const nodeBdColorMap = { 27 | otherSpace: '#FFA500', 28 | currentSpace: '#181616' 29 | } 30 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/blood-relation/graph/type.ts: -------------------------------------------------------------------------------- 1 | import { NodeConfig, EdgeConfig } from '@antv/g6-core/lib/types' 2 | 3 | export type CustomNodeConfig = NodeConfig & { 4 | objType: 'page' | 'element' | 'popover' | 'bridge' 5 | spaceType: 'otherSpace' | 'currentSpace' 6 | showName: string 7 | expandParent: boolean 8 | expandSon: boolean 9 | edges: EdgeConfig[] 10 | terminalId: number 11 | versionId: number 12 | naturalWidth?: number 13 | naturalHeight?: number 14 | } 15 | -------------------------------------------------------------------------------- /src/views/home/tracker/object/list/const.ts: -------------------------------------------------------------------------------- 1 | export const objectType = { 2 | 1: "页面", 3 | 2: "元素", 4 | 3: "浮层", 5 | }; 6 | 7 | export const elementType = { 8 | page: "页面", 9 | panel: "浮层", 10 | mod: "模块", 11 | cell: "卡片", 12 | btn: "按钮", 13 | layer: "老浮层", 14 | unknown: "未设置", 15 | }; 16 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/components/StatusTag.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/components/object-diff/LineageInfo.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 47 | 48 | 63 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/components/object-diff/ObjectDiffDetail.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 45 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/components/object-diff/TrackerInfo.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 50 | 51 | 66 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/components/object-diff/util.ts: -------------------------------------------------------------------------------- 1 | import { ChangeTypeEnum } from "@/types/object.type"; 2 | 3 | export function genClasses(paramChangeType: ChangeTypeEnum) { 4 | return paramChangeType === ChangeTypeEnum.CREATE 5 | ? "new-item" 6 | : paramChangeType === ChangeTypeEnum.DELETE 7 | ? "delete-item" 8 | : ""; 9 | } 10 | 11 | export function genTooltip( 12 | { 13 | paramChangeType, 14 | terminalVersionName, 15 | isNewestTerminalVersion, 16 | isCurrentTask, 17 | }, 18 | type = "参数" 19 | ) { 20 | let res = ""; 21 | const operate = paramChangeType === ChangeTypeEnum.CREATE ? "增加" : "删除"; 22 | 23 | if (isNewestTerminalVersion) { 24 | res += `${terminalVersionName}${operate}了该${type}`; 25 | } 26 | 27 | if (isCurrentTask) { 28 | res += res ? "," : ""; 29 | res += `当前需求${operate}了该${type}`; 30 | } 31 | 32 | return res; 33 | } 34 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/detail/BackToList.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 50 | 51 | 57 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/detail/TaskObject.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 54 | 55 | 66 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/detail/TrackerTab.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 44 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/detail/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 71 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/list/RequirementGroup.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/list/SettingSprint.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 28 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/list/SettingVersion.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 79 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/list/TerminalList.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/list/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 43 | 44 | 53 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/version/Publish.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 39 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/version/VersionNumEditModal.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 56 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/version/VersionTree.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 59 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/version/graph/context-menu.ts: -------------------------------------------------------------------------------- 1 | import G6 from "@antv/g6"; 2 | import { Item } from "@antv/g6-core/lib/types"; 3 | 4 | export const getContextMenu = (eventEmitter) => { 5 | const nodeContextMenu = new G6.Menu({ 6 | getContent() { 7 | return ` 8 |
    9 |
  • 设为主分支
  • 10 |
11 | `; 12 | }, 13 | handleMenuClick: (target: HTMLElement, item: Item) => { 14 | const title = target.getAttribute("data-title"); 15 | 16 | title === "set-main-version" && 17 | eventEmitter.emit("set-main-version", item.getID()); 18 | }, 19 | shouldBegin: (e) => { 20 | const model: any = e.item?.getModel(); 21 | 22 | return model?.canSetMainVersion; 23 | }, 24 | offsetX: 8, 25 | offsetY: 8, 26 | itemTypes: ["node"], 27 | }); 28 | return [nodeContextMenu]; 29 | }; 30 | -------------------------------------------------------------------------------- /src/views/home/tracker/requirement/version/version-publish/BackToList.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 41 | 42 | 48 | -------------------------------------------------------------------------------- /src/vue-router.d.ts: -------------------------------------------------------------------------------- 1 | // typings.d.ts or router.ts 2 | import 'vue-router' 3 | 4 | declare module 'vue-router' { 5 | interface RouteMeta { 6 | type?: number // 权限控制 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/vuex.d.ts: -------------------------------------------------------------------------------- 1 | import { Store } from 'vuex' 2 | 3 | declare module '@vue/runtime-core' { 4 | // 声明自己的 store state 5 | interface State { 6 | count: number 7 | } 8 | 9 | interface ComponentCustomProperties { 10 | $store: Store 11 | defferMoule: number 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "jsx": "preserve", 6 | "strict": false, 7 | "skipLibCheck": true, 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strictNullChecks": false, 14 | "sourceMap": true, 15 | "baseUrl": ".", 16 | "types": [ 17 | "webpack-env", 18 | "jest" 19 | ], 20 | "paths": { 21 | "@/*": [ 22 | "src/*" 23 | ] 24 | }, 25 | "lib": [ 26 | "esnext", 27 | "dom", 28 | "dom.iterable", 29 | "scripthost" 30 | ] 31 | }, 32 | "include": [ 33 | "**/*.d.ts", 34 | "src/**/*.ts", 35 | "src/**/*.tsx", 36 | "src/**/*.vue", 37 | "tests/**/*.ts", 38 | "tests/**/*.tsx", 39 | "auto-imports.d.ts", 40 | "src/middler/webpackLoader/installCommon.js" 41 | ], 42 | "exclude": ["node_modules"] 43 | } 44 | --------------------------------------------------------------------------------